com.nokia.tools.screen.ui.actions.PlayerController.java Source code

Java tutorial

Introduction

Here is the source code for com.nokia.tools.screen.ui.actions.PlayerController.java

Source

/*
* Copyright (c) 2006-2010 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.tools.screen.ui.actions;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.swing.JComponent;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPart;

import com.nokia.tools.editing.core.EditingUtil;
import com.nokia.tools.editing.core.TypedAdapter;
import com.nokia.tools.editing.jfc.JFCDiagramAdapter;
import com.nokia.tools.editing.model.EditDiagram;
import com.nokia.tools.editing.model.EditObject;
import com.nokia.tools.media.player.IErrorHandler;
import com.nokia.tools.media.player.IPlayer;
import com.nokia.tools.media.player.PlayState;
import com.nokia.tools.screen.core.IScreenElement;
import com.nokia.tools.screen.core.JEMUtil;
import com.nokia.tools.screen.ui.UiPlugin;
import com.nokia.tools.screen.ui.utils.ScreenUtil;

/**
 * This controller controls all animated elements in screen. Individual
 * animation shall be turned on/off by calling {@link IPlayer#play()} and
 * {@link IPlayer#stop(}.
 * 
 */
public class PlayerController extends TypedAdapter implements Runnable, IErrorHandler {
    private static Set<PlayerController> controllers = new HashSet<PlayerController>();

    public static final int REFRESH_INTERVAL = 50;

    private volatile Thread thread;

    private EditPartViewer viewer;

    private Set<IScreenElement> toRefresh = Collections.synchronizedSet(new HashSet<IScreenElement>());

    private Set<IPlayer> badPlayers = new HashSet<IPlayer>();

    private float speedFactor = 1.0f;

    private Set<IPlayStateListener> listeners = new HashSet<IPlayStateListener>();

    private PlayState state;

    private long duration = Long.MAX_VALUE;

    private boolean isPlayOnce;

    private Map<String, Object> attributes = new HashMap<String, Object>();

    private Shell shell;

    /**
     * Constructs a controller for the given screen.
     * 
     * @param screen the root screen element.
     */
    private PlayerController(EditPartViewer viewer) {
        this.viewer = viewer;
        IScreenElement screen = ScreenUtil.getScreen(viewer);
        screen.getWidget().eAdapters().add(this);
    }

    /**
     * Creates a new instance of the controller if it's not available, otherwise
     * returns the existing one.
     * 
     * @param viewer the viewer where the animation will be run.
     * @return the controller instance.
     */
    public synchronized static PlayerController getInstance(EditPartViewer viewer) {
        if (viewer == null) {
            return null;
        }

        IWorkbenchPart part = ((DefaultEditDomain) viewer.getEditDomain()).getEditorPart();
        if (part == null) {
            throw new IllegalArgumentException("No editor part found.");
        }
        IScreenElement screen = ScreenUtil.getScreen(viewer);
        if (screen == null || screen.getWidget() == null) {
            return null;
        }
        PlayerController controller = null;
        for (PlayerController c : controllers) {
            if (c.viewer == viewer) {
                controller = c;
                break;
            }
        }
        if (controller == null) {
            controller = new PlayerController(viewer);
            controllers.add(controller);
        }
        return controller;
    }

    public static PlayerController findByElement(IScreenElement element) {
        IScreenElement root = element.getRoot();
        for (PlayerController controller : controllers.toArray(new PlayerController[controllers.size()])) {
            IScreenElement screen = ScreenUtil.getScreen(controller.viewer);
            if (root == screen) {
                return controller;
            }
        }
        return null;
    }

    /**
     * @return the duration
     */
    public long getDuration() {
        return duration;
    }

    /**
     * @param duration the duration to set
     */
    public void setDuration(long duration) {
        if (duration <= 0) {
            throw new IllegalArgumentException("The duration is not valid: " + duration);
        }
        this.duration = duration;
    }

    /**
     * @return the isPlayOnce
     */
    public boolean isPlayOnce() {
        return isPlayOnce;
    }

    /**
     * @param isPlayOnce the isPlayOnce to set
     */
    public void setPlayOnce(boolean isPlayOnce) {
        this.isPlayOnce = isPlayOnce;
    }

    private void playElement(IScreenElement element) {
        IPlayer player = (IPlayer) element.getAdapter(IPlayer.class);
        if (player != null) {
            if (player.isPlayable()) {
                toRefresh.add(element);
                player.setSpeedFactor(speedFactor);
                player.setErrorHandler(this);
                player.play();
            }
        }
    }

