/***************************************************************************** * Copyright (c) 2008 CEA LIST. * * * 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.internal; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.util.Geometry; import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageModel; import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ITabFolderModel; import org.eclipse.papyrus.infra.core.sasheditor.internal.eclipsecopy.AbstractTabFolderPart; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabFolderEvent; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.events.MenuDetectEvent; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseTrackListener; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.internal.DragCursors; import org.eclipse.ui.internal.dnd.DragUtil; import org.eclipse.ui.internal.dnd.IDragOverListener; import org.eclipse.ui.internal.dnd.IDropTarget; /** * Controller associated to a tabfolder. * * * * Extends MultiPageEditor to inherit methods implementations. * * @param T * Common ancestor for the model provided for the sash windows by the application. * This is the type used externally by the application. Sash implementation don't use this type, * it just carry it to ask for the appropriate wrapper. Concrete implementation can specify * a type. * * TODO : be more precise for the generic type ? * TODO : Listen to the page change event, and call setActivePage(). */ @SuppressWarnings("restriction") public class TabFolderPart extends AbstractTabFolderPart { /** Log object */ Logger log = Logger.getLogger(getClass().getName()); /** Interface to the model */ protected ITabFolderModel partModel; /** Raw model associated to this part. We store it because the PartModel do not provide it */ private Object rawModel; /** The wrapper around the CTabFolder. This represent the SWT control associated to this part. */ protected PTabFolder pTabFolder; /** Ordered set of currently shown diagrams (list of their models) TODO remove */ protected TabPartList currentTabItems = new TabPartList(); /** The drop target associated to this folderPart */ private DropTarget dropTarget; /** * Track the mouse hover and fire appropriate event. */ private MouseHoverTracker mouseHoverTracker; /** * Listener on DragOver event. */ IDragOverListener dragOverListener = new IDragOverListener() { /** * * @see org.eclipse.ui.internal.dnd.IDragOverListener#drag(org.eclipse.swt.widgets.Control, java.lang.Object, org.eclipse.swt.graphics.Point, * org.eclipse.swt.graphics.Rectangle) */ public IDropTarget drag(Control currentControl, Object draggedObject, Point position, Rectangle dragRectangle) { // System.out.println(TabFolderPart.this.getClass().getSimpleName() + ".drag()"); // System.out.println(this + ".drag()"); return null; } }; /** * Listener on CTabFolder events. */ private PTabFolder.IPTabFolderListener cTabFolderEventListener = new PTabFolder.IPTabFolderListener() { public void contextMenuDetectEvent(CTabItem tab, Event event) { // System.out.println("contextMenuDetect()"); } /** * The close cross has been pressed. Remove the corresponding tab. {@inheritDoc} */ public void itemClosedEvent(CTabFolderEvent event, int pageIndex) { // System.out.println("itemClosedEvent()"); // TODO: call appropriate method (to be determine) // model.removeTab(pageIndex); // getSashWindowContainer().getContentProvider().removeTab(model, pageIndex); getContentProvider().removePage(partModel, pageIndex); } public void menuDetectEvent(CTabItem tab, MenuDetectEvent event) { // System.out.println("menuDetectEvent()"); } /** * Listen to pageChange event, and propagate to TabFolderPart. * * @param newPageIndex */ public void pageChangeEvent(int newPageIndex) { pageChangedEvent(newPageIndex); } }; /** * Constructor. * * @param nestedPartManager * @param partModel * @param rawModel * */ public TabFolderPart(IPanelParent parent, ITabFolderModel partModel, Object rawModel) { super(parent); this.partModel = partModel; this.rawModel = rawModel; } /** * Get the associated model. */ protected ITabFolderModel getPartModel() { return partModel; } /** * Activate the part. Register as listener to required services. */ private void activate() { // Listen to page changes pTabFolder.getEventManager().addListener(cTabFolderEventListener); // Create the tracker that will show tooltips on tabs. mouseHoverTracker = new MouseHoverTracker(pTabFolder.getControl(), new ImageToolTipManager()); } /** * Deactivate this part. * Unregistered from required service. Do not dispose the part. */ private void deactivate() { // Listen to page changes pTabFolder.getEventManager().removeListener(cTabFolderEventListener); mouseHoverTracker.deactivate(); } /** * Fill the provided part map with this parts and recursively call children to fillin. * * @param partMap */ public void fillPartMap(PartLists partMap) { partMap.addPart(this); garbageState = GarbageState.UNVISITED; for(TabItemPart child : currentTabItems) { child.fillPartMap(partMap); } } /** * Creates the control tree associated to this part. * Create the control for this part, and eventually recursively call the method for the childs, if any. * */ @Override public void createPartControl(Composite parent) { createControl(parent); // createPages(); // model.addChangeListener(modelListener); // model.activate(); activate(); } /** * Add a new page at the end of pages. A new tab is created for the page, and * the page control is created. * * @param pageModel * @param index */ // private void addPage(Object pageModel) // { // int index = currentTabItems.size(); // createTabItem(pageModel, index); // } /** * Create the control for this Part. Does not create children. * This method is called by the parent after this folder is created. * */ public void createControl(Composite parent) { PTabFolder res = new PTabFolder(); pTabFolder = res; res.createPartControl(parent); initDrag(res.getControl()); // init menu initMenuManager(); } /** * Init the menuManager after the control has been created. * Get the {@link MenuManager} from the {@link SashWindowsContainer}. Set it to this folder if it * is not null. */ private void initMenuManager() { MenuManager menuManager = getSashWindowContainer().getFolderTabMenuManager(); if(menuManager != null) { setFolderTabMenuManager(menuManager); } } /** * Set a {@link MenuManager} used to manage a contextual menu that is shown on the tabs area of this folder. * @param menuManager The {@link MenuManager} used to create the menu on the tab area. */ public void setFolderTabMenuManager(MenuManager menuManager) { Composite folderControl = getControl(); Menu menu = menuManager.createContextMenu(folderControl); folderControl.setMenu(menu); } /** * The page has change. Propagate the event to the container. * * @param newPageIndex */ @Override protected void pageChange(int newPageIndex) { // System.out.println(this.getClass().getSimpleName() + ".pageChange("+ newPageIndex +")"); // Do nothing if out of range. if(newPageIndex < 0 || newPageIndex > currentTabItems.size() - 1) return; getSashWindowContainer().pageChanged(currentTabItems.get(newPageIndex).childPart); } /** * An event signaling that the selected page is changed has been caught. Propagate the event to * the container. * * @param newPageIndex */ protected void pageChangedEvent(int newPageIndex) { // System.out.println(this.getClass().getSimpleName() + ".pageChange("+ newPageIndex +")"); // Do nothing if out of range. if(newPageIndex < 0 || newPageIndex > currentTabItems.size() - 1) return; getSashWindowContainer().pageChangedEvent(currentTabItems.get(newPageIndex).childPart); } /** * Dispose the TilePart and its controls. * * @see org.eclipse.papyrus.infra.core.sasheditor.eclipsecopy.MultiPageEditorTile#dispose() */ public void dispose() { // detach menu as it is shared between folders. getControl().setMenu(null); deactivate(); // getControl().dispose(); pTabFolder.dispose(); } /** * Dispose this part and all its children. * The method is called recursively on children of the part. * Associated items are disposed. */ @Override public void disposeThisAndChildren() { // Dispose children if any for( TabItemPart child : currentTabItems) { if(child != null) { child.disposeThisAndChildren(); } } // clean up properties to help GC partModel = null; rawModel = null; currentTabItems.clear(); currentTabItems = null; dragOverListener = null; } /** * */ private void initDrag(Composite container) { DragUtil.addDragTarget(container, dragOverListener); } /** * Get the associated CTabFolder */ @Override protected CTabFolder getTabFolder() { return pTabFolder.getTabFolder(); } /** * Return the swt Control associated to this part. */ public Composite getControl() { return getTabFolder(); } /** * The <code>MultiPageEditor</code> implementation of this <code>IWorkbenchPart</code> method sets focus on * the active nested editor, if there is one. * <p> * Subclasses may extend or reimplement. * </p> */ public void setFocus() { setFocus(getActivePage()); } /** * Sets focus to the control for the given page. If the page has an editor, this calls its <code>setFocus()</code> method. Otherwise, this calls * <code>setFocus</code> on the control for the page. * * @param pageIndex * the index of the page */ private void setFocus(int pageIndex) { if(pageIndex < 0 || pageIndex >= getPageCount()) { // page index out of bounds, don't set focus. return; } getPagePart(pageIndex).setFocus(); } /** * Set the active page of this multi-page editor to the page that contains the given editor part. This method has no effect of the given editor * part is not contained in this multi-page editor. * * @param editorPart * the editor part * @since 3.3 */ public final void setActiveEditor(PagePart editorPart) { int count = getPageCount(); for(int i = 0; i < count; i++) { PagePart editor = getPagePart(i); if(editor == editorPart) { setActivePage(i); break; } } } /** * Return the part containing specified point. Normally return this part, because the caller has * already determine that this contain the part. * */ @Override public AbstractPart findPart(Point toFind) { return this; } /** * Locates the part that intersects the given point and that have the expected type * * @param toFind * @return */ @Override public AbstractPart findPartAt(Point toFind, Class<?> expectedTileType) { if(expectedTileType == this.getClass()) return this; // ask current active tab TabItemPart activeTabPart = getActiveTab(); if(activeTabPart == null) return null; return getActiveTab().findPartAt(toFind, expectedTileType); } /** * Get the currently active tab. * * @return */ private TabItemPart getActiveTab() { int index = getActivePage(); if(index != -1) { return currentTabItems.get(index); } return null; } /** * Get the specified childPart * * @param index * Index of the requested childPart. * @return */ protected PagePart getPagePart(int index) { return currentTabItems.get(index).getChildPart(); } /** * Get the visible PagePart (from the Container point of view). * The visible PagePart is the one that has its diagram area visible. * From the folder point of view, this is the active PagePart. * @return */ public PagePart getVisiblePagePart() { int index = getActivePage(); if(index != -1) { return getPagePart(index); } return null; } /** * Find the part associated to the provided control. * */ @Override public AbstractPanelPart findPart(Object control) { if(getControl() == control) return this; // Check if it is one of the Item if(control instanceof CTabItem && ((CTabItem)control).getParent() == getControl()) return this; // Ask childs TODO return null; } /** * * @see org.eclipse.papyrus.infra.core.sasheditor.internal.AbstractPanelPart#getDropTarget(java.lang.Object, * org.eclipse.papyrus.infra.core.sasheditor.internal.TabFolderPart, org.eclipse.swt.graphics.Point) * * @param draggedObject * @param sourcePart * @param position * @return */ public IDropTarget getDropTarget(Object draggedObject, TabFolderPart sourcePart, Point position) { // see org.eclipse.ui.internal.presentations.util.ReplaceDragHandler // Determine which tab we're currently dragging over CTabItem tabUnderPointer = pTabFolder.getItem(position); // Compute source tab index. If folder, index==-1 int sourceIndex = PTabFolder.getDraggedObjectTabIndex(draggedObject); // This drop target only deals with tabs... if we're not dragging over // a tab, exit. if(tabUnderPointer == null) { Rectangle titleArea = pTabFolder.getTabArea(); // If we're dragging over the title area, treat this as a drop in the last // tab position. if(titleArea.contains(position) && pTabFolder.getTabFolder().getItemCount() > 0) { int dragOverIndex = pTabFolder.getTabFolder().getItemCount(); CTabItem lastTab = pTabFolder.getTabFolder().getItem(dragOverIndex - 1); // Can't drag to end unless you can see the end if(!lastTab.isShowing()) { return null; } // If we are unable to compute the bounds for this tab, then ignore the drop Rectangle lastTabBounds = lastTab.getBounds(); if(lastTabBounds.isEmpty()) { return null; } // if (dragStart >= 0) { // dragOverIndex--; // // return createDropTarget( sourcePart, lastTabBounds, dragOverIndex); // // return new StackDropResult(lastTabBounds, new Integer(dragOverIndex)); // } // Make the drag-over rectangle look like a tab at the end of the tab region. // We don't actually know how wide the tab will be when it's dropped, so just // make it 3 times wider than it is tall. // titleArea is in Display coordinate, lastTabBounds in parent coordinate Rectangle dropRectangle = titleArea; dropRectangle.x = dropRectangle.x + lastTabBounds.x + lastTabBounds.width; dropRectangle.width = 3 * dropRectangle.height; return createDropTarget(sourcePart, sourceIndex, dropRectangle, dragOverIndex); // return new StackDropResult(dropRectangle, new Integer(dragOverIndex)); } else { // If the closest side is the side with the tabs, consider this a stack operation. // Otherwise, let the drop fall through to whatever the default behavior is Rectangle displayBounds = DragUtil.getDisplayBounds(pTabFolder.getControl()); int closestSide = Geometry.getClosestSide(displayBounds, position); if(closestSide == pTabFolder.getTabFolder().getTabPosition()) { return createDropTarget(sourcePart, sourceIndex, displayBounds, -1); } return null; } } if(!tabUnderPointer.isShowing()) { return null; } // Get thumbnail bounds in display coordinates Rectangle tabBounds = pTabFolder.getItemBounds(tabUnderPointer); if(tabBounds.isEmpty()) { return null; } return createDropTarget(sourcePart, sourceIndex, tabBounds, pTabFolder.getTabFolder().indexOf(tabUnderPointer)); } /** * Copied from org.eclipse.ui.internal.PartStack */ public IDropTarget createDropTarget(TabFolderPart sourcePart, int sourceIndex, Rectangle snapRectangle, int tabIndex) { if(dropTarget == null) { dropTarget = new DropTarget(sourcePart, sourceIndex, snapRectangle, tabIndex); return dropTarget; } dropTarget.setTarget(sourcePart, sourceIndex, snapRectangle, tabIndex); return dropTarget; } /** * Class implementing methods required by drop targets. Drop target use when the drop occur on one of the thumbnail of the folder. */ protected class DropTarget implements IDropTarget { int cursor = SWT.CENTER; private TabFolderPart sourcePart; private Rectangle snapRectangle; private int targetIndex; private int sourceIndex; /** * Constructor. targetPart is the current folder. * * @param sourcePart * The sourcePart of the drag * @param sourceIndex * Index of the tab from where the drop occur * @param snapRectangle * the drop area. * @param targetIndex * Index of the tab where the drop occur */ public DropTarget(TabFolderPart sourcePart, int sourceIndex, Rectangle snapRectangle, int targetIndex) { this.sourceIndex = sourceIndex; this.targetIndex = targetIndex; this.sourcePart = sourcePart; this.snapRectangle = snapRectangle; } public void setTarget(TabFolderPart sourcePart, int sourceIndex, Rectangle snapRectangle, int targetIndex) { this.sourceIndex = sourceIndex; this.targetIndex = targetIndex; this.sourcePart = sourcePart; this.snapRectangle = snapRectangle; } /** * * @see org.eclipse.ui.internal.dnd.IDropTarget#drop() */ public void drop() { // move from a folder to another if(sourcePart == TabFolderPart.this) { // move inside the same folder getContentProvider().movePage(sourcePart.getPartModel(), sourceIndex, targetIndex); } else { // move between folder getContentProvider().movePage(sourcePart.getPartModel(), sourceIndex, TabFolderPart.this.getPartModel(), targetIndex); } } /** * Return the cursor used during drag. * * @see org.eclipse.ui.internal.dnd.IDropTarget#getCursor() */ public Cursor getCursor() { // System.out.println(TabFolderPart.this.getClass().getSimpleName() + ".getCursor()-" + count++); return DragCursors.getCursor(DragCursors.positionToDragCursor(cursor)); } public Rectangle getSnapRectangle() { // System.out.println(TabFolderPart.this.getClass().getSimpleName() + ".getSnapRectangle()-" + count); return snapRectangle; } } /** * Orphan this node. The parent is set to null, but control is left unchanged. The node can be reattached with reparent(). * * @see * @return the parent */ public void orphan() { // orphan only if we are in UNCHANGED state if(garbageState == GarbageState.UNVISITED) { garbageState = GarbageState.ORPHANED; parent = null; } } /** * * @see org.eclipse.papyrus.infra.core.sasheditor.internal.AbstractPart#getGarbageState() * * @return */ public GarbageState getGarbageState() { return garbageState; } /** * Change the parent of this method. * * @see org.eclipse.papyrus.infra.core.sasheditor.sash.ITilePart#reparent(org.eclipse.papyrus.infra.core.sasheditor.sash.ITilePart) */ public void reparent(IPanelParent newParent, Composite swtParent) { parent = newParent; // Create control if needed // This can happen if the TilePart is just created after a refresh // if(getControl() == null) // { // return; // // createContainer(parent.getControl()); // } // Reparent the control assert (getControl() != null); // getControl().setParent(newParent.getControl()) ; getControl().setParent(swtParent); garbageState = GarbageState.REPARENTED; } /** * Return true if the Part is for the specified real model. Return false otherwise. * * @param realModel * The raw model to check * @return */ public boolean isPartFor(Object realModel) { return getRawModel() == realModel; } /** * Get the raw model associated to this part. * * @return */ protected Object getRawModel() { return rawModel; } /** * Refresh the tab of this page (I.e the name and icon in the tab). * * @param page The page to be refreshed */ public void refreshPageTab( PagePart page) { TabItemPart itemPart = currentTabItems.getByPagePart(page); if(itemPart==null) return; itemPart.refreshTabDecorations(); } /** * Synchronize the TabFolder with the models. * The Tabs order is fixed and can't be moved. So, we move the associated ITilepart if needed. * For each existing Tab, compare its model and the requested model. Synchronize if necessary. * If there is more new model, add new Tab * If there is less newModel, remove unused Tabs. * * @param partLists */ public void synchronize2(PartLists partLists) { // get list of model to be displayed. This is a list of Object. List<Object> newModels = (List<Object>)partModel.getChildren(); // Disable redraw CTabFolder folder = getTabFolder(); folder.setRedraw(false); // Remember active page int activePageIndex = getActivePage(); // Iterate over the minimum common size // Synchronize each tab with the requested model int minSize = Math.min(newModels.size(), currentTabItems.size()); int index; for(index = 0; index < minSize; index++) { Object curModel = newModels.get(index); TabItemPart curTab = currentTabItems.get(index); if(!curTab.isTabItemFor(curModel)) { resetTabItem(curTab, partLists, curModel); // end activePageIndex = index; } else { // Change curTab state curTab.getChildPart().unchanged(); } } // Check for extra tabs or extra models if(index < newModels.size()) { // There is extra models, add new tabs for(int i = index; i < newModels.size(); i++) { Object curModel = newModels.get(i); // Create a new TabItem associated to the curModel. createTabItem(partLists, curModel, i); // end } // Set the last as active activePageIndex = newModels.size() - 1; } else if(index < currentTabItems.size()) { // There is too much tabs, remove them List<TabItemPart> toRemove = new ArrayList<TabItemPart>(); // Collect tab to be removed for(int i = index; i < currentTabItems.size(); i++) { TabItemPart curTab = currentTabItems.get(i); toRemove.add(curTab); } // do remove for(TabItemPart curTab : toRemove) { // removeTab(curTab) removeTabItem(curTab); // end } // Set the active page as the last part if needed if(activePageIndex >= currentTabItems.size()) activePageIndex = currentTabItems.size() - 1; } folder.setRedraw(true); // folder.setSelection(activePageIndex); folder.redraw(); if(activePageIndex >= 0) { // System.err.println("setActivePage(" + activePageIndex + ") : " + this); // Set the activeTab has visible. // Do it here because otherwise the active tab could be not visible. // This come from an undefined bug setting the tab.isVisible(false) in some case. folder.getItem(activePageIndex).getControl().setVisible(true); setActivePage(activePageIndex); } else { // Check if there is item in the CTabFolder. // If true, we have a trouble if(getTabFolder().getItemCount() > 0) { // System.err.println("Active page not set while synchronizing !"); // We have items, but none is selected. // Select the first one. if(getTabFolder().getSelectionIndex() < 0) { setActivePage(0); } } } // folder.update(); // folder.showSelection(); } /** * Remove the specified tabItem. * Also call appropriate remove() method on the tabItem. * * @param curTab */ private void removeTabItem(TabItemPart tabItem) { currentTabItems.remove(tabItem); tabItem.remove(); } /** * Create a new TabItem associated to the part corresponding to the specified newModel. * The TabItem is created at the specified index. * The associated parts is searched in the existingParts or created if none is found. * * @param existingParts * List of existing parts. * @param newModel * @param index * @param i */ private void createTabItem(PartLists existingParts, Object newModel, int index) { TabItemPart newTab = null; PagePart modelPart = existingParts.findPagePartFor(newModel); if(modelPart != null) { // A part already exist for the model. Use it. modelPart.reparent(this); newTab = new TabItemPart(this, modelPart, index); } else { // No part found, create one modelPart = createChildPart(newModel); if(modelPart != null) { existingParts.addCreatedPage(modelPart); // Attach it to the tabItem newTab = new TabItemPart(this, modelPart, index); } } // Add to the list of items. if(newTab != null) { currentTabItems.add(index, newTab); } } // /** // * Create a new TabItem and associated part corresponding to the specified newModel. // * The TabItem is created at the specified index. // * The associated parts is created. // * // * @param existingParts List of existing parts. // * @param newModel // * @param index // * @param i // */ // private void createTabItem(Object newModel, int index) { // TabItemPart newTab; // // PagePart modelPart = createChildPart( newModel ); // // Attach it to the tabItem // newTab = new TabItemPart(this, modelPart, index); // // // Add to the list of items. // currentTabItems.add(index, newTab); // } /** * Instruct the specified tabItem to use the new model. Check if a part already exist for the model * and use it if any. Otherwise create a new Part. * * @param curTab * @param existingParts * @param newModel */ private void resetTabItem(TabItemPart tabItem, PartLists existingParts, Object newModel) { PagePart modelPart = existingParts.findPagePartFor(newModel); if(modelPart != null) { // A part already exist for the model. Use it. tabItem.resetChild(modelPart); } else { // No part found, create one modelPart = createChildPart(newModel); existingParts.addCreatedPage(modelPart); // Attach it to the tabItem tabItem.resetChild(modelPart); } } /** * Factory method to create a new Part for the specified newModel. * The method should always return a valid Part. In case of error while creating the requested part, * the method should return a default part, maybe showing the encountered error. * The control for the child is created. * * @param newModel * @return The new part */ private PagePart createChildPart(Object newModel) { // Create the child PartModel. Delegate creation to this part PartModel. IPageModel partModel = getPartModel().createChildSashModel(newModel); if(partModel != null) { // Delegate part creation to the container. This allow the container to provide appropriate // objects not available from the part. PagePart newPart = getSashWindowContainer().createPagePart(this, partModel, newModel); // Create control. // Fire events before and after getSashWindowContainer().getLifeCycleEventProvider().firePageAboutToBeOpenedEvent(newPart); newPart.createPartControl(getControl()); getSashWindowContainer().getLifeCycleEventProvider().firePageOpenedEvent(newPart); return newPart; } // Use exception ? return null; } /** * Show tab status * * @debug This is fo debug purpose. * @param msg */ private void showTabs(String msg) { System.out.println("------- " + msg); // Show items CTabFolder folder = getTabFolder(); CTabItem items[] = folder.getItems(); System.out.printf("sel.index %2d :\n", folder.getSelectionIndex()); System.out.printf("items %2d :", folder.getItemCount()); for(CTabItem item : items) { System.out.printf("%10s |", item.getControl()); } System.out.println(); System.out.printf("it.dispose:"); for(CTabItem item : items) { System.out.printf("%10b |", item.getControl().isDisposed()); } System.out.println(); System.out.printf("it.ctrl.vis:"); for(CTabItem item : items) { System.out.printf("%10s |", item.getControl().isVisible()); } System.out.println(); // System.out.printf("it.ctrl :"); for(CTabItem item : items) { System.out.printf("%10s |", item.getControl()); } System.out.println(); // // System.out.printf("tabs.ctrl :" ); // for( TabItemPart tab : currentModels) // { // System.out.printf( "%10s |", tab.childPart.getControl()); // } // System.out.println(); // // System.out.printf("tab.editor:" ); // for( TabItemPart tab : currentModels) // { // System.out.printf( "%10s |", tab.childPart.getIEditorPart()); // } // System.out.println(); // System.out.printf("tabs %2d :", currentTabItems.size()); for(TabItemPart tab : currentTabItems) { System.out.printf("%10s |", tab); } System.out.println(); } /** * Show tile status. */ protected void showStatus() { // System.out.println( "tabfolder[" + currentModels.size() + "]:" // + ", disposed=" + getCTabFolder().isDisposed() // + ", visible=" + getCTabFolder().isVisible() // + ", garbState=" + garbageState // + ", " + this); CTabFolder ctrl = getTabFolder(); System.out.printf("tabfolder[%2d]: disposed=%-5b, visible=%-5b, garbState=%-10s, %s\n" , currentTabItems.size(), ctrl.isDisposed(), (ctrl.isDisposed() ? false : getTabFolder().isVisible()), garbageState, this); } /** * Accept the provided visitor. * Call the corresponding accept method in the visitor. * * @param visitor * @return */ public boolean visit(IPartVisitor visitor) { return visitor.accept(this); } /** * Visit the children of this Tile. * * @param visitor */ public boolean visitChildren(IPartVisitor visitor) { for(TabItemPart child : currentTabItems) { if(!child.visit(visitor)) return false; } // All children have accepter the visit, continue visiting. return true; } /** * Collection of tabpart. * Add miscelenaous methods. * * @author dumoulin * */ @SuppressWarnings("serial") public class TabPartList extends ArrayList<TabItemPart> { /** * Does the list contains a part with the specified model. * * @param model * @return */ public boolean containsModel(Object model) { return indexOfModel(model) >= 0; } /** * Returns the index of the first occurrence of the specified element * in this list, or -1 if this list does not contain the element. * More formally, returns the lowest index <tt>i</tt> such that <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>, * or -1 if there is no such index. */ public int indexOfModel(Object o) { if(o == null) { for(int i = 0; i < size(); i++) if(get(i) == null) return i; } else { for(int i = 0; i < size(); i++) if(o.equals(get(i).getChildPart().getRawModel())) return i; } return -1; } /** * Get the TabPart by its model. * * @param model * @return */ public TabItemPart getByModel(Object model) { return get(indexOfModel(model)); } /** * Get the TabPart by its PagePart. * * @param page * @return */ public TabItemPart getByPagePart(PagePart page) { return get(indexOfModel(page.getRawModel())); } } /** * Track the mouse for flying over the tabs and show tooltip. * Show the tooltip when the mouse stop over a tab. * Disable the tooltip if mouse is clicked. * Reenable the tooltip when exiting the tab. * * @author dumoulin * */ public class MouseHoverTracker { /** * Control for which a mouse tracker is requested. */ private Control control; /** Tooltip manager showing tooltip */ private ImageToolTipManager toolTipManager; /** Anonymous mouse tracker */ MouseTrackListener mouseTrackListener = new MouseTrackListener() { private int count = 0; public void mouseEnter(MouseEvent e) { // System.out.println("MouseEnter()" + count++); } public void mouseExit(MouseEvent e) { // System.out.println("MouseExit()" + count++); toolTipManager.closeToolTip(); } public void mouseHover(MouseEvent e) { CTabFolder folder = getTabFolder(); // Point pt = folder.toDisplay(e.x, e.y); Point pt = new Point(e.x, e.y); CTabItem item = folder.getItem(pt); int index = pTabFolder.getItemIndex(pt); if(index == -1) { toolTipManager.closeToolTip(); return; } PagePart part = currentTabItems.get(index).getChildPart(); // System.out.println("MouseHover(" + e.widget // + ", part=" + part.getPageTitle() // + ", item=" + item // + ") - " + count++); // TODO move it away //toolTipManager.showToolTip(item.getBounds(), part.getControl(), pt); toolTipManager.showToolTip(part, item.getBounds(), pt); } }; /** * Listener on mouse clicked. * Used to disable the current tooltip. */ private Listener mouseClickedListener = new Listener() { private int count = 0; public void handleEvent(Event event) { switch(event.type) { case SWT.MouseUp: // System.out.println("MouseUp()" + count++); toolTipManager.disableToolTip(); break; } } }; /** * Build a tracker for the specified control. * Constructor. * * @param control */ public MouseHoverTracker(Control control, ImageToolTipManager toolTipManager) { this.control = control; this.toolTipManager = toolTipManager; activate(); } public void activate() { control.addMouseTrackListener(mouseTrackListener); control.addListener(SWT.MouseUp, mouseClickedListener); } public void deactivate() { control.removeMouseTrackListener(mouseTrackListener); control.removeListener(SWT.MouseUp, mouseClickedListener); toolTipManager.dispose(); } } }