/* ****************************************************************************** * 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.branch; import java.util.HashSet; import java.util.Set; import org.eclipse.draw2d.PositionConstants; import org.xmind.core.Core; import org.xmind.core.ITopic; import org.xmind.gef.graphicalpolicy.AbstractGraphicalPolicy; import org.xmind.gef.graphicalpolicy.IStructure; import org.xmind.gef.graphicalpolicy.IStyleSelector; import org.xmind.gef.part.IGraphicalPart; import org.xmind.gef.part.IPart; import org.xmind.ui.branch.IBranchHook; import org.xmind.ui.branch.IBranchPolicy; import org.xmind.ui.branch.IBranchStructure; import org.xmind.ui.branch.IBranchStructureExtension; import org.xmind.ui.mindmap.IBranchPart; import org.xmind.ui.mindmap.ICacheManager; import org.xmind.ui.mindmap.ICacheValueProvider; import org.xmind.ui.mindmap.ISummaryPart; import org.xmind.ui.mindmap.MindMapUI; import org.xmind.ui.tools.IToolHelper; import org.xmind.ui.util.MindMapUtils; public abstract class AbstractBranchPolicy extends AbstractGraphicalPolicy implements IBranchPolicy, ICacheValueProvider { protected static final String RIGHT_STRUCTURE_ID = "org.xmind.ui.branchStructure.right"; //$NON-NLS-1$ protected static final String DOWN_STRUCTURE_ID = "org.xmind.ui.branchStructure.down"; //$NON-NLS-1$ protected static final String UP_STRUCTURE_ID = "org.xmind.ui.branchStructure.up"; //$NON-NLS-1$ protected static final String LEFT_STRUCTURE_ID = "org.xmind.ui.branchStructure.left"; //$NON-NLS-1$ protected static IBranchStructure left = null; protected static IBranchStructure right = null; protected static IBranchStructure up = null; protected static IBranchStructure down = null; protected BranchPolicyManager manager; private String id; private Set<IBranchPart> calculationQueue; public AbstractBranchPolicy(BranchPolicyManager manager, String id) { this.manager = manager; this.id = id; } protected String getPolicyId() { return id; } public void activate(IGraphicalPart part) { super.activate(part); if (part instanceof IBranchPart) { activateBranch((IBranchPart) part); } } public void deactivate(IGraphicalPart part) { if (part instanceof IBranchPart) { deactivateBranch((IBranchPart) part); } super.deactivate(part); } public void postDeactivate(IBranchPart branch) { } protected void activateBranch(IBranchPart branch) { ICacheManager cm = MindMapUtils.getCacheManager(branch); if (cm != null) { cm.setValueProvider(CACHE_STRUCTURE_ID, this); } addHook(branch); flushStructureCache(branch, true, true); } protected void deactivateBranch(IBranchPart branch) { flushStructureCache(branch, true, true); removeHook(branch); ICacheManager cm = MindMapUtils.getCacheManager(branch); if (cm != null) { cm.removeValueProvider(CACHE_STRUCTURE_ID); } } protected void addHook(IBranchPart branch) { IBranchHook hook = createHook(branch); if (hook != null) { hook.hook(branch); MindMapUtils.setCache(branch, IBranchHook.CACHE_BRANCH_HOOK, hook); } } protected abstract IBranchHook createHook(IBranchPart branch); protected void removeHook(IBranchPart branch) { IBranchHook hook = (IBranchHook) MindMapUtils.getCache(branch, IBranchHook.CACHE_BRANCH_HOOK); if (hook != null) { MindMapUtils.flushCache(branch, IBranchHook.CACHE_BRANCH_HOOK); hook.unhook(branch); } } public void flushStructureCache(IBranchPart branch, boolean ancestors, boolean descendants) { flushStructureCache(branch); if (ancestors) flushParentStructureCache(branch); if (descendants) flushChildrenStructureCache(branch); } protected void flushStructureCache(IBranchPart branch) { MindMapUtils.flushCache(branch, CACHE_STRUCTURE_ID); } protected void flushParentStructureCache(IBranchPart branch) { IBranchPart parent = branch.getParentBranch(); if (parent == null) return; String policyId = parent.getBranchPolicyId(); if (getPolicyId().equals(policyId)) { parent.getBranchPolicy().flushStructureCache(parent, true, false); } } protected void flushChildrenStructureCache(IBranchPart branch) { for (IBranchPart child : branch.getSubBranches()) { flushChildStructureCache(child); } for (IBranchPart child : branch.getCalloutBranches()) { flushChildStructureCache(child); } for (IBranchPart child : branch.getSummaryBranches()) { flushChildStructureCache(child); } } protected void flushChildStructureCache(IBranchPart child) { String policyId = child.getBranchPolicyId(); if (getPolicyId().equals(policyId)) { child.getBranchPolicy().flushStructureCache(child, false, true); } } public IToolHelper getToolHelper(IBranchPart parent, Class<? extends IToolHelper> type) { return null; } public boolean isPropertyModifiable(IBranchPart branch, String propertyKey) { return isPropertyModifiable(branch, propertyKey, null); } public boolean isPropertyModifiable(IBranchPart branch, String propertyKey, String secondaryKey) { boolean modifiable = internalCheckPropertyModifiability(branch, propertyKey, secondaryKey); if (modifiable) { modifiable = !isUnmodifiableProperty(branch, propertyKey, secondaryKey); } return modifiable; } protected abstract boolean isUnmodifiableProperty(IBranchPart branch, String propertyKey, String secondaryKey); protected boolean internalCheckPropertyModifiability(IBranchPart branch, String propertyKey, String secondaryKey) { if (Core.TopicFolded.equals(propertyKey)) return isBranchFoldable(branch); if (Core.TopicHyperlink.equals(propertyKey)) return isHyperlinkModifiable(branch); return true; } protected boolean isHyperlinkModifiable(IBranchPart branch) { ITopic t = branch.getTopic(); String uri = t.getHyperlink(); if (uri != null) { return MindMapUI.getProtocolManager().isHyperlinkModifiable(t, uri); } return true; } protected boolean isBranchFoldable(IBranchPart branch) { return !branch.isCentral();// && !branch.getSubBranches().isEmpty(); } protected IStyleSelector createDefaultStyleSelector() { return DefaultBranchStyleSelector.getDefault(); } public IStructure getStructure(IGraphicalPart part) { IBranchPart branch = (IBranchPart) part; String structureId = (String) MindMapUtils.getCache(branch, CACHE_STRUCTURE_ID); return getStructureAlgorithmById(part, structureId); } private IStructure getStructureAlgorithmById(IGraphicalPart part, String structureId) { if (structureId != null) { IStructureDescriptor structureDescriptor = manager .getStructureDescriptor(structureId); if (structureDescriptor != null) return structureDescriptor.getAlgorithm(); IBranchStructure sa = getPredefinedStructure(structureId); if (sa != null) return sa; } return super.getStructure(part); } public Object getValue(IPart part, String key) { IBranchPart branch = (IBranchPart) part; if (CACHE_STRUCTURE_ID.equals(key)) { return calculateStructureAlgorithmId(branch); } return null; } protected String calculateStructureAlgorithmId(IBranchPart branch) { IBranchPart parent = branch.getParentBranch(); if (parent != null && parent.getSummaryBranches().contains(branch)) { return calcSummaryBranchStructureType(branch, parent); } if (isCalculatingOn(branch)) return getDefaultStructureId(); startCalculationOn(branch); String structureId = calcAdditionalStructureId(branch, parent); endCalculationOn(branch); if (structureId != null) return structureId; return getDefaultStructureId(); } protected abstract String calcAdditionalStructureId(IBranchPart branch, IBranchPart parent); private String calcSummaryBranchStructureType(IBranchPart branch, IBranchPart parent) { ISummaryPart summary = MindMapUtils.findAttachedSummary(parent, branch); if (summary != null) { IStructure sa = parent.getBranchPolicy().getStructure(parent); if (sa instanceof IBranchStructureExtension) { int direction = ((IBranchStructureExtension) sa) .getSummaryDirection(parent, summary); if (direction == PositionConstants.WEST) return LEFT_STRUCTURE_ID; if (direction == PositionConstants.NORTH) return UP_STRUCTURE_ID; if (direction == PositionConstants.SOUTH) return DOWN_STRUCTURE_ID; } } return RIGHT_STRUCTURE_ID; } protected boolean isCalculatingOn(IBranchPart branch) { return calculationQueue != null && calculationQueue.contains(branch); } protected void startCalculationOn(IBranchPart branch) { if (calculationQueue == null) calculationQueue = new HashSet<IBranchPart>(); calculationQueue.add(branch); } protected void endCalculationOn(IBranchPart branch) { if (calculationQueue == null) return; calculationQueue.remove(branch); if (calculationQueue.isEmpty()) calculationQueue = null; } protected IBranchStructure getPredefinedStructure(String structureId) { if (LEFT_STRUCTURE_ID.equals(structureId)) { if (left == null) left = new LeftStructure(); return left; } else if (RIGHT_STRUCTURE_ID.equals(structureId)) { if (right == null) right = new RightStructure(); return right; } else if (UP_STRUCTURE_ID.equals(structureId)) { if (up == null) up = new UpStructure(); return up; } else if (DOWN_STRUCTURE_ID.equals(structureId)) { if (down == null) down = new DownStructure(); return down; } return null; } protected abstract String getDefaultStructureId(); }