Java tutorial
/* * Freeplane - mind map editor * Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev * * This file is created by Dimitry Polivaev in 2008. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.freeplane.features.clipboard.mindmapmode; import java.awt.Graphics2D; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.swing.JFileChooser; import javax.swing.text.BadLocationException; import javax.swing.text.Element; import javax.swing.text.ElementIterator; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import org.apache.commons.lang.StringUtils; import org.freeplane.core.resources.ResourceController; import org.freeplane.core.ui.ExampleFileFilter; import org.freeplane.core.ui.components.UITools; import org.freeplane.core.util.FileUtils; import org.freeplane.core.util.FixedHTMLWriter; import org.freeplane.core.util.HtmlUtils; import org.freeplane.core.util.LogUtils; import org.freeplane.core.util.TextUtils; import org.freeplane.features.attribute.AttributeController; import org.freeplane.features.clipboard.ClipboardController; import org.freeplane.features.clipboard.MindMapNodesSelection; import org.freeplane.features.link.LinkController; import org.freeplane.features.link.NodeLinks; import org.freeplane.features.link.mindmapmode.MLinkController; import org.freeplane.features.map.FreeNode; import org.freeplane.features.map.IMapSelection; import org.freeplane.features.map.MapModel; import org.freeplane.features.map.MapReader; import org.freeplane.features.map.MapReader.NodeTreeCreator; import org.freeplane.features.map.MapWriter.Hint; import org.freeplane.features.map.MapWriter.Mode; import org.freeplane.features.map.NodeModel; import org.freeplane.features.map.mindmapmode.MMapController; import org.freeplane.features.mode.Controller; import org.freeplane.features.mode.ModeController; import org.freeplane.features.text.TextController; import org.freeplane.features.text.mindmapmode.MTextController; import org.freeplane.features.url.UrlManager; import org.freeplane.n3.nanoxml.XMLException; import org.freeplane.view.swing.features.filepreview.ExternalResource; import org.freeplane.view.swing.features.filepreview.ViewerController; import org.freeplane.view.swing.features.filepreview.ViewerController.PasteMode; /** * @author Dimitry Polivaev */ public class MClipboardController extends ClipboardController { public static final String RESOURCES_REMIND_USE_RICH_TEXT_IN_NEW_NODES = "remind_use_rich_text_in_new_nodes"; private class DirectHtmlFlavorHandler implements IDataFlavorHandler { private String textFromClipboard; public DirectHtmlFlavorHandler(final String textFromClipboard) { this.textFromClipboard = textFromClipboard; } void paste(final NodeModel target) { textFromClipboard = cleanHtml(textFromClipboard); final NodeModel node = Controller.getCurrentModeController().getMapController() .newNode(textFromClipboard, Controller.getCurrentController().getMap()); final String text = textFromClipboard; final Matcher m = HREF_PATTERN.matcher(text); if (m.matches()) { final String body = m.group(2); if (!body.matches(".*<\\s*a.*")) { final String href = m.group(1); ((MLinkController) LinkController.getController()).setLinkTypeDependantLink(node, href); } } ((MMapController) Controller.getCurrentModeController().getMapController()).insertNode(node, target); } public void paste(Transferable t, final NodeModel target, final boolean asSibling, final boolean isLeft, int dropAction) { paste(target); } } private class FileListFlavorHandler implements IDataFlavorHandler { final List<File> fileList; public FileListFlavorHandler(final List<File> fileList) { super(); this.fileList = fileList; } public void paste(Transferable t, final NodeModel target, final boolean asSibling, final boolean isLeft, int dropAction) { boolean pasteImages = dropAction == DnDConstants.ACTION_COPY; ViewerController viewerController = ((ViewerController) Controller.getCurrentModeController() .getExtension(ViewerController.class)); for (final File file : fileList) { if (pasteImages && viewerController.paste(file, target, PasteMode.valueOf(asSibling), isLeft)) { continue; } final MMapController mapController = (MMapController) Controller.getCurrentModeController() .getMapController(); final NodeModel node = mapController.newNode(file.getName(), target.getMap()); ((MLinkController) LinkController.getController()).setLinkTypeDependantLink(node, file); mapController.insertNode(node, target, asSibling, isLeft, isLeft); } } } interface IDataFlavorHandler { void paste(Transferable t, NodeModel target, boolean asSibling, boolean isLeft, int dropAction); } private class MindMapNodesFlavorHandler implements IDataFlavorHandler { private final String textFromClipboard; public MindMapNodesFlavorHandler(final String textFromClipboard) { this.textFromClipboard = textFromClipboard; } public void paste(Transferable t, final NodeModel target, final boolean asSibling, final boolean isLeft, int dropAction) { if (textFromClipboard != null) { paste(textFromClipboard, target, asSibling, isLeft); } } private void paste(final String text, final NodeModel target, final boolean asSibling, final boolean isLeft) { final String[] textLines = text.split(ClipboardController.NODESEPARATOR); final MMapController mapController = (MMapController) Controller.getCurrentModeController() .getMapController(); final MapReader mapReader = mapController.getMapReader(); final NodeTreeCreator nodeTreeCreator = mapReader.nodeTreeCreator(target.getMap()); nodeTreeCreator.setHint(Hint.MODE, Mode.CLIPBOARD); for (int i = 0; i < textLines.length; ++i) { try { final NodeModel newModel = nodeTreeCreator.create(new StringReader(textLines[i])); newModel.removeExtension(FreeNode.class); final boolean wasLeft = newModel.isLeft(); mapController.insertNode(newModel, target, asSibling, isLeft, wasLeft != isLeft); } catch (final XMLException e) { LogUtils.severe("error on paste", e); } } nodeTreeCreator.finish(target); } } private static class PasteHtmlWriter extends FixedHTMLWriter { private final Element element; public PasteHtmlWriter(final Writer writer, final Element element, final HTMLDocument doc, final int pos, final int len) { super(writer, doc, pos, len); this.element = getStandAloneElement(element); } @Override protected ElementIterator getElementIterator() { return new ElementIterator(element); } private Element getStandAloneElement(final Element element) { final String name = element.getName(); if (name.equals("ul") || name.equals("ol") || name.equals("table") || name.equals("html")) { return element; } return getStandAloneElement(element.getParentElement()); } @Override public void write() throws IOException, BadLocationException { if (element.getName().equals("html")) { super.write(); return; } write("<html>"); super.write(); write("</html>"); } } private class StringFlavorHandler implements IDataFlavorHandler { private final String textFromClipboard; public StringFlavorHandler(final String textFromClipboard) { this.textFromClipboard = textFromClipboard; } public void paste(Transferable t, final NodeModel target, final boolean asSibling, final boolean isLeft, int dropAction) { final TextFragment[] textFragments = split(textFromClipboard); pasteStringWithoutRedisplay(textFragments, target, asSibling, isLeft); } private TextFragment[] split(final String textFromClipboard) { final LinkedList<TextFragment> textFragments = new LinkedList<TextFragment>(); final String[] textLines = textFromClipboard.split("\n"); for (int i = 0; i < textLines.length; ++i) { String text = textLines[i]; text = text.replaceAll("\t", " "); if (text.matches(" *")) { continue; } int depth = 0; while (depth < text.length() && text.charAt(depth) == ' ') { ++depth; } final String visibleText = text.trim(); final String link = LinkController.findLink(text); if (!visibleText.equals("")) { textFragments.add(new TextFragment(visibleText, link, depth)); } } return textFragments.toArray(new TextFragment[textFragments.size()]); } } private class StructuredHtmlFlavorHandler implements IDataFlavorHandler { private final String textFromClipboard; public StructuredHtmlFlavorHandler(final String textFromClipboard) { this.textFromClipboard = textFromClipboard; } private String addFragment(final HTMLDocument doc, final Element element, final int depth, final int start, final int end, final LinkedList<TextFragment> htmlFragments) throws BadLocationException, IOException { final String paragraphText = doc.getText(start, end - start).trim(); if (paragraphText.length() > 0) { final StringWriter out = new StringWriter(); new PasteHtmlWriter(out, element, doc, start, end - start).write(); final String string = out.toString(); if (!string.equals("")) { final String link = LinkController.findLink(string); final TextFragment htmlFragment = new TextFragment(string, link, depth); htmlFragments.add(htmlFragment); } } return paragraphText; } private Element getParentElement(final HTMLDocument doc) { final Element htmlRoot = doc.getDefaultRootElement(); final Element bodyElement = htmlRoot.getElement(htmlRoot.getElementCount() - 1); Element parentCandidate = bodyElement; do { if (parentCandidate.getElementCount() > 1) { return parentCandidate; } parentCandidate = parentCandidate.getElement(0); } while (!(parentCandidate.isLeaf() || parentCandidate.getName().equalsIgnoreCase("p-implied"))); return bodyElement; } private boolean isSeparateElement(final Element current) { return !current.isLeaf(); } public void paste(Transferable t, final NodeModel target, final boolean asSibling, final boolean isLeft, int dropAction) { pasteHtmlWithoutRedisplay(textFromClipboard, target, asSibling, isLeft); } private void pasteHtmlWithoutRedisplay(final Object t, final NodeModel parent, final boolean asSibling, final boolean isLeft) { String textFromClipboard = (String) t; textFromClipboard = cleanHtml(textFromClipboard); final TextFragment[] htmlFragments = split(textFromClipboard); pasteStringWithoutRedisplay(htmlFragments, parent, asSibling, isLeft); } private void split(final HTMLDocument doc, final Element parent, final LinkedList<TextFragment> htmlFragments, int depth) throws BadLocationException, IOException { final int elementCount = parent.getElementCount(); int headerDepth = 0; boolean headerFound = false; int start = -1; int end = -1; Element last = null; for (int i = 0; i < elementCount; i++) { final Element current = parent.getElement(i); final String name = current.getName(); final Matcher matcher = HEADER_REGEX.matcher(name); if (matcher.matches()) { try { if (!headerFound) { depth--; } final int newHeaderDepth = Integer.parseInt(matcher.group(1)); depth += newHeaderDepth - headerDepth; headerDepth = newHeaderDepth; headerFound = true; } catch (final NumberFormatException e) { LogUtils.severe(e); } } else { if (headerFound) { headerFound = false; depth++; } } final boolean separateElement = isSeparateElement(current); if (separateElement && current.getElementCount() != 0) { start = -1; last = null; split(doc, current, htmlFragments, depth + 1); continue; } if (separateElement && start != -1) { addFragment(doc, last, depth, start, end, htmlFragments); } if (start == -1 || separateElement) { start = current.getStartOffset(); last = current; } end = current.getEndOffset(); if (separateElement) { addFragment(doc, current, depth, start, end, htmlFragments); } } if (start != -1) { addFragment(doc, last, depth, start, end, htmlFragments); } } private TextFragment[] split(final String text) { final LinkedList<TextFragment> htmlFragments = new LinkedList<TextFragment>(); final HTMLEditorKit kit = new HTMLEditorKit(); final HTMLDocument doc = new HTMLDocument(); final StringReader buf = new StringReader(text); try { kit.read(buf, doc, 0); final Element parent = getParentElement(doc); split(doc, parent, htmlFragments, 0); } catch (final IOException e) { LogUtils.severe(e); } catch (final BadLocationException e) { LogUtils.severe(e); } return htmlFragments.toArray(new TextFragment[htmlFragments.size()]); } } private static class TextFragment { int depth; String link; String text; public TextFragment(final String text, final String link, final int depth) { super(); this.text = text; this.depth = depth; this.link = link; } } private class ImageFlavorHandler implements IDataFlavorHandler { private static final String IMAGE_FORMAT = "png"; final private BufferedImage image; public ImageFlavorHandler(BufferedImage img) { super(); BufferedImage fixedImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D fig = fixedImg.createGraphics(); fig.drawImage(img, 0, 0, null); fig.dispose(); fixedImg.flush(); this.image = fixedImg; } public void paste(Transferable t, NodeModel target, boolean asSibling, boolean isLeft, int dropAction) { final ModeController modeController = Controller.getCurrentModeController(); final MMapController mapController = (MMapController) modeController.getMapController(); File mindmapFile = target.getMap().getFile(); if (mindmapFile == null) { UITools.errorMessage(TextUtils.getRawText("map_not_saved")); return; } final String mmFileName = mindmapFile.getName(); String fileNameTemplate = mmFileName.substring(0, mmFileName.lastIndexOf('.')) + "_"; while (fileNameTemplate.length() < 3) fileNameTemplate = fileNameTemplate + '_'; //file that we'll save to disk. File file; try { final File dir = mindmapFile.getParentFile(); file = File.createTempFile(fileNameTemplate, "." + IMAGE_FORMAT, dir); String imgfilepath = file.getAbsolutePath(); File tempFile = file = new File(imgfilepath); final JFileChooser fileChooser = new JFileChooser(file); final ExampleFileFilter filter = new ExampleFileFilter(); filter.addExtension(IMAGE_FORMAT); fileChooser.setAcceptAllFileFilterUsed(false); fileChooser.setFileFilter(filter); fileChooser.setSelectedFile(file); int returnVal = fileChooser.showSaveDialog(UITools.getFrame()); if (returnVal != JFileChooser.APPROVE_OPTION) { tempFile.delete(); return; } file = fileChooser.getSelectedFile(); if (tempFile.exists() && !file.getAbsoluteFile().equals(tempFile)) { tempFile.delete(); } if (file.isDirectory()) return; if (!FileUtils.getExtension(file.getName()).equals(IMAGE_FORMAT)) file = new File(file.getPath() + '.' + IMAGE_FORMAT); final URI uri = LinkController.toLinkTypeDependantURI(mindmapFile, file); ImageIO.write(image, IMAGE_FORMAT, file); final NodeModel node = mapController.newNode(file.getName(), target.getMap()); final ExternalResource extension = new ExternalResource(uri); node.addExtension(extension); mapController.insertNode(node, target, asSibling, isLeft, isLeft); } catch (IOException e) { e.printStackTrace(); } } } private static final Pattern HEADER_REGEX = Pattern.compile("h(\\d)", Pattern.CASE_INSENSITIVE); private static final Pattern HREF_PATTERN = Pattern .compile("<html>\\s*<body>\\s*<a\\s+href=\"([^>]+)\">(.*)</a>\\s*</body>\\s*</html>"); private static final String RESOURCE_UNFOLD_ON_PASTE = "unfold_on_paste"; public static final String RESOURCES_CUT_NODES_WITHOUT_QUESTION = "cut_nodes_without_question"; public static String firstLetterCapitalized(final String text) { if (text == null || text.length() == 0) { return text; } return text.substring(0, 1).toUpperCase() + text.substring(1, text.length()); } private List<NodeModel> newNodes; /** * @param modeController */ public MClipboardController() { super(); createActions(); } private String cleanHtml(String in) { in = in.replaceFirst("(?i)(?s)<head>.*</head>", "").replaceFirst("(?i)(?s)^.*<html[^>]*>", "<html>") .replaceFirst("(?i)(?s)<body [^>]*>", "<body>").replaceAll("(?i)(?s)<script.*?>.*?</script>", "") .replaceAll("(?i)(?s)</?tbody.*?>", "").replaceAll("(?i)(?s)<!--.*?-->", "") .replaceAll("(?i)(?s)</?o[^>]*>", ""); if (StringUtils.equals( ResourceController.getResourceController().getProperty("cut_out_pictures_when_pasting_html"), "true")) { in = in.replaceAll("(?i)(?s)<img[^>]*>", ""); } in = HtmlUtils.unescapeHTMLUnicodeEntity(in); return in; } /** * @param modeController */ private void createActions() { final ModeController modeController = Controller.getCurrentModeController(); modeController.addAction(new CutAction()); modeController.addAction(new PasteAction()); modeController.addAction(new SelectedPasteAction()); modeController.addAction(new CloneAction()); modeController.addAction(new MoveAction()); } @Override public Transferable copy(IMapSelection selection) { final List<NodeModel> collection = selection.getSortedSelection(true); final MindMapNodesSelection transferable = copy(collection, false); transferable.setNodeObjects(collection); return transferable; } Transferable cut(final List<NodeModel> collection) { Controller.getCurrentModeController().getMapController().sortNodesByDepth(collection); final MindMapNodesSelection transferable = copy(collection, true); for (final NodeModel node : collection) { if (node.getParentNode() != null) { ((MMapController) Controller.getCurrentModeController().getMapController()).deleteNode(node); } } setClipboardContents(transferable); return transferable; } private IDataFlavorHandler getFlavorHandler(final Transferable t) { if (t.isDataFlavorSupported(MindMapNodesSelection.mindMapNodesFlavor)) { try { final String textFromClipboard = t.getTransferData(MindMapNodesSelection.mindMapNodesFlavor) .toString(); return new MindMapNodesFlavorHandler(textFromClipboard); } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } if (t.isDataFlavorSupported(MindMapNodesSelection.fileListFlavor)) { try { final List<File> fileList = castToFileList(t.getTransferData(MindMapNodesSelection.fileListFlavor)); if (!shouldIgnoreFileListFlavor(fileList)) return new FileListFlavorHandler(fileList); } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } final ResourceController resourceController = ResourceController.getResourceController(); if (t.isDataFlavorSupported(MindMapNodesSelection.htmlFlavor)) { try { final String textFromClipboard = t.getTransferData(MindMapNodesSelection.htmlFlavor).toString(); if (textFromClipboard.charAt(0) != 65533) { if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { final MTextController textController = (MTextController) TextController.getController(); final boolean richText = textController .useRichTextInEditor(RESOURCES_REMIND_USE_RICH_TEXT_IN_NEW_NODES); if (richText) { final boolean structuredHtmlImport = resourceController .getBooleanProperty("structured_html_import"); final IDataFlavorHandler htmlFlavorHandler; if (structuredHtmlImport) { htmlFlavorHandler = new StructuredHtmlFlavorHandler(textFromClipboard); } else { htmlFlavorHandler = new DirectHtmlFlavorHandler(textFromClipboard); } return htmlFlavorHandler; } } } } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { try { final String plainTextFromClipboard = t.getTransferData(DataFlavor.stringFlavor).toString(); return new StringFlavorHandler(plainTextFromClipboard); } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } if (t.isDataFlavorSupported(DataFlavor.imageFlavor)) { try { BufferedImage image = (BufferedImage) t.getTransferData(DataFlavor.imageFlavor); return new ImageFlavorHandler(image); } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } return null; } private boolean shouldIgnoreFileListFlavor(final List<File> fileList) { if (fileList.isEmpty()) return true; final File file = fileList.get(0); if (file.isDirectory()) return false; final String name = file.getName(); return name.endsWith(".URL") || name.endsWith(".url"); } @SuppressWarnings("unchecked") private List<File> castToFileList(Object transferData) { return (List<File>) transferData; } Collection<IDataFlavorHandler> getFlavorHandlers() { final Transferable t = getClipboardContents(); final Collection<IDataFlavorHandler> handlerList = new LinkedList<IDataFlavorHandler>(); if (t == null) { return handlerList; } if (t.isDataFlavorSupported(MindMapNodesSelection.mindMapNodesFlavor)) { try { final String textFromClipboard = t.getTransferData(MindMapNodesSelection.mindMapNodesFlavor) .toString(); handlerList.add(new MindMapNodesFlavorHandler(textFromClipboard)); } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } if (t.isDataFlavorSupported(MindMapNodesSelection.htmlFlavor)) { try { final String textFromClipboard = t.getTransferData(MindMapNodesSelection.htmlFlavor).toString(); if (textFromClipboard.charAt(0) != 65533) { if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { handlerList.add(new StructuredHtmlFlavorHandler(textFromClipboard)); handlerList.add(new DirectHtmlFlavorHandler(textFromClipboard)); } } } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { try { final String plainTextFromClipboard = t.getTransferData(DataFlavor.stringFlavor).toString(); handlerList.add(new StringFlavorHandler(plainTextFromClipboard)); } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } if (t.isDataFlavorSupported(MindMapNodesSelection.fileListFlavor)) { try { final List<File> fileList = castToFileList(t.getTransferData(MindMapNodesSelection.fileListFlavor)); handlerList.add(new FileListFlavorHandler(fileList)); } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } if (t.isDataFlavorSupported(DataFlavor.imageFlavor)) { try { BufferedImage image = (BufferedImage) t.getTransferData(DataFlavor.imageFlavor); handlerList.add(new ImageFlavorHandler(image)); } catch (final UnsupportedFlavorException e) { } catch (final IOException e) { } } return handlerList; } public void paste(final Transferable t, final NodeModel target, final boolean asSibling, final boolean isLeft) { paste(t, target, asSibling, isLeft, DnDConstants.ACTION_NONE); } public void paste(final Transferable t, final NodeModel target, final boolean asSibling, final boolean isLeft, int dropAction) { if (t == null) { return; } /* * DataFlavor[] fl = t.getTransferDataFlavors(); for (int i = 0; i < * fl.length; i++) { System.out.println(fl[i]); } */ final IDataFlavorHandler handler = getFlavorHandler(t); paste(t, handler, target, asSibling, isLeft, dropAction); } void paste(final Transferable t, final IDataFlavorHandler handler, final NodeModel target, final boolean asSibling, final boolean isLeft) { paste(t, handler, target, asSibling, isLeft, DnDConstants.ACTION_NONE); } void paste(final Transferable t, final IDataFlavorHandler handler, final NodeModel target, final boolean asSibling, final boolean isLeft, int dropAction) { if (handler == null) { return; } final MMapController mapController = (MMapController) Controller.getCurrentModeController() .getMapController(); if (asSibling && !mapController.isWriteable(target.getParentNode()) || !asSibling && !mapController.isWriteable(target)) { final String message = TextUtils.getText("node_is_write_protected"); UITools.errorMessage(message); return; } try { Controller.getCurrentController().getViewController().setWaitingCursor(true); if (newNodes == null) { newNodes = new LinkedList<NodeModel>(); } newNodes.clear(); handler.paste(t, target, asSibling, isLeft, dropAction); final ModeController modeController = Controller.getCurrentModeController(); if (!asSibling && modeController.getMapController().isFolded(target) && ResourceController.getResourceController().getBooleanProperty(RESOURCE_UNFOLD_ON_PASTE)) { modeController.getMapController().setFolded(target, false); } for (final NodeModel child : newNodes) { AttributeController.getController().performRegistrySubtreeAttributes(child); } } finally { Controller.getCurrentController().getViewController().setWaitingCursor(false); } } private void pasteStringWithoutRedisplay(final TextFragment[] textFragments, NodeModel parent, final boolean asSibling, final boolean isLeft) { final MapModel map = parent.getMap(); int insertionIndex; if (asSibling) { NodeModel target = parent; parent = parent.getParentNode(); insertionIndex = parent.getChildPosition(target); } else { insertionIndex = parent.getChildCount(); } final ArrayList<NodeModel> parentNodes = new ArrayList<NodeModel>(); final ArrayList<Integer> parentNodesDepths = new ArrayList<Integer>(); parentNodes.add(parent); parentNodesDepths.add(new Integer(-1)); final MMapController mapController = (MMapController) Controller.getCurrentModeController() .getMapController(); for (int i = 0; i < textFragments.length; ++i) { final TextFragment textFragment = textFragments[i]; String text = textFragment.text; final String link = textFragment.link; URI uri = null; if (link != null) { try { URI linkUri = new URI(link); uri = linkUri; File absoluteFile = UrlManager.getController().getAbsoluteFile(map, uri); if (absoluteFile != null) { //if ("file".equals(linkUri.getScheme())) { final File mapFile = map.getFile(); uri = LinkController.toLinkTypeDependantURI(mapFile, absoluteFile); if (link.equals(text)) { text = uri.toString(); } } } catch (Exception e) { } } final NodeModel node = mapController.newNode(text, map); if (uri != null) { NodeLinks.createLinkExtension(node).setHyperLink(uri); } for (int j = parentNodes.size() - 1; j >= 0; --j) { if (textFragment.depth > ((Integer) parentNodesDepths.get(j)).intValue()) { for (int k = j + 1; k < parentNodes.size(); ++k) { final NodeModel n = (NodeModel) parentNodes.get(k); if (n.getParentNode() == null) { mapController.insertNode(n, parent, insertionIndex++); } parentNodes.remove(k); parentNodesDepths.remove(k); } final NodeModel target = (NodeModel) parentNodes.get(j); node.setLeft(isLeft); if (target != parent) { target.setFolded(true); target.insert(node, target.getChildCount()); } parentNodes.add(node); parentNodesDepths.add(new Integer(textFragment.depth)); break; } } } { for (int k = 0; k < parentNodes.size(); ++k) { final NodeModel n = (NodeModel) parentNodes.get(k); if (map.getRootNode() != n && n.getParentNode() == null) { mapController.insertNode(n, parent, insertionIndex++); } } } } private enum Operation { CLONE, MOVE }; public void addClone(final Transferable transferable, final NodeModel target) { processTransferable(transferable, target, Operation.CLONE); } public void move(final Transferable transferable, final NodeModel target) { processTransferable(transferable, target, Operation.MOVE); } private void processTransferable(final Transferable transferable, final NodeModel target, Operation operation) { if (!transferable.isDataFlavorSupported(MindMapNodesSelection.mindMapNodeObjectsFlavor)) return; try { @SuppressWarnings("unchecked") final List<NodeModel> clonedNodes = (List<NodeModel>) transferable .getTransferData(MindMapNodesSelection.mindMapNodeObjectsFlavor); for (NodeModel clonedNode : clonedNodes) { if (clonedNode.getParentNode() == null || !clonedNode.getMap().equals(target.getMap())) return; final MMapController mapController = (MMapController) Controller.getCurrentModeController() .getMapController(); if (!clonedNode.isRoot() && !clonedNode.subtreeContainsCloneOf(target)) { switch (operation) { case CLONE: final NodeModel clone = clonedNode.cloneTree(); mapController.addNewNode(clone, target, target.getChildCount(), target.isNewChildLeft()); break; case MOVE: mapController.moveNodeAsChild(clonedNode, target, target.isNewChildLeft(), target.isNewChildLeft() != clonedNode.isLeft()); break; } } } } catch (Exception e) { LogUtils.severe(e); } } }