/***************************************************************************** * Copyright (c) 2009 CEA LIST & LIFL * * * 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: * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation * *****************************************************************************/ package org.eclipse.papyrus.infra.core.sasheditor.contentprovider.simple; import java.util.ArrayList; import java.util.List; import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IAbstractPanelModel; import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IContentChangedListener; import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IContentChangedProvider; import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageModel; import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ISashWindowsContentProvider; import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ITabFolderModel; import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IContentChangedListener.ContentEvent; import org.eclipse.papyrus.infra.core.sasheditor.internal.SashWindowsContainer; import org.eclipse.swt.SWT; /** * A simple implementation of providers allowing sashes and folders. * The tabs can be added and removed. * * @author dumoulin * */ public class SimpleSashWindowsContentProvider implements ISashWindowsContentProvider, IContentChangedProvider { /** The currently selected tab folder */ private TabFolderModel currentTabFolder; /** The root model. */ private RootModel rootModel; /** */ private ContentChangeListenerManager contentChangedListenerManager = new ContentChangeListenerManager(); /** * Constructor. */ public SimpleSashWindowsContentProvider() { // Create a tree with one single folder currentTabFolder = new TabFolderModel(this); rootModel = new RootModel(currentTabFolder); } /** * Add a listener listening on content changed. This listener will be * notified each time the content change. * * @param listener */ public void addListener(IContentChangedListener listener) { contentChangedListenerManager.addContentChangedListener(listener); } /** * Add a listener listening on content changed. This listener will be * notified each time the content change. * * @param listener */ public void removeListener(IContentChangedListener listener) { contentChangedListenerManager.removeContentChangedListener(listener); } /** * Add a listener listening on content changed. This listener will be * notified each time the content change. * * @param listener */ protected void firePropertyChanged(ContentEvent event) { contentChangedListenerManager.fireContentChanged(event); } /** * Add the page which should be an IPageModel instance. {@inheritDoc} */ public void addPage(Object newModel) { addPage((IPageModel)newModel); } /** * Add the page which should be an IPageModel instance. {@inheritDoc} */ public void addPage(Object newModel, int index) { addPage((IPageModel)newModel, index); } /** * * {@inheritDoc} */ public void addPage(IPageModel newModel) { currentTabFolder.doAddItem(newModel); firePropertyChanged(new ContentEvent(ContentEvent.ADDED, this, newModel)); } /** * * {@inheritDoc} */ public void addPage(int index, IPageModel newModel) { currentTabFolder.doAddItem(index, newModel); firePropertyChanged(new ContentEvent(ContentEvent.ADDED, this, newModel)); } /** * Move a Page inside the folder. {@inheritDoc} */ public void movePage(ITabFolderModel folderModel, int oldIndex, int newIndex) { org.eclipse.papyrus.infra.core.sasheditor.Activator.log.debug("movePage()"); ((TabFolderModel)folderModel).moveTab(oldIndex, newIndex); } /** * Move a tab from folder to folder. * The change event is sent only once after the complete operation is performed. {@inheritDoc} */ public void movePage(ITabFolderModel srcFolderModel, int sourceIndex, ITabFolderModel targetFolderModel, int targetIndex) { // This implementation use (TabFolderModel), so we can cast safely org.eclipse.papyrus.infra.core.sasheditor.Activator.log.debug("movePage()"); if(sourceIndex == -1) { moveAllPages(srcFolderModel, targetFolderModel); return; } IPageModel movedTab = doMoveTab((TabFolderModel)srcFolderModel, sourceIndex, (TabFolderModel)targetFolderModel, targetIndex); removeEmptyFolder((TabFolderModel)srcFolderModel); doSetCurrentFolder((TabFolderModel)targetFolderModel); contentChangedListenerManager.fireContentChanged(new ContentEvent(ContentEvent.MOVED, this, movedTab)); } /** * Move all tabs from source to target * * @param srcFolderModel * @param targetFolderModel */ public void moveAllPages(ITabFolderModel srcFolderModel, ITabFolderModel targetFolderModel) { TabFolderModel srcFolder = (TabFolderModel)srcFolderModel; TabFolderModel targetFolder = (TabFolderModel)targetFolderModel; List<IPageModel> toMove = srcFolder.doRemoveAll(); targetFolder.doAddAllTab(toMove); removeEmptyFolder((TabFolderModel)srcFolderModel); doSetCurrentFolder((TabFolderModel)targetFolderModel); contentChangedListenerManager.fireContentChanged(new ContentEvent(ContentEvent.MOVED, this, srcFolderModel)); } /** * Set the Current Folder to the newCurrentFolder. * * @param targetFolderModel */ private void doSetCurrentFolder(TabFolderModel newCurrentFolder) { currentTabFolder = (TabFolderModel)newCurrentFolder; } /** * Create a new folder and insert it at the specified side. Move the specified tab into the created Folder. * * The change event is sent only once after the complete operation is performed. {@inheritDoc} * * @param referenceFolder The folder used as reference to insert the newly created Folder. * @param side The side to which the created folder is inserted. Can be SWT.TOP, DOWN, LEFT, RIGHT. */ public void createFolder(ITabFolderModel sourceFolder, int tabIndex, ITabFolderModel referenceFolder, int side) { org.eclipse.papyrus.infra.core.sasheditor.Activator.log.debug("createFolder()"); ITabFolderModel newFolder = doCreateFolder((TabFolderModel)sourceFolder, tabIndex, (TabFolderModel)referenceFolder, side); contentChangedListenerManager.fireContentChanged(new ContentEvent(ContentEvent.CHANGED, this, sourceFolder)); // return newFolder; } /** * Create a new folder and insert it at the specified side of the reference folder. * The change event is sent only once after the complete operation is performed. * * This method is not part of the {@link SashWindowsContainer} API. It is here to help writing junit tests. * * @param referenceFolder The folder used as reference to insert the newly created Folder. * @param side The side to which the created folder is inserted. Can be SWT.TOP, DOWN, LEFT, RIGHT. */ public ITabFolderModel createFolder(ITabFolderModel referenceFolder, int side) { org.eclipse.papyrus.infra.core.sasheditor.Activator.log.debug("createFolder()"); ITabFolderModel newFolder = doCreateFolder((TabFolderModel)referenceFolder, side); contentChangedListenerManager.fireContentChanged(new ContentEvent(ContentEvent.CHANGED, this, referenceFolder)); return newFolder; } /** * Move a tab from folder to folder. * The change event is sent only once after the complete operation is performed. * * @return The moved tab. */ private IPageModel doMoveTab(TabFolderModel srcFolderModel, int sourceIndex, TabFolderModel targetFolderModel, int targetIndex) { IPageModel tab = srcFolderModel.doRemoveTab(sourceIndex); targetFolderModel.doAddItem(targetIndex, tab); return tab; } /** * Move a tab from folder to folder. * The tab is added at the end of the target folder. * The change event is sent only once after the complete operation is performed. {@inheritDoc} */ private void doMoveTab(TabFolderModel srcFolderModel, int sourceIndex, TabFolderModel targetFolderModel) { IPageModel tab = srcFolderModel.doRemoveTab(sourceIndex); targetFolderModel.doAddItem(tab); } /** * Create a new folder and insert it at the specified side. * */ private TabFolderModel doCreateFolder(TabFolderModel tabFolder, int tabIndex, TabFolderModel targetFolder, int side) { // Create new folder. Parent will be set when inserted. TabFolderModel newFolder = new TabFolderModel(this); // Inset folder doInsertFolder(newFolder, targetFolder, side); // Move tab from folder to folder doMoveTab(tabFolder, tabIndex, newFolder); // Remove unused folder if necessary removeEmptyFolder(tabFolder); doSetCurrentFolder(targetFolder); return newFolder; } /** * Create a new folder and insert it at the specified side. * */ private TabFolderModel doCreateFolder(TabFolderModel referenceFolder, int side) { // Create new folder. Parent will be set when inserted. TabFolderModel newFolder = new TabFolderModel(this); // Insert folder doInsertFolder(newFolder, referenceFolder, side); doSetCurrentFolder(referenceFolder); return newFolder; } /** * Remove the folder if it is empty. * * @param tabFolder */ private void removeEmptyFolder(TabFolderModel tabFolder) { // Check if empty if(tabFolder.getChildren().size() > 0) return; AbstractModel parent = tabFolder.getParent(); // Forbid removing of the last folder if(parent == rootModel) return; // Parent is a sash. Ask it to remove the child and itself ((SashPanelModel)parent).delete(tabFolder); } /** * Insert the folderToInsert on the specified side of the refFolder. Create and insert the * requested SashModel. * * @param folderToInsert * @param refFolder * @param side */ private void doInsertFolder(TabFolderModel folderToInsert, TabFolderModel refFolder, int side) { // Get the parent under which the sash will be inserted AbstractModel refParent = refFolder.getParent(); SashPanelModel newSash; int direction; // Compute sash direction if(side == SWT.LEFT || side == SWT.RIGHT) direction = SWT.HORIZONTAL; else direction = SWT.VERTICAL; // Create sash if(side == SWT.LEFT || side == SWT.UP) { newSash = new SashPanelModel(refParent, folderToInsert, refFolder, direction); } else { newSash = new SashPanelModel(refParent, refFolder, folderToInsert, direction); } // Change sash childs parent refFolder.setParent(newSash); folderToInsert.setParent(newSash); // Change sash parent refParent.replaceChild(refFolder, newSash); } /** * Get the root used as root to be shown in the editor. {@inheritDoc} */ public IAbstractPanelModel getRootModel() { return rootModel.getChild(); } /** * Create the interface used to access the rootModel {@inheritDoc} */ public IAbstractPanelModel createChildSashModel(Object root) { // The root object should be of type IAbstractPanelModel. // This is normally the object returned by getRootPanel return (IAbstractPanelModel)root; } /** * * {@inheritDoc} */ public void removePage(int index) { currentTabFolder.removeTab(index); } /** * Remove the specified page which should be an instance of IPageModel. {@inheritDoc} */ public void removePage(Object page) { removePage((IPageModel)page); } /** * Remove the specified tab from its parent. {@inheritDoc} */ public void removePage(IPageModel tabItem) { TabFolderModel folder = lookupPageFolder(tabItem); if(folder != null) folder.removeTab(tabItem); } /** * Remove the tab at the specified index. * */ public void removePage(ITabFolderModel parentFolder, int tabIndex) { TabFolderModel folder = (TabFolderModel)parentFolder; IPageModel removed = folder.doRemoveTab(tabIndex); removeEmptyFolder(folder); doSetCurrentFolder(lookupPageFolder()); contentChangedListenerManager.fireContentChanged(new ContentEvent(ContentEvent.REMOVED, this, removed)); } /** * Lookup the folder containing the specified tabItem. * * @param tabItem * Item for which a folder is looked for. If the item is null, return * the first folder encountered. * @return The folder containing the item, or the first encountered folder if item is null. */ private TabFolderModel lookupPageFolder(IPageModel tabItem) { return rootModel.lookupTabFolder(tabItem); } /** * Lookup for the first folder in the model. * * @return The first encountered folder. */ private TabFolderModel lookupPageFolder() { return rootModel.lookupTabFolder(null); } /** * Get the parent of the specified tabItem, or null * * @param tabItem * @return The parent tabFolder or null if not found. */ public ITabFolderModel getParentFolder(IPageModel tabItem) { return lookupPageFolder(tabItem); } /** * Return the currently selected TabFolder. * * @return */ public ITabFolderModel getCurrentTabFolder() { return currentTabFolder; } /** * Set the the current Folder. * * @see org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ISashWindowsContentProvider#setCurrentFolder(java.lang.Object) * * @param rawModel * Object identifying the current folder. In this implementation, the object is the FolderModel. */ public void setCurrentFolder(Object rawModel) { if(!(rawModel instanceof TabFolderModel)) { return; } doSetCurrentFolder((TabFolderModel)rawModel); } /** * A class managing a list of listeners. * * @author dumoulin */ protected class ContentChangeListenerManager { private List<IContentChangedListener> listeners; /** * Add a listener listening on content changed. This listener will be * notified each time the content change. * * @param listener */ public void addContentChangedListener(IContentChangedListener listener) { if(listeners == null) createListeners(); // Check if already exists. if(listeners.contains(listener)) return; listeners.add(listener); } /** * Add a listener listening on content changed. This listener will be * notified each time the content change. * * @param listener */ public void removeContentChangedListener(IContentChangedListener listener) { if(listeners == null) return; listeners.remove(listener); } /** * Create the list of listeners. */ private void createListeners() { if(listeners == null) listeners = new ArrayList<IContentChangedListener>(); } /** * Fire the changed event. * * @param event */ public void fireContentChanged(ContentEvent event) { if(listeners == null) return; for(IContentChangedListener listener : listeners) { listener.contentChanged(event); } } } }