/* * ***************************************************************************** * * Copyright (c) 2006-2012 XMind Ltd. and others. This file is a part of XMind * 3. XMind releases 3 and above are dual-licensed under the Eclipse Public * License (EPL), which is available at * http://www.eclipse.org/legal/epl-v10.html and the GNU Lesser General Public * License (LGPL), which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. Contributors: XMind Ltd. - * initial API and implementation *******************************************************************************/ package org.xmind.ui.internal.mindmap; import java.util.ArrayList; import java.util.List; import org.eclipse.draw2d.FigureCanvas; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Layout; import org.xmind.core.IRelationship; import org.xmind.core.IRelationshipEnd; import org.xmind.core.ISheet; import org.xmind.core.ISummary; import org.xmind.core.ITopic; import org.xmind.core.ITopicComponent; import org.xmind.core.IWorkbook; import org.xmind.gef.GraphicalViewer; import org.xmind.gef.ISelectionSupport; import org.xmind.gef.command.Command; import org.xmind.gef.command.CompoundCommand; import org.xmind.gef.command.ICommandStack; import org.xmind.gef.part.IPart; import org.xmind.gef.part.IRootPart; import org.xmind.gef.service.IRevealService; import org.xmind.ui.commands.ModifyFoldedCommand; import org.xmind.ui.internal.MindMapUIPlugin; import org.xmind.ui.internal.figures.BranchFigure; import org.xmind.ui.mindmap.IBranchPart; import org.xmind.ui.mindmap.IMindMap; import org.xmind.ui.mindmap.IMindMapViewer; import org.xmind.ui.mindmap.ISheetPart; import org.xmind.ui.mindmap.ITopicPart; import org.xmind.ui.mindmap.MindMapUI; import org.xmind.ui.prefs.PrefConstants; import org.xmind.ui.util.MindMapUtils; public class MindMapViewer extends GraphicalViewer implements IMindMapViewer, IPropertyChangeListener { public static final int OVERVIEW_WIDTH = 300; public static final int OVERVIEW_HEIGHT = 180; private class OverviewLayout extends Layout { @Override protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { if (wHint < 0 || hHint < 0) { Control[] children = composite.getChildren(); int w = Math.max(0, wHint); int h = Math.max(0, hHint); for (int i = 0; i < children.length; i++) { Control child = children[i]; Point childSize = child.getSize(); w = Math.max(w, childSize.x); h = Math.max(h, childSize.y); } } return new Point(wHint, hHint); } @Override protected void layout(Composite composite, boolean flushCache) { org.eclipse.swt.graphics.Rectangle area = composite.getParent() .getClientArea(); Control[] children = composite.getChildren(); if (children.length == 2) { children[1].setBounds(area); children[0].setBounds(area.x + area.width - OVERVIEW_WIDTH, area.y + area.height - OVERVIEW_HEIGHT, OVERVIEW_WIDTH, OVERVIEW_HEIGHT); } } } protected class MindMapSelectionSupport extends GraphicalSelectionSupport { public IPart findSelectablePart(Object element) { if (element instanceof ISheet) return null; IPart p = super.findSelectablePart(element); if (p instanceof ITopicPart) { IBranchPart branch = MindMapUtils.findBranch(p); if (branch != null) { BranchFigure branchFigure = (BranchFigure) branch .getFigure(); if (branchFigure.isMinimized()) p = null; } } return p; } protected Object getModel(IPart p) { return MindMapUtils.getRealModel(p); } public ISelection getModelSelection() { ISelection selection = super.getModelSelection(); if (selection instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection) selection; if (ss.isEmpty() && getInput() != null) { selection = new StructuredSelection( MindMapUtils.toRealModel(getInput())); } } return selection; } } private boolean inputChangedOnSelectionChanged = false; private Composite overviewContainer; private IPreferenceStore ps; public MindMapViewer() { setDndSupport(MindMapUI.getMindMapDndSupport()); setPartFactory(MindMapUI.getMindMapPartFactory()); setRootPart(new MindMapRootPart()); getProperties().set(VIEWER_RENDER_TEXT_AS_PATH, false); ps = MindMapUIPlugin.getDefault().getPreferenceStore(); } public <T> T getAdapter(Class<T> adapter) { if (IMindMap.class.equals(adapter)) return adapter.cast(getMindMap()); if (ISheet.class.equals(adapter)) return adapter.cast(getSheet()); if (ITopic.class.equals(adapter)) return adapter.cast(getCentralTopic()); if (ISheetPart.class.equals(adapter)) return adapter.cast(getSheetPart()); if (IBranchPart.class.equals(adapter)) return adapter.cast(getCentralBranchPart()); if (ITopicPart.class.equals(adapter)) return adapter.cast(getCentralTopicPart()); if (IWorkbook.class.equals(adapter)) { ISheet sheet = getSheet(); return adapter .cast(sheet == null ? null : sheet.getOwnedWorkbook()); } return super.getAdapter(adapter); } protected Control internalCreateControl(Composite parent, int style) { Composite container = new Composite(parent, SWT.NONE); container.setLayout(new OverviewLayout()); container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Composite composite = createOverview(container); FigureCanvas fc = (FigureCanvas) super.internalCreateControl(container, style); fc.setScrollBarVisibility(FigureCanvas.ALWAYS); fc.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE)); final Overview overview = new Overview(composite, fc, this); composite.setVisible(ps.getBoolean(PrefConstants.SHOW_OVERVIEW)); ps.removePropertyChangeListener(this); ps.addPropertyChangeListener(this); composite.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { overview.dispose(); if (ps != null) { ps = null; } } }); return fc; } private Composite createOverview(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(1, false); layout.marginWidth = 0; layout.marginHeight = 0; layout.horizontalSpacing = 0; layout.verticalSpacing = 0; composite.setLayout(layout); overviewContainer = composite; return composite; } protected ISelectionSupport createSelectionSupport() { return new MindMapSelectionSupport(); } public Object getPreselected() { Object o = super.getPreselected(); if (o instanceof IPart) { o = ((IPart) o).getModel(); } return MindMapUtils.toRealModel(o); } public Object getFocused() { Object o = super.getFocused(); if (o instanceof IPart) { o = ((IPart) o).getModel(); } return MindMapUtils.toRealModel(o); } protected void revealParts(List<? extends IPart> parts) { super.revealParts(parts); if (getFocusedPart() != null && parts.contains(getFocusedPart())) { IRevealService revealService = (IRevealService) getService( IRevealService.class); if (revealService != null) { revealService.reveal(new StructuredSelection(getFocusedPart())); } } } /* * (non-Javadoc) * @see org.xmind.gef.AbstractViewer#fireFocusedPartChanged() */ @Override protected void fireFocusedPartChanged() { super.fireFocusedPartChanged(); if (getFocusedPart() != null) { IRevealService revealService = (IRevealService) getService( IRevealService.class); if (revealService != null) { revealService.reveal(new StructuredSelection(getFocusedPart())); } } } public void setSelection(ISelection selection) { if (selection instanceof IStructuredSelection) { for (Object element : ((IStructuredSelection) selection).toList()) { if (element instanceof ITopicComponent) { setSelectionAndUnfold(element); } else if (element instanceof IRelationship) { IRelationship r = (IRelationship) element; IRelationshipEnd e1 = r.getEnd1(); IRelationshipEnd e2 = r.getEnd2(); if (e1 instanceof ITopicComponent) { setSelectionAndUnfold(e1); } if (e2 instanceof ITopicComponent) { setSelectionAndUnfold(e2); } } } } super.setSelection(selection, true); } private void setSelectionAndUnfold(Object element) { List<Command> showElementsCommands = new ArrayList<Command>(1); ITopic parent = ((ITopicComponent) element).getParent(); while (parent != null) { if (parent.isFolded()) { showElementsCommands .add(new ModifyFoldedCommand(parent, false)); } parent = parent.getParent(); } if (!showElementsCommands.isEmpty()) { Command command = new CompoundCommand( showElementsCommands.get(0).getLabel(), showElementsCommands); saveAndRun(command); } } protected void saveAndRun(Command command) { ICommandStack cs = getEditDomain().getCommandStack(); if (cs != null) cs.execute(command); } protected void ensureVisible(Rectangle box, Rectangle clientArea, int margin) { super.ensureVisible(box, clientArea, 10); } // protected void inputChanged(Object input, Object oldInput) { // ISelection oldSelection = getSelection(); // super.inputChanged(input, oldInput); // if (!inputChangedOnSelectionChanged && getEditDomain() != null // && needSelectCentral(oldSelection)) { // getEditDomain().handleRequest(MindMapUI.REQ_SELECT_CENTRAL, this); // } // } /* * (non-Javadoc) * @see * org.xmind.gef.GraphicalViewer#setSelectionOnInputChanged(org.eclipse. * jface.viewers.ISelection) */ @Override protected void setSelectionOnInputChanged(ISelection selection) { if (inputChangedOnSelectionChanged) return; setSelection(selection, false); } // private boolean needSelectCentral(ISelection oldSelection) { // if (oldSelection.isEmpty()) // return true; // if (oldSelection instanceof IStructuredSelection) { // IStructuredSelection ss = (IStructuredSelection) oldSelection; // if (ss.size() == 1 && ss.getFirstElement() instanceof ISheet) // return true; // } // return false; // } public IMindMap getMindMap() { Object input = getInput(); return input instanceof IMindMap ? (IMindMap) input : null; } public void setMindMap(IMindMap mindMap) { setInput(mindMap); } public IBranchPart getCentralBranchPart() { ISheetPart sheetPart = getSheetPart(); return sheetPart == null ? null : sheetPart.getCentralBranch(); } public ITopic getCentralTopic() { Object input = getInput(); if (input instanceof IMindMap) { return ((IMindMap) input).getCentralTopic(); } else if (input instanceof ISheet) { return ((ISheet) input).getRootTopic(); } else if (input instanceof ITopic) { return (ITopic) input; } return null; } public ITopicPart getCentralTopicPart() { IBranchPart centralBranchPart = getCentralBranchPart(); return centralBranchPart == null ? null : centralBranchPart.getTopicPart(); } public ISheet getSheet() { Object input = getInput(); if (input instanceof IMindMap) { return ((IMindMap) input).getSheet(); } else if (input instanceof ISheet) { return (ISheet) input; } else if (input instanceof ITopic) { return ((ITopic) input).getOwnedSheet(); } return null; } public ISheetPart getSheetPart() { IRootPart rootPart = getRootPart(); if (rootPart != null) { IPart contents = rootPart.getContents(); if (contents instanceof ISheetPart) return (ISheetPart) contents; } return null; } public boolean isPrimaryCentralTopic() { ISheet sheet = getSheet(); ITopic centralTopic = getCentralTopic(); return centralTopic != null && sheet != null && centralTopic.equals(sheet.getRootTopic()); } public IPart findPart(Object element) { if (element instanceof ISummary) element = ((ISummary) element).getTopic(); return super.findPart(element); } @Override public void propertyChange(final PropertyChangeEvent event) { if (overviewContainer == null || overviewContainer.isDisposed()) return; overviewContainer.getDisplay().asyncExec(new Runnable() { @Override public void run() { setOverviewVisible(event.getProperty()); } }); } private void setOverviewVisible(String id) { if (PrefConstants.SHOW_OVERVIEW.equals(id)) { overviewContainer.setVisible(ps.getBoolean(id)); } } }