    /**
     * Tells all animators to play.
     */
    public void play() {
        if (isPlaying()) {
            return;
        }

        badPlayers.clear();
        shell = Display.getCurrent().getActiveShell();
        IScreenElement screen = ScreenUtil.getScreen(viewer);
        for (IScreenElement element : screen.getAllChildren()) {
            playElement(element);
        }

        startPlaying();

        notifyPlayStateChanged(PlayState.PLAYING);
    }

    /**
     * Tells animators in selection to play.
     */
    public void playSelection(IStructuredSelection sel) {
        if (isPlaying()) {
            return;
        }

        for (Object element : sel.toArray()) {
            IScreenElement se = JEMUtil.getScreenElement(element);
            playElement(se);
        }

        startPlaying();
        notifyPlayStateChanged(PlayState.PLAYING);
    }

    /**
     * Tells all animators to play.
     */
    public void pause() {
        if (!isPlaying()) {
            return;
        }

        IScreenElement screen = ScreenUtil.getScreen(viewer);
        for (IScreenElement element : screen.getAllChildren()) {
            IPlayer player = (IPlayer) element.getAdapter(IPlayer.class);
            if (player != null) {
                player.pause();
            }
        }

        stopPlaying();
        notifyPlayStateChanged(PlayState.PAUSED);
    }

    /**
     * Tells all animators to play.
     */
    public void resume() {
        if (!isPaused()) {
            return;
        }

        IScreenElement screen = ScreenUtil.getScreen(viewer);
        for (IScreenElement element : screen.getAllChildren()) {
            IPlayer player = (IPlayer) element.getAdapter(IPlayer.class);
            if (player != null && PlayState.PAUSED == player.getState()) {
                player.play();
            }
        }

        startPlaying();
        notifyPlayStateChanged(PlayState.PLAYING);
    }

    private synchronized void stopPlaying() {
        if (thread == null) {
            return;
        }
        Thread tmp = thread;
        thread = null;
        tmp.interrupt();

        JFCDiagramAdapter adapter = getDiagramAdapter();
        if (adapter != null) {
            adapter.setAnimating(false);
        }
    }

    private synchronized void startPlaying() {
        if (thread != null) {
            return;
        }
        thread = new Thread(this, getClass().getSimpleName());
        thread.start();
        JFCDiagramAdapter adapter = getDiagramAdapter();
        if (adapter != null) {
            adapter.setAnimating(true);
        }
    }

    /**
     * Tells all animators to stop.
     */
    public void stop() {
        stop(true);
    }

    private void stop(boolean forceImageUpdate) {
        if (!isPlaying() && !isPaused()) {
            return;
        }

        toRefresh.clear();

        IScreenElement screen = ScreenUtil.getScreen(viewer);
        for (IScreenElement element : screen.getAllChildren()) {
            IPlayer player = (IPlayer) element.getAdapter(IPlayer.class);
            if (player != null) {
                player.stop();
            }
        }

        if (forceImageUpdate) {
            try {
                // force image update to draw the first frame
                imageUpdate();
            } catch (InterruptedException e) {
            } catch (Exception e) {
                UiPlugin.error(e);
            }
        }

        stopPlaying();
        notifyPlayStateChanged(PlayState.STOPPED);
    }

    public void dispose() {
        stop();
        IScreenElement screen = ScreenUtil.getScreen(viewer);
        for (IScreenElement element : screen.getAllChildren()) {
            IPlayer player = (IPlayer) element.getAdapter(IPlayer.class);
            if (player != null) {
                player.dispose();
            }
        }

    }

    /**
     * @return true if the controller is current playing animation, false
     *         otherwise.
     */
    public boolean isPlaying() {
        return thread != null;
    }

