org.springframework.ide.eclipse.boot.dash.util.ColumnViewerAnimator.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.ide.eclipse.boot.dash.util.ColumnViewerAnimator.java

Source

/*******************************************************************************
 * Copyright (c) 2015 Pivotal, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Pivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.util;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.progress.UIJob;

/**
 * A ColumnViewerAnimator manages a Job that periodically updates labels for
 * 'animated' label icons in a ColumnViewer (actually, only TableViewer or
 * TreeViewer are supported with current implementation).
 *
 * @author Kris De Volder
 */
public class ColumnViewerAnimator {

    /**
     * Target provides abstraction needed so that we can 'setImage' easily on ViewerCell
     * from either Table or Tree viewer.
     */
    private static abstract class Target {
        private static final Target NULL_TARGET = new Target(null) {
            //Using 'widget = null' means this Target behaves like a disposed widget, which
            // means that the animator will ignore / remove it and not keep a Job spinning
            // to essentially do nothing animating it.
            void setImage(Image image) {
            }
        };
        private Widget widget;

        public Target(Widget widget) {
            this.widget = widget;
        }

        static Target from(ViewerCell cell) {
            final Widget item = cell.getItem();
            final int col = cell.getColumnIndex();
            if (item instanceof TableItem) {
                return new Target(item) {
                    void setImage(Image image) {
                        ((TableItem) item).setImage(col, image);
                    }
                };
            } else if (item instanceof TreeItem) {
                return new Target(item) {
                    void setImage(Image image) {
                        ((TreeItem) item).setImage(col, image);
                    }
                };
            } else {
                return NULL_TARGET;
            }
        }

        abstract void setImage(Image image);

        public boolean isDisposed() {
            return widget == null || widget.isDisposed();
        }
    }

    public class CellAnimation {
        public final Image[] imgs;
        public final Target item; //could be TableItem or TreeItem

        public CellAnimation(ViewerCell cell, Image[] imgs) {
            this.item = Target.from(cell);
            this.imgs = imgs;
        }

    }

    protected static final long INTERVAL = 100;

    private int animationCounter = 0;

    private ColumnViewer tv;

    public ColumnViewerAnimator(ColumnViewer tv) {
        this.tv = tv;
    }

    private Map<ViewerCell, CellAnimation> animatedElements = new HashMap<ViewerCell, CellAnimation>();

    private Job job;

    public void setAnimation(ViewerCell cell, Image[] images) {
        if (images == null || images.length == 0) {
            cell.setImage(null);
            stopAnimation(cell);
        } else if (images.length == 1) {
            stopAnimation(cell);
            cell.setImage(images[0]);
        } else {
            cell.setImage(currentImage(images));
            startAnimation(cell, images);
        }
    }

    private synchronized void stopAnimation(Object e) {
        animatedElements.remove(e);
    }

    private synchronized void startAnimation(ViewerCell cell, Image[] imgs) {
        animatedElements.put(cell, new CellAnimation(cell, imgs));
        ensureJob();
        job.schedule();
    }

    private synchronized CellAnimation[] getAnimations() {
        //Copy elements to avoid CME.
        return animatedElements.values().toArray(new CellAnimation[animatedElements.size()]);
    }

    private void ensureJob() {
        if (job == null) {
            job = new UIJob("Animate table icons") {
                @Override
                public IStatus runInUIThread(IProgressMonitor monitor) {
                    if (!tv.getControl().isDisposed()) {
                        animationCounter++;
                        for (CellAnimation a : getAnimations()) {
                            Image[] imgs = a.imgs;
                            if (a.item.isDisposed()) {
                                //See bug: https://www.pivotaltracker.com/story/show/100608788
                                stopAnimation(a);
                            } else {
                                a.item.setImage(imgs[animationCounter % imgs.length]);
                            }
                        }
                        if (job != null && animatedElements.size() > 0) {
                            job.schedule(INTERVAL);
                        }
                    }
                    return Status.OK_STATUS;
                }

            };
            job.setSystem(true);
        }
    }

    private Image currentImage(Image[] images) {
        return images[animationCounter % images.length];
    }

    public void dispose() {
        job = null;
    }

}