/******************************************************************************* * Copyright (c) 2010 Ahmed Mahran and others. * 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: * Ahmed Mahran - initial API and implementation *******************************************************************************/ package org.eclipse.nebula.effects.stw; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.TabFolder; /** * Transition manager applies the required transition on a {@link Transitionable} object. * * @author Ahmed Mahran (ahmahran@gmail.com) */ public class TransitionManager { //private int _finalFrom = -1; //private int _finalTo = -1; //private double _finalDirection = 0; //private boolean _isProcessFinalDest = false; private int _lastItem = -1; Color backgroundColor; Image backgroundImage; //AtomicBoolean isAnyTransitionInProgress = new AtomicBoolean(false); //AtomicBoolean isCurrentTransitionCanceled = new AtomicBoolean(false); private Transitionable _transitionable; private Transition _transition; private List<TransitionListener> _listeners; private int _transitionsBeingProcessed = 0; private Map<Integer, Image> _images = new HashMap<Integer, Image>(); /** * Constructs a transition manager to handle transitions on the provided * transitionable object. * @param transitionable the transitionable object to perform transitions on */ public TransitionManager(final Transitionable transitionable) { _transitionable = transitionable; _listeners = new ArrayList<TransitionListener>(); backgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); //the selected item before the one to be transitioned to _lastItem = transitionable.getSelection(); transitionable.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { try { //the item to be transitioned to int currentItem = _transitionable.getSelection(); startTransition(_lastItem, currentItem, transitionable.getDirection(currentItem, _lastItem)); //now the item transition ends on will be used //to start transition from next time _lastItem = currentItem; } catch(Exception e) { e.printStackTrace(); } }}); } /** * Constructs a transition manager to handle transitions on the provided * {@link CTabFolder} as the transitionable object. * @param tabFolder the {@link CTabFolder} as the transitionable object to perform transitions on */ public TransitionManager(final CTabFolder tabFolder) { this(new Transitionable(){ public void addSelectionListener(SelectionListener listener) { tabFolder.addSelectionListener(listener); } public Control getControl(int index) { return tabFolder.getItem(index).getControl(); } public Composite getComposite() { return tabFolder; } public int getSelection() { return tabFolder.getSelectionIndex(); } public void setSelection(int index) { tabFolder.setSelection(index); } public double getDirection(int toIndex, int fromIndex) { return toIndex > fromIndex ? Transition.DIR_RIGHT : Transition.DIR_LEFT; } }); } /** * Constructs a transition manager to handle transitions on the provided * {@link TabFolder} as the transitionable object. * @param tabFolder the {@link TabFolder} as the transitionable object to perform transitions on */ public TransitionManager(final TabFolder tabFolder) { this(new Transitionable(){ public void addSelectionListener(SelectionListener listener) { tabFolder.addSelectionListener(listener); } public Control getControl(int index) { return tabFolder.getItem(index).getControl(); } public Composite getComposite() { return tabFolder; } public int getSelection() { return tabFolder.getSelectionIndex(); } public void setSelection(int index) { tabFolder.setSelection(index); } public double getDirection(int toIndex, int fromIndex) { return toIndex > fromIndex ? Transition.DIR_RIGHT : Transition.DIR_LEFT; } }); } /** * Carries out the transition effect on the transitionable object by transitioning from * <code>fromIndex</code> to <code>toIndex</code> in the direction <code>direction</code> * @param fromIndex the index of the {@link Control} to start transition from * @param toIndex the index of the {@link Control} to make transition to * @param direction the direction of the transition */ public void startTransition(int fromIndex, int toIndex, double direction) { _transitionsBeingProcessed++; try { ////if any transition is in progress, cancel it and ////start the most recent one to catch up with the ////user's selections //if(isAnyTransitionInProgress.get()) { // isCurrentTransitionCanceled.set(true); //} // //isAnyTransitionInProgress.set(true); //when this event is fired, the current selected item //is the item to be transitioned to and the previously //selected one is the item to start the transition from //reselect the older item to start transition from _transitionable.setSelection(fromIndex); //capture an image of the "from" view Control from = _transitionable.getControl(fromIndex); Rectangle fromSize = from.getBounds(); Image imgFrom = new Image(from.getDisplay(), fromSize.width, fromSize.height); GC gcfrom = new GC(from); from.update(); gcfrom.copyArea(imgFrom, 0, 0); if (_transitionsBeingProcessed == 1) { // Updates the control's cached image only if no previous // transition processing has started. This is done to not // add images from started transitions, like an image from // a cubic rotation which is in the middle of the // animation. updateControlImage(imgFrom, fromIndex); } gcfrom.dispose(); //capture an image of the "to" view Control to = _transitionable.getControl(toIndex); Image imgTo = null; Rectangle toSize = to.getBounds(); Image ctrlImgTo = getControlImage(toIndex); if (ctrlImgTo != null && fromIndex != toIndex) { imgTo = new Image(to.getDisplay(), toSize.width, toSize.height); GC gcto = new GC(imgTo); Rectangle imgSize = ctrlImgTo.getBounds(); gcto.drawImage(ctrlImgTo, 0, 0, imgSize.width, imgSize.height, 0, 0, toSize.width, toSize.height); gcto.dispose(); } else { _transitionable.setSelection(toIndex); imgTo = ImageCapture.getImage(to, toSize.width, toSize.height, true); _transitionable.setSelection(fromIndex); } //create and show the canvas that the transition will be showed on Canvas canvas = new Canvas(_transitionable.getComposite(), SWT.DOUBLE_BUFFERED); canvas.moveAbove(null); canvas.setBounds(to.getBounds()); //make the transition _transition.start(imgFrom, imgTo, canvas, direction); _transitionable.setSelection(toIndex); //dispose the transition canvas canvas.dispose(); //dispose the image of the "to" view imgTo.dispose(); //if the current transition was canceled to process //a new recent one, show the new selection and make //a new transition to it //if(isCurrentTransitionCanceled.get()) { // // isCurrentTransitionCanceled.set(false); // isAnyTransitionInProgress.set(false); // //unlock(); // //} else { // // isAnyTransitionInProgress.set(false); for(TransitionListener tl: _listeners) tl.transitionFinished(TransitionManager.this); //} } catch(Exception e) { e.printStackTrace(); } _transitionsBeingProcessed--; // try { // // //if any transition is in progress, cancel it and // //start the most recent one to catch up with the // //user's selections // if(isAnyTransitionInProgress.get()) { // // isCurrentTransitionCanceled.set(true); // _finalTo = _transitionable.getSelection(); // _isProcessFinalDest = true; // return; // // } // // isCurrentTransitionCanceled.set(false); // // //when this event is fired, the current selected item // //is the item to be transitioned to and the previously // //selected one is the item to start the transition from // // //the item to be transitioned to // int currentItem = _transitionable.getSelection(); // // //reselect the older item to start transition from // _transitionable.setSelection(_lastItem); // // //capture an image of the "from" view // Control from = _transitionable.getControl(_lastItem); // Rectangle size = from.getBounds(); // Image imgFrom = new Image(from.getDisplay(), size.width, size.height); // GC gcfrom = new GC(from); // from.update(); // gcfrom.copyArea(imgFrom, 0, 0); // gcfrom.dispose(); // // //capture an image of the "to" view // Control to = _transitionable.getControl(currentItem); // _transitionable.setSelection(currentItem); // Image imgTo = ImageCapture.getImage(to, size.width, size.height, true); // _transitionable.setSelection(_lastItem); // // // //create and show the canvas that the transition will be showed on // Canvas canvas = new Canvas(_transitionable.getComposite(), SWT.DOUBLE_BUFFERED); // canvas.moveAbove(null); // canvas.setBounds(to.getBounds()); // // //make the transition // GC gcOn = new GC(canvas); // _transition.start(imgFrom, imgTo, gcOn, direction); // _transitionable.setSelection(currentItem); // gcOn.dispose(); // // //dispose the transition canvas // canvas.dispose(); // // //now the item transition ends on will be used // //to start transition from next time // _lastItem = currentItem; // // //if the current transition was canceled to process // //a new recent one, show the new selection and make // //a new transition to it // if(_isProcessFinalDest) { // // _isProcessFinalDest = false; // _transitionable.setSelection(_finalTo); // startTransition(direction); // // } else { // // for(TransitionListener tl: _listeners) // tl.transitionFinished(TransitionManager.this); // // } // // } catch(Exception e) { e.printStackTrace(); } } /** * Update an image related to a control. * @param img Image to update. * @param ctrlIndex Index of the control related to the image. */ private void updateControlImage(Image img, int ctrlIndex) { Image previousImg = _images.put(ctrlIndex, img); if (previousImg != null && !previousImg.equals(img)) { previousImg.dispose(); } } /** * Returns the image related to a control. * @param ctrlIndex Control's index related to the image. * @return image related to the control's index. */ private Image getControlImage(int ctrlIndex) { return _images.get(ctrlIndex); } /** * Sets the control images used in the transitions processing. * The control images are updated during the application execution. * The old images are disposed during the control images update. * This method should be invoked in the beginning of * the application execution, to set the control images at an * initial state. If this method is not invoked, the control * images will be populated internally by this transition * manager, but some flicks may be seen on the first transitions * processing. * @param images Control images used in the transitions * processing. */ public void setControlImages(Image[] images) { clearControlImages(); for (int i = 0; i < images.length; i++) { _images.put(i, new Image( images[i].getDevice(), images[i].getImageData())); } } /** * Clears the control images used in the * transitions processing. Disposes all the images as well. */ public void clearControlImages() { for (Image image : _images.values()) { image.dispose(); } _images.clear(); } @Override public void finalize() { // Clear cached control images on finalize. clearControlImages(); } /** * Sets and changes the transition effect * @param transition the transition effect to be applied on the transitionable object */ public void setTransition(Transition transition) { _transition = transition; } /** * Returns the current transition effect * @return the current transition effect */ public Transition getTransition() { return _transition; } /** * Sets the background color of the transition frame * @param color the background color of the transition frame */ public void setBackground(Color color) { backgroundColor = color; } /** * Returns the background color of the transition frame * @return the background color of the transition frame */ public Color getBackground() { return backgroundColor; } /** * Sets the background image of the transition frame * @param image the background image of the transition frame */ public void setBackgroundImage(Image image) { backgroundImage = image; } /** * Returns the background image of the transition frame * @return the background image of the transition frame */ public Image getBackgroundImage() { return backgroundImage; } /** * Returns the transitionable object * @return the transitionable object */ public Transitionable getTransitionable() { return _transitionable; } /** * Adds a new transition listener to be invoked at the end of each transition * @param transitionListener the new transition listener to be invoked at the end of each transition */ public void addTransitionListener(TransitionListener transitionListener) { _listeners.add(transitionListener); } /** * Removes a transition listener from the list of transition listeners * @param transitionListener the transition listener to be removed */ public void removeTransitionListener(TransitionListener transitionListener) { _listeners.remove(transitionListener); } }