    /**
     * @return true if the controller is current playing animation, false
     *         otherwise.
     */
    public boolean isPaused() {
        return PlayState.PAUSED == state;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Runnable#run()
     */
    public void run() {
        long animationStart = System.currentTimeMillis();

        while (Thread.currentThread() == thread) {
            long repaintStart = System.nanoTime();

            try {
                boolean allStopped = true;
                synchronized (toRefresh) {
                    for (Iterator<IScreenElement> i = toRefresh.iterator(); i.hasNext();) {
                        IScreenElement element = i.next();
                        IPlayer player = (IPlayer) element.getAdapter(IPlayer.class);
                        if (player == null || player.isDisposed()) {
                            i.remove();
                            continue;
                        }
                        if (PlayState.PLAYING == player.getState()) {
                            allStopped = false;
                        }

                        if (!isPlayOnce && PlayState.STOPPED == player.getState() && !badPlayers.contains(player)) {
                            // let it loop when there is other things
                            // playing
                            player.play();
                            allStopped = false;
                        }
                    }
                }

                imageUpdate();

                Thread.yield();

                if (allStopped || PlayState.STOPPED == state) {
                    stop();
                    break;
                }
            } catch (InterruptedException e) {
                break;
            } catch (Exception e) {
                UiPlugin.error(e);
                stop();
            }
            if (Thread.interrupted()) {
                break;
            }
            try {
                long repaintTook = System.nanoTime() - repaintStart;
                long sleepTime = REFRESH_INTERVAL * 1000000 - repaintTook;
                if (sleepTime > 0) {
                    Thread.sleep(sleepTime / 1000000, (int) (sleepTime % 1000000));
                }
            } catch (Exception e) {
            }

            long animationTook = System.currentTimeMillis() - animationStart;
            if (animationTook > duration) {
                if (isPlayOnce) {
                    PlayerController.this.stop();
                } else {
                    PlayerController.this.pause();
                }
            }
        }
    }

    protected void imageUpdate() throws Exception {

        IScreenElement screen = ScreenUtil.getScreen(viewer);
        Object bean = screen.getBean();
        final JComponent component = (JComponent) bean;
        if (component.getSize().width <= 0 || component.getSize().height <= 0) {
            stop(false);
            return;
        }

        IEditorPart editorPart = ((DefaultEditDomain) viewer.getEditDomain()).getEditorPart();
        EditPartViewer currentViewer = (EditPartViewer) editorPart.getAdapter(EditPartViewer.class);
        if (viewer != currentViewer) {
            // screen changed
            // stop animation preview
            stop(false);
            return;
        }
        // updates the figure, the adapter may choose not to refresh the image
        // figures when in the live mode
        JFCDiagramAdapter adapter = getDiagramAdapter();
        if (adapter != null) {
            adapter.notifyChanged(null);
        }
    }

    protected JFCDiagramAdapter getDiagramAdapter() {
        IScreenElement screen = ScreenUtil.getScreen(viewer);
        EditDiagram diagram = EditingUtil.getDiagram((EditObject) screen.getWidget());
        if (diagram != null) {
            return (JFCDiagramAdapter) EcoreUtil.getExistingAdapter(diagram, JFCDiagramAdapter.class);
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.emf.common.notify.Adapter#notifyChanged(org.eclipse.emf.common.notify.Notification)
     */
    public void notifyChanged(Notification notification) {
        if (Notification.REMOVING_ADAPTER == notification.getEventType() && notification.getNewValue() == null
                && notification.getOldValue() == this) {
            for (Iterator<PlayerController> i = controllers.iterator(); i.hasNext();) {
                PlayerController controller = i.next();
                if (controller.viewer == viewer) {
                    i.remove();
                    controller.dispose();
                }
            }
        }
    }

    public float getSpeedFactor() {
        return speedFactor;
    }

    public void setSpeedFactor(float speedFactor) {
        this.speedFactor = speedFactor;
        IScreenElement screen = ScreenUtil.getScreen(viewer);
        for (IScreenElement element : screen.getAllChildren()) {
            IPlayer player = (IPlayer) element.getAdapter(IPlayer.class);
            if (player != null) {
                player.setSpeedFactor(speedFactor);
            }
        }
    }

    public void addPlayStateListener(IPlayStateListener listener) {
        synchronized (listeners) {
            if (!listeners.contains(listener)) {
                listeners.add(listener);
            }
        }
    }

    public void removePlayStateListener(IPlayStateListener listener) {
        synchronized (listeners) {
            listeners.remove(listener);
        }
    }

    public void setAttribute(String name, Object value) {
        attributes.put(name, value);
    }

    public Object getAttribute(String name) {
        return attributes.get(name);
    }

    protected void notifyPlayStateChanged(final PlayState state) {
        this.state = state;
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                synchronized (listeners) {
                    for (IPlayStateListener listener : listeners) {
                        listener.stateChanged(state);
                    }
                }
            }
        });
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.nokia.tools.media.utils.player.IErrorHandler#handleError(com.nokia.tools.media.utils.player.IPlayer,
     *      java.lang.Throwable)
     */
    public void handleError(final IPlayer player, final Throwable e) {
        if (badPlayers.contains(player)) {
            return;
        }

        badPlayers.add(player);
        player.stop();

        if (shell != null) {
            shell.getDisplay().asyncExec(new Runnable() {
                public void run() {
                    // make sure we're in the GUI mode
                    if (shell != null) {
                        MessageDialog.openError(shell, Messages.Error_Animation_Title,
                                NLS.bind(Messages.Error_Animation_Message, new Object[] { player.getUrl(),
                                        player.getClass().getSimpleName(), e.getMessage() }));
                    }
                }
            });
        }
    }

    public interface IPlayStateListener {

        void stateChanged(PlayState state);

    }
}