/* ****************************************************************************** * 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.editpolicies; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.dnd.TransferData; import org.eclipse.swt.widgets.Display; import org.xmind.core.IBoundary; import org.xmind.core.ICloneData; import org.xmind.core.IImage; import org.xmind.core.IRelationship; import org.xmind.core.ISheet; import org.xmind.core.ITitled; import org.xmind.core.ITopic; import org.xmind.core.ITopicComponent; import org.xmind.core.IWorkbook; import org.xmind.core.marker.IMarker; import org.xmind.core.marker.IMarkerGroup; import org.xmind.core.marker.IMarkerRef; import org.xmind.gef.GEF; import org.xmind.gef.IViewer; import org.xmind.gef.Request; import org.xmind.gef.command.Command; import org.xmind.gef.command.CompoundCommand; import org.xmind.gef.dnd.IDndClient; import org.xmind.gef.dnd.IDndSupport; import org.xmind.gef.part.IPart; import org.xmind.ui.commands.AddBoundaryCommand; import org.xmind.ui.commands.AddMarkerCommand; import org.xmind.ui.commands.AddRelationshipCommand; import org.xmind.ui.commands.AddTopicCommand; import org.xmind.ui.commands.CommandMessages; import org.xmind.ui.commands.DeleteMarkerCommand; import org.xmind.ui.commands.ModifyBoundaryMasterCommand; import org.xmind.ui.commands.ModifyImageAlignmentCommand; import org.xmind.ui.commands.ModifyImageSizeCommand; import org.xmind.ui.commands.ModifyImageSourceCommand; import org.xmind.ui.commands.ModifyPositionCommand; import org.xmind.ui.commands.ModifyTitleTextCommand; import org.xmind.ui.commands.ModifyTopicRangeCommand; import org.xmind.ui.mindmap.MindMapUI; import org.xmind.ui.util.MindMapUtils; public class EditablePolicy extends DeletablePolicy { private static final String DEFAULT_DND_CLIENT_ID = MindMapUI.DND_MINDMAP_ELEMENT; // private boolean isCutPrev = false; // private Map<Object, Object> transferMap = null; public boolean understands(String requestType) { return super.understands(requestType) || GEF.REQ_COPY.equals(requestType) || GEF.REQ_CUT.equals(requestType) || GEF.REQ_PASTE.equals(requestType) || MindMapUI.REQ_REPLACE_ALL.equals(requestType); } public void handle(Request request) { String reqType = request.getType(); if (GEF.REQ_COPY.equals(reqType)) { copy(request); } else if (GEF.REQ_CUT.equals(reqType)) { cut(request); } else if (GEF.REQ_PASTE.equals(reqType)) { paste(request); } else if (MindMapUI.REQ_REPLACE_ALL.equals(reqType)) { replaceAll(request); } else if (!GEF.REQ_DELETE.equals(reqType)) { super.handle(request); } } protected void copy(Request request) { IViewer viewer = request.getTargetViewer(); if (viewer == null) return; List<IPart> sources = request.getTargets(); List<Object> models = filter(MindMapUtils.getRealModels(sources), viewer); if (models.isEmpty()) return; performCopy(models.toArray(), request.getTargetViewer()); } protected List<Object> filter(List<Object> elements, IViewer viewer) { ISheet sheet = (ISheet) viewer.getAdapter(ISheet.class); ITopic root = (ITopic) viewer.getAdapter(ITopic.class); ArrayList<Object> list = new ArrayList<Object>(elements.size()); for (Object o : elements) { if (!shouldRemove(o, elements, sheet, root, viewer)) list.add(o); } return list; } private boolean shouldRemove(Object element, List<Object> elements, ISheet sheet, ITopic root, IViewer viewer) { if (element instanceof IRelationship) { IRelationship r = (IRelationship) element; for (Object o : elements) { if (o instanceof ISheet && o.equals(r.getParent())) return true; } } else if (element instanceof ITopicComponent) { ITopicComponent c = (ITopicComponent) element; ISheet s = null; ITopic p = null; for (Object o : elements) { if (o instanceof ISheet) { if (s == null) s = c.getOwnedSheet(); if (o.equals(s)) return true; } if (p == null) p = c.getParent(); if (o instanceof ITopic && p != null && MindMapUtils.isDescendentOf(p, (ITopic) o)) return true; } } return false; } protected void performCopy(Object[] elements, IViewer viewer) { IDndSupport dndSupport = viewer.getDndSupport(); if (dndSupport != null) { String[] ids = dndSupport.getDndClientIds(); if (ids.length > 0) { List<Object> data = new ArrayList<Object>(ids.length); List<Transfer> transfers = new ArrayList<Transfer>(ids.length); for (String id : ids) { IDndClient client = dndSupport.getDndClient(id); if (client != null) { Object object = client.toTransferData(elements, viewer); if (object != null) { data.add(object); Transfer transfer = client.getTransfer(); transfers.add(transfer); } } } if (!data.isEmpty()) { Clipboard clipboard = new Clipboard(Display.getCurrent()); clipboard.setContents(data.toArray(), transfers .toArray(new Transfer[0])); clipboard.dispose(); } } } } protected void cut(Request request) { List<IPart> sources = request.getTargets(); List<Object> models = MindMapUtils.getRealModels(sources); if (models.isEmpty()) return; // isCutPrev = true; performCopy(models.toArray(), request.getTargetViewer()); delete(request); } protected String getDeleteLabel(String type) { if (MindMapUI.CATEGORY_TOPIC.equals(type)) return CommandMessages.Command_CutTopic; if (MindMapUI.CATEGORY_RELATIONSHIP.equals(type)) return CommandMessages.Command_CutRelationship; if (MindMapUI.CATEGORY_MARKER.equals(type)) return CommandMessages.Command_DeleteMarker; if (MindMapUI.CATEGORY_SHEET.equals(type)) return CommandMessages.Command_CutSheet; if (MindMapUI.CATEGORY_BOUNDARY.equals(type)) return CommandMessages.Command_DeleteBoundary; return CommandMessages.Command_Cut; } protected void paste(Request request) { IViewer viewer = request.getTargetViewer(); if (viewer == null) return; ISheet targetSheet = (ISheet) viewer.getAdapter(ISheet.class); if (targetSheet == null) return; IWorkbook targetWorkbook = targetSheet.getParent(); if (targetWorkbook == null) return; IPart target = request.getPrimaryTarget(); Object targetModel = target == null ? viewer.getAdapter(ITopic.class) : MindMapUtils.getRealModel(target); Object[] elements = getElementsFromClipboard(viewer, targetModel); if (elements == null || elements.length == 0) return; paste(request, elements, viewer, target, MindMapUtils.getRealModels( request.getTargets()).toArray(), targetWorkbook); // if (transferMap != null) { // transferMap.clear(); // transferMap = null; // } } protected void paste(Request request, Object[] elements, IViewer viewer, IPart targetPart, Object[] targets, IWorkbook targetWorkbook) { ICloneData result = targetWorkbook.clone(Arrays.asList(elements)); if (!result.hasCloned()) return; Collection<Object> cloneds = result.getCloneds(); ITopic targetTopic; if (targets.length > 0 && targets[0] instanceof ITopic) { targetTopic = (ITopic) targets[0]; } else { targetTopic = (ITopic) viewer.getAdapter(ITopic.class); } ISheet sheet = (ISheet) viewer.getAdapter(ISheet.class); List<Command> cmds = new ArrayList<Command>(cloneds.size()); for (Object cloned : cloneds) { if (cloned instanceof ITopic) { if (targetTopic != null) { ITopic clonedTopic = (ITopic) cloned; AddTopicCommand addTopic = new AddTopicCommand(clonedTopic, targetTopic, -1, ITopic.ATTACHED); cmds.add(addTopic); ModifyPositionCommand resetPosition = new ModifyPositionCommand( clonedTopic, null); cmds.add(resetPosition); } } else if (cloned instanceof IRelationship) { if (sheet != null) { IRelationship clonedRelationship = (IRelationship) cloned; AddRelationshipCommand addRel = new AddRelationshipCommand( clonedRelationship, sheet); cmds.add(addRel); } } else if (cloned instanceof IMarker || cloned instanceof IMarkerRef) { if (targetTopic != null) { IMarker marker = (cloned instanceof IMarker) ? (IMarker) cloned : ((IMarkerRef) cloned).getMarker(); if (marker != null) { IMarkerGroup group = marker.getParent(); if (group.isSingleton()) { for (IMarker m : group.getMarkers()) { if (targetTopic.hasMarker(m.getId())) { cmds.add(new DeleteMarkerCommand( targetTopic, m.getId())); } } } String markerId = (cloned instanceof IMarker) ? ((IMarker) cloned) .getId() : ((IMarkerRef) cloned).getMarkerId(); AddMarkerCommand addMarker = new AddMarkerCommand( targetTopic, markerId); cmds.add(addMarker); } } } else if (cloned instanceof IBoundary) { IBoundary b = (IBoundary) cloned; List<ITopic> topics = getSiblingTopics(targets); if (!topics.isEmpty() && !hasBoundary(topics)) { if (topics.get(0).isAttached()) { cmds.add(new ModifyTopicRangeCommand(b, topics.get(0), topics.get(topics.size() - 1))); cmds.add(new AddBoundaryCommand(b, topics.get(0) .getParent())); } else if (topics.size() == 1 && !topics.get(0).isRoot()) { cmds.add(new ModifyBoundaryMasterCommand(b, true)); cmds.add(new AddBoundaryCommand(b, topics.get(0))); } } } else if (cloned instanceof IImage) { IImage image = (IImage) cloned; if (targetTopic != null) { cmds.add(new ModifyImageSourceCommand(targetTopic, image .getSource())); cmds.add(new ModifyImageSizeCommand(targetTopic, image .getWidth(), image.getHeight())); cmds.add(new ModifyImageAlignmentCommand(targetTopic, image .getAlignment())); } } } // if (isCutPrev) // modifyTopicLinksRef(elements, targetWorkbook, cmds, result); if (cmds.isEmpty()) return; CompoundCommand cmd = new CompoundCommand(cmds); cmd.setLabel(CommandMessages.Command_Paste); saveAndRun(cmd, request.getTargetDomain()); select(cmd.getSources(), viewer); } // private void modifyTopicLinksRef(Object[] elements, // IWorkbook targetWorkbook, List<Command> cmds, ICloneData result) { // // ITopicRefCounter topicLinkRef = (ITopicRefCounter) targetWorkbook // .getAdapter(ITopicRefCounter.class); // for (Object element : elements) { // if (element instanceof ITopic) { // ITopic originTopic = (ITopic) transferMap.get(element); // ITopic clonedTopic = (ITopic) result.get(element); // modifyTopicLink(originTopic, clonedTopic, topicLinkRef, cmds); // } // } // } // private void modifyTopicLink(ITopic originTopic, ITopic clonedTopic, // ITopicRefCounter topicLinkRef, List<Command> cmds) { // String oldHref = originTopic.getId(); // List<ITopic> topicLinks = topicLinkRef.getLinkTopics(oldHref); // if (topicLinks != null && !topicLinks.isEmpty()) { // String newHref = clonedTopic.getId(); //// ModifyTopicLinkCommand cmd = new ModifyTopicLinkCommand(topicLinks, //// newHref); //// cmds.add(cmd); //// topicLinkRef.modifyTargetLink(oldHref, newHref); // // ModifyTopicHyperlinkCommand command = new ModifyTopicHyperlinkCommand( // topicLinks, "xmind:#" + newHref); //$NON-NLS-1$ // cmds.add(command); // } // // List<ITopic> children = originTopic.getAllChildren(); // if (children != null && !children.isEmpty()) { // for (int i = 0; i < children.size(); i++) { // ITopic oTopic = children.get(i); // ITopic eTopic = clonedTopic.getAllChildren().get(i); // modifyTopicLink(oTopic, eTopic, topicLinkRef, cmds); // } // } // } private List<ITopic> getSiblingTopics(Object[] targets) { ITopic start = null; ITopic end = null; for (Object o : targets) { if (o instanceof ITopic) { ITopic t = (ITopic) o; if (start != null && MindMapUtils.isDescendentOf(start, t)) { start = null; } if (end != null && MindMapUtils.isDescendentOf(end, t)) { end = null; } if (start == null) { start = t; } else if (t.getParent().equals(start.getParent()) && t.getType().equals(start.getType()) && t.getIndex() < start.getIndex()) { start = t; } if (end == null) { end = t; } else if (t.getParent().equals(end.getParent()) && t.getType().equals(end.getType()) && t.getIndex() > end.getIndex()) { end = t; } } } if (start == null || end == null) return Collections.emptyList(); return Arrays.asList(start, end); } private boolean hasBoundary(List<ITopic> topics) { if (topics.get(0).isAttached()) { int startIndex = topics.get(0).getIndex(); int endIndex = topics.get(topics.size() - 1).getIndex(); for (IBoundary b : topics.get(0).getParent().getBoundaries()) { if (b.getStartIndex() == startIndex || b.getEndIndex() == endIndex) return true; } } else if (topics.size() == 1) { for (IBoundary b : topics.get(0).getBoundaries()) { if (b.isMasterBoundary()) return true; } } return false; } protected Object[] getElementsFromClipboard(IViewer viewer, Object target) { IDndSupport dndSupport = viewer.getDndSupport(); if (dndSupport != null) { String[] ids = dndSupport.getDndClientIds(); if (ids.length > 0) { Clipboard clipboard = new Clipboard(Display.getCurrent()); try { return getElementsFromClipboard(viewer, target, clipboard, dndSupport, ids); } finally { clipboard.dispose(); } } } return null; } protected Object[] getElementsFromClipboard(IViewer viewer, Object target, Clipboard clipboard, IDndSupport dndSupport, String[] ids) { TransferData[] types = clipboard.getAvailableTypes(); IDndClient elementClient = dndSupport .getDndClient(DEFAULT_DND_CLIENT_ID); if (elementClient != null) { Object[] elements = getElementsFromClipboard(viewer, target, elementClient, clipboard, types); if (elements != null) return elements; } for (String id : ids) { if (!DEFAULT_DND_CLIENT_ID.equals(id)) { IDndClient client = dndSupport.getDndClient(id); Object[] elements = getElementsFromClipboard(viewer, target, client, clipboard, types); if (elements != null) return elements; } } return null; } protected Object[] getElementsFromClipboard(IViewer viewer, Object target, IDndClient client, Clipboard clipboard, TransferData[] types) { if (client == null) return null; Transfer transfer = client.getTransfer(); if (transfer == null) return null; // if (transfer instanceof MindMapElementTransfer) { // transferMap = ((MindMapElementTransfer) transfer).getTransferMap(); // } for (TransferData type : types) { if (transfer.isSupportedType(type)) { Object contents = clipboard.getContents(transfer); if (contents != null) return client.toViewerElements(contents, viewer, target); } } return null; } private void replaceAll(Request request) { String text = (String) request.getParameter(GEF.PARAM_TEXT); String replacement = (String) request .getParameter(MindMapUI.PARAM_REPLACEMENT); if (text == null || replacement == null) return; boolean ignoreCase = Boolean.TRUE.equals(request .getParameter(MindMapUI.PARAM_IGNORE_CASE)); List<IPart> targets = request.getTargets(); if (targets.isEmpty()) return; if (ignoreCase) { text = text.toLowerCase(); } PropertyCommandBuilder builder = new PropertyCommandBuilder(request); if (builder.canStart()) { builder.start(); for (IPart target : targets) { Object model = MindMapUtils.getRealModel(target); if (model instanceof ITitled) { ITitled t = (ITitled) model; String oldText = t.getTitleText(); if (oldText != null) { String searchText = ignoreCase ? oldText.toLowerCase() : oldText; StringBuilder sb = new StringBuilder(oldText.length() + replacement.length()); int start = 0; int index = searchText.indexOf(text); while (index >= 0) { sb.append(oldText.substring(start, index)); sb.append(replacement); start = index + text.length(); index = searchText.indexOf(text, start); } if (start < oldText.length()) { sb.append(oldText.substring(start)); } String newText = sb.toString(); if (!newText.equals(oldText)) { builder.add(new ModifyTitleTextCommand(t, newText), true); } } builder.addSource(t, true); } } builder.end(); } } }