/*FreeMind - A Program for creating and viewing Mindmaps *Copyright (C) 2000-2004 Joerg Mueller, Daniel Polansky, Christian Foltin and others. * *See COPYING for Details * *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, write to the Free Software *Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Created on 09.05.2004 */ package freemind.modes.mindmapmode.actions; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JOptionPane; import freemind.controller.MindMapNodesSelection; import freemind.controller.actions.generated.instance.PasteNodeAction; import freemind.controller.actions.generated.instance.TransferableContent; import freemind.controller.actions.generated.instance.TransferableFile; import freemind.controller.actions.generated.instance.TransferableImage; import freemind.controller.actions.generated.instance.UndoPasteNodeAction; import freemind.controller.actions.generated.instance.XmlAction; import freemind.main.FreeMind; import freemind.main.FreeMindCommon; import freemind.main.HtmlTools; import freemind.main.Resources; import freemind.main.Tools; import freemind.main.XMLParseException; import freemind.modes.ControllerAdapter; import freemind.modes.MindMapNode; import freemind.modes.ModeController; import freemind.modes.NodeAdapter; import freemind.modes.mindmapmode.MindMapController; import freemind.modes.mindmapmode.MindMapMapModel; import freemind.modes.mindmapmode.MindMapNodeModel; import freemind.modes.mindmapmode.actions.xml.ActionPair; import freemind.modes.mindmapmode.actions.xml.ActorXml; public class PasteAction extends AbstractAction implements ActorXml { private static java.util.logging.Logger logger; private final MindMapController mMindMapController; private UndoPasteHandler mUndoPasteHandler; public PasteAction(MindMapController pMindMapController) { super(pMindMapController.getText("paste"), new ImageIcon( pMindMapController.getResource("images/editpaste.png"))); this.mMindMapController = pMindMapController; if (logger == null) { logger = mMindMapController.getFrame().getLogger( this.getClass().getName()); } setEnabled(false); this.mMindMapController.getActionFactory().registerActor(this, getDoActionClass()); // special undo handler for paste. mUndoPasteHandler = new UndoPasteHandler(mMindMapController); this.mMindMapController.getActionFactory().registerActor( mUndoPasteHandler, UndoPasteNodeAction.class); } public void actionPerformed(ActionEvent e) { Transferable clipboardContents = this.mMindMapController .getClipboardContents(); MindMapNode selectedNode = this.mMindMapController.getSelected(); this.mMindMapController.paste(clipboardContents, selectedNode); } /* * (non-Javadoc) * * @see * freemind.controller.actions.ActorXml#act(freemind.controller.actions. * generated.instance.XmlAction) */ public void act(XmlAction action) { PasteNodeAction pasteAction = (PasteNodeAction) action; _paste(getTransferable(pasteAction.getTransferableContent()), mMindMapController.getNodeFromID(pasteAction.getNode()), pasteAction.getAsSibling(), pasteAction.getIsLeft()); } /* * (non-Javadoc) * * @see freemind.controller.actions.ActorXml#getDoActionClass() */ public Class getDoActionClass() { return PasteNodeAction.class; } /** * @param t * @param coord * @param pUndoAction * is filled automatically when not null. * @return a new PasteNodeAction. */ public PasteNodeAction getPasteNodeAction(Transferable t, NodeCoordinate coord, UndoPasteNodeAction pUndoAction) { PasteNodeAction pasteAction = new PasteNodeAction(); final String targetId = mMindMapController.getNodeID(coord.target); pasteAction.setNode(targetId); pasteAction.setTransferableContent(getTransferableContent(t, pUndoAction)); pasteAction.setAsSibling(coord.asSibling); pasteAction.setIsLeft(coord.isLeft); if (pUndoAction != null) { pUndoAction.setNode(targetId); pUndoAction.setAsSibling(coord.asSibling); pUndoAction.setIsLeft(coord.isLeft); String s = mMindMapController.marshall(pUndoAction); logger.fine("Undo action: " + s); } return pasteAction; } /** URGENT: Change this method. */ public void paste(MindMapNode node, MindMapNode parent) { if (node != null) { insertNodeInto(node, parent); mMindMapController.nodeStructureChanged(parent); } } /** * @param t * the content * @param target * where to add the content * @param asSibling * if true, the content is added beside the target, otherwise as * new children * @param isLeft * if something is pasted as a sibling to root, it must be * decided on which side of root * @return true, if successfully executed. */ public boolean paste(Transferable t, MindMapNode target, boolean asSibling, boolean isLeft) { UndoPasteNodeAction undoAction = new UndoPasteNodeAction(); PasteNodeAction pasteAction; pasteAction = getPasteNodeAction(t, new NodeCoordinate(target, asSibling, isLeft), undoAction); // Undo-action /* * how to construct the undo action for a complex paste? a) Paste pastes * a number of new nodes that are adjacent. This number should be * determined. * * * d) But, as there are many possibilities which data flavor is pasted, * it has to be determined before, which one will be taken. */ return mMindMapController.doTransaction("paste", new ActionPair(pasteAction, undoAction)); } public static class NodeCoordinate { public MindMapNode target; public boolean asSibling; public boolean isLeft; public NodeCoordinate(MindMapNode target, boolean asSibling, boolean isLeft) { this.target = target; this.asSibling = asSibling; this.isLeft = isLeft; } public MindMapNode getNode() { if (asSibling) { MindMapNode parentNode = target.getParentNode(); return (MindMapNode) parentNode.getChildAt(parentNode .getChildPosition(target) - 1); } else { logger.finest("getChildCount = " + target.getChildCount() + ", target = " + target); return (MindMapNode) target .getChildAt(target.getChildCount() - 1); } } public NodeCoordinate(MindMapNode node, boolean isLeft) { this.isLeft = isLeft; MindMapNode parentNode = node.getParentNode(); int childPosition = parentNode.getChildPosition(node); if (childPosition == parentNode.getChildCount() - 1) { target = parentNode; asSibling = false; } else { target = (MindMapNode) parentNode.getChildAt(childPosition + 1); asSibling = true; } } } private interface DataFlavorHandler { void paste(Object TransferData, MindMapNode target, boolean asSibling, boolean isLeft, Transferable t) throws UnsupportedFlavorException, IOException; DataFlavor getDataFlavor(); } private class FileListFlavorHandler implements DataFlavorHandler { public void paste(Object TransferData, MindMapNode target, boolean asSibling, boolean isLeft, Transferable t) { // TODO: Does not correctly interpret asSibling. List fileList = (List) TransferData; for (ListIterator it = fileList.listIterator(); it.hasNext();) { File file = (File) it.next(); MindMapNode node = mMindMapController.newNode(file.getName(), target.getMap()); node.setLeft(isLeft); node.setLink(Tools.fileToRelativeUrlString(file, mMindMapController.getModel().getFile())); insertNodeInto((MindMapNodeModel) node, target, asSibling, isLeft, false); // addUndoAction(node); } } public DataFlavor getDataFlavor() { return MindMapNodesSelection.fileListFlavor; } } private class MindMapNodesFlavorHandler implements DataFlavorHandler { public void paste(Object TransferData, MindMapNode target, boolean asSibling, boolean isLeft, Transferable t) { String textFromClipboard = (String) TransferData; if (textFromClipboard != null) { String[] textLines = textFromClipboard .split(ModeController.NODESEPARATOR); if (textLines.length > 1) { mMindMapController.getFrame().setWaitingCursor(true); } // and now? paste it: String mapContent = MindMapMapModel.MAP_INITIAL_START + FreeMind.XML_VERSION + "\"><node TEXT=\"DUMMY\">"; for (int j = 0; j < textLines.length; j++) { mapContent += textLines[j]; } mapContent += "</node></map>"; // logger.info("Pasting " + mapContent); try { MindMapNode node = mMindMapController.getMindMapMapModel() .loadTree( new MindMapMapModel.StringReaderCreator( mapContent), false); for (ListIterator i = node.childrenUnfolded(); i.hasNext();) { MindMapNodeModel importNode = (MindMapNodeModel) i .next(); insertNodeInto(importNode, target, asSibling, isLeft, true); // addUndoAction(importNode); } for (ListIterator i = node.childrenUnfolded(); i.hasNext();) { MindMapNodeModel importNode = (MindMapNodeModel) i .next(); mMindMapController.invokeHooksRecursively(importNode, mMindMapController.getModel()); } for (ListIterator i = node.childrenUnfolded(); i.hasNext();) { MindMapNodeModel importNode = (MindMapNodeModel) i .next(); mMindMapController.processUnfinishedLinksInHooks(importNode); } } catch (Exception e) { freemind.main.Resources.getInstance().logException(e); } } } public DataFlavor getDataFlavor() { return MindMapNodesSelection.mindMapNodesFlavor; } } private static final Pattern HREF_PATTERN = Pattern .compile("<html>\\s*<body>\\s*<a\\s+href=\"([^>]+)\">(.*)</a>\\s*</body>\\s*</html>"); private class DirectHtmlFlavorHandler implements DataFlavorHandler { public void paste(Object transferData, MindMapNode target, boolean asSibling, boolean isLeft, Transferable t) throws UnsupportedFlavorException, IOException { String textFromClipboard = (String) transferData; // workaround for java decoding bug // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6740877 if (textFromClipboard.charAt(0) == 65533) { throw new UnsupportedFlavorException( MindMapNodesSelection.htmlFlavor); } // ^ This outputs transfer data to standard output. I don't know // why. // { Alternative pasting of HTML mMindMapController.getFrame().setWaitingCursor(true); textFromClipboard = textFromClipboard .replaceFirst("(?i)(?s)<head>.*</head>", "") .replaceFirst("(?i)(?s)^.*<html[^>]*>", "<html>") .replaceFirst("(?i)(?s)<body [^>]*>", "<body>") .replaceAll("(?i)(?s)<script.*?>.*?</script>", "") .replaceAll("(?i)(?s)</?tbody.*?>", ""). // Java HTML Editor // does not like // the tag. replaceAll("(?i)(?s)<!--.*?-->", ""). // Java HTML Editor // shows comments in // not very nice // manner. replaceAll("(?i)(?s)</?o[^>]*>", ""); // Java HTML Editor // does not like // Microsoft Word's // <o> tag. if (Tools.safeEquals( mMindMapController.getFrame().getProperty( "cut_out_pictures_when_pasting_html"), "true")) { textFromClipboard = textFromClipboard.replaceAll( "(?i)(?s)<img[^>]*>", ""); } // Cut out images. textFromClipboard = HtmlTools .unescapeHTMLUnicodeEntity(textFromClipboard); MindMapNode node = mMindMapController.newNode(textFromClipboard, mMindMapController.getMap()); // if only one <a>...</a> element found, set link Matcher m = HREF_PATTERN.matcher(textFromClipboard); if (m.matches()) { final String body = m.group(2); if (!body.matches(".*<\\s*a.*")) { final String href = m.group(1); node.setLink(href); } } insertNodeInto(node, target); // addUndoAction(node); mMindMapController.getFrame().setWaitingCursor(false); } public DataFlavor getDataFlavor() { return MindMapNodesSelection.htmlFlavor; } } private class HtmlFlavorHandler implements DataFlavorHandler { public void paste(Object TransferData, MindMapNode target, boolean asSibling, boolean isLeft, Transferable t) throws UnsupportedFlavorException, IOException { // System.err.println("htmlFlavor"); String textFromClipboard = (String) TransferData; // ^ This outputs transfer data to standard output. I don't know // why. MindMapNode pastedNode = pasteStringWithoutRedisplay(t, target, asSibling, isLeft); textFromClipboard = textFromClipboard.replaceAll("<!--.*?-->", ""); // remove // HTML // comment String[] links = textFromClipboard .split("<[aA][^>]*[hH][rR][eE][fF]=\""); MindMapNode linkParentNode = null; URL referenceURL = null; boolean baseUrlCanceled = false; for (int i = 1; i < links.length; i++) { String link = links[i].substring(0, links[i].indexOf("\"")); String textWithHtml = links[i].replaceAll("^[^>]*>", "") .replaceAll("</[aA]>[\\s\\S]*", ""); String text = HtmlTools .toXMLUnescapedText(textWithHtml.replaceAll("\\n", "") .replaceAll("<[^>]*>", "").trim()); if (text.equals("")) { text = link; } URL linkURL = null; try { linkURL = new URL(link); } catch (MalformedURLException ex) { try { // Either invalid URL or relative URL if (referenceURL == null && !baseUrlCanceled) { String referenceURLString = JOptionPane .showInputDialog(mMindMapController .getView().getSelected(), mMindMapController .getText("enter_base_url")); if (referenceURLString == null) { baseUrlCanceled = true; } else { referenceURL = new URL(referenceURLString); } } linkURL = new URL(referenceURL, link); } catch (MalformedURLException ex2) { } } if (linkURL != null) { if (links.length == 2 & pastedNode != null) { // pastedNode != null iff the number of pasted lines is // one // The firts element in links[] array is never a link, // therefore // the condition links.length == 2 actually says // "there is one link". // Set link directly into node ((MindMapNodeModel) pastedNode).setLink(linkURL .toString()); break; } if (linkParentNode == null) { linkParentNode = mMindMapController.newNode("Links", target.getMap()); linkParentNode.setLeft(target.isNewChildLeft()); // Here we cannot set bold, because linkParentNode.font // is null insertNodeInto(linkParentNode, target); ((NodeAdapter) linkParentNode).setBold(true); } MindMapNode linkNode = mMindMapController.newNode(text, target.getMap()); linkNode.setLink(linkURL.toString()); insertNodeInto(linkNode, linkParentNode); } } } public DataFlavor getDataFlavor() { return MindMapNodesSelection.htmlFlavor; } } private class StringFlavorHandler implements DataFlavorHandler { public void paste(Object TransferData, MindMapNode target, boolean asSibling, boolean isLeft, Transferable t) throws UnsupportedFlavorException, IOException { // System.err.println("stringFlavor"); pasteStringWithoutRedisplay(t, target, asSibling, isLeft); } public DataFlavor getDataFlavor() { return DataFlavor.stringFlavor; } } private class ImageFlavorHandler implements DataFlavorHandler { public void paste(Object transferData, MindMapNode target, boolean asSibling, boolean isLeft, Transferable t) throws UnsupportedFlavorException, IOException { logger.info("imageFlavor"); mMindMapController.getFrame().setWaitingCursor(true); /* * BufferedImage img = null; try { img = ImageIO.read(new * File("image.jpg")); } catch (IOException e) { } */ String imgfile = "" + transferData; String strText = "<html><body><img src=\"" + imgfile + "\"/></body></html>"; MindMapNode node = mMindMapController.newNode(strText, mMindMapController.getMap()); // if only one <a>...</a> element found, set link insertNodeInto(node, target); // addUndoAction(node); mMindMapController.getFrame().setWaitingCursor(false); } public DataFlavor getDataFlavor() { return DataFlavor.imageFlavor; } } /* * */ private void _paste(Transferable t, MindMapNode target, boolean asSibling, boolean isLeft) { if (t == null) { return; } // Uncomment to print obtained data flavors /* * DataFlavor[] fl = t.getTransferDataFlavors(); for (int i = 0; i < * fl.length; i++) { System.out.println(fl[i]); } */ DataFlavorHandler[] dataFlavorHandlerList = getFlavorHandlers(); for (int i = 0; i < dataFlavorHandlerList.length; i++) { DataFlavorHandler handler = dataFlavorHandlerList[i]; DataFlavor flavor = handler.getDataFlavor(); if (t.isDataFlavorSupported(flavor)) { try { handler.paste(t.getTransferData(flavor), target, asSibling, isLeft, t); break; } catch (UnsupportedFlavorException e) { Resources.getInstance().logException(e); } catch (IOException e) { Resources.getInstance().logException(e); } } } mMindMapController.getFrame().setWaitingCursor(false); } /** */ private DataFlavorHandler[] getFlavorHandlers() { DataFlavorHandler[] dataFlavorHandlerList = new DataFlavorHandler[] { new FileListFlavorHandler(), new MindMapNodesFlavorHandler(), new DirectHtmlFlavorHandler(), new StringFlavorHandler(), new ImageFlavorHandler() }; // %%% Make dependent on an option?: new HtmlFlavorHandler(), return dataFlavorHandlerList; } public MindMapNodeModel pasteXMLWithoutRedisplay(String pasted, MindMapNode target, boolean asSibling, boolean changeSide, boolean isLeft, HashMap pIDToTarget) throws XMLParseException { // Call nodeStructureChanged(target) after this function. logger.fine("Pasting " + pasted + " to " + target); try { MindMapNodeModel node = (MindMapNodeModel) mMindMapController .createNodeTreeFromXml(new StringReader(pasted), pIDToTarget); insertNodeInto(node, target, asSibling, isLeft, changeSide); mMindMapController.invokeHooksRecursively(node, mMindMapController.getModel()); mMindMapController.processUnfinishedLinksInHooks(node); return node; } catch (IOException ee) { freemind.main.Resources.getInstance().logException(ee); return null; } } private void insertNodeInto(MindMapNodeModel node, MindMapNode target, boolean asSibling, boolean isLeft, boolean changeSide) { MindMapNode parent; if (asSibling) { parent = target.getParentNode(); } else { parent = target; } if (changeSide) { node.setParent(parent); node.setLeft(isLeft); } // now, the import is finished. We can inform others about the new // nodes: if (asSibling) { insertNodeInto(node, parent, parent.getChildPosition(target)); } else { insertNodeInto(node, target); } } static final Pattern nonLinkCharacter = Pattern.compile("[ \n()'\",;]"); /** * Paste String (as opposed to other flavours) * * Split the text into lines; determine the new tree structure by the number * of leading spaces in lines. In case that trimmed line starts with * protocol (http:, https:, ftp:), create a link with the same content. * * If there was only one line to be pasted, return the pasted node, null * otherwise. * * @param isLeft * TODO */ private MindMapNode pasteStringWithoutRedisplay(Transferable t, MindMapNode parent, boolean asSibling, boolean isLeft) throws UnsupportedFlavorException, IOException { String textFromClipboard = (String) t .getTransferData(DataFlavor.stringFlavor); Pattern mailPattern = Pattern.compile("([^@ <>\\*']+@[^@ <>\\*']+)"); String[] textLines = textFromClipboard.split("\n"); if (textLines.length > 1) { mMindMapController.getFrame().setWaitingCursor(true); } MindMapNode realParent = null; if (asSibling) { // When pasting as sibling, we use virtual node as parent. When the // pasting to // virtual node is completed, we insert the children of that virtual // node to // the parent of real parent. realParent = parent; parent = new MindMapNodeModel(mMindMapController.getFrame(), mMindMapController.getMap()); } ArrayList parentNodes = new ArrayList(); ArrayList parentNodesDepths = new ArrayList(); parentNodes.add(parent); parentNodesDepths.add(new Integer(-1)); String[] linkPrefixes = { "http://", "ftp://", "https://" }; MindMapNode pastedNode = null; 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; } String visibleText = text.trim(); // If the text is a recognizable link (e.g. // http://www.google.com/index.html), // make it more readable by look nicer by cutting off obvious prefix // and other // transforamtions. if (visibleText.matches("^http://(www\\.)?[^ ]*$")) { visibleText = visibleText.replaceAll("^http://(www\\.)?", "") .replaceAll("(/|\\.[^\\./\\?]*)$", "") .replaceAll("((\\.[^\\./]*\\?)|\\?)[^/]*$", " ? ...") .replaceAll("_|%20", " "); String[] textParts = visibleText.split("/"); visibleText = ""; for (int textPartIdx = 0; textPartIdx < textParts.length; textPartIdx++) { if (textPartIdx > 0) { visibleText += " > "; } visibleText += textPartIdx == 0 ? textParts[textPartIdx] : Tools.firstLetterCapitalized(textParts[textPartIdx] .replaceAll("^~*", "")); } } MindMapNode node = mMindMapController.newNode(visibleText, parent.getMap()); if (textLines.length == 1) { pastedNode = node; } // Heuristically determine, if there is a mail. Matcher mailMatcher = mailPattern.matcher(visibleText); if (mailMatcher.find()) { node.setLink("mailto:" + mailMatcher.group()); } // Heuristically determine, if there is a link. Because this is // heuristic, it is probable that it can be improved to include // some matches or exclude some matches. for (int j = 0; j < linkPrefixes.length; j++) { int linkStart = text.indexOf(linkPrefixes[j]); if (linkStart != -1) { int linkEnd = linkStart; while (linkEnd < text.length() && !nonLinkCharacter.matcher( text.substring(linkEnd, linkEnd + 1)) .matches()) { linkEnd++; } node.setLink(text.substring(linkStart, linkEnd)); } } // Determine parent among candidate parents // Change the array of candidate parents accordingly for (int j = parentNodes.size() - 1; j >= 0; --j) { if (depth > ((Integer) parentNodesDepths.get(j)).intValue()) { for (int k = j + 1; k < parentNodes.size(); ++k) { MindMapNode n = (MindMapNode) parentNodes.get(k); if (n.getParentNode() == parent) { // addUndoAction(n); } parentNodes.remove(k); parentNodesDepths.remove(k); } MindMapNode target = (MindMapNode) parentNodes.get(j); node.setLeft(isLeft); insertNodeInto(node, target); parentNodes.add(node); parentNodesDepths.add(new Integer(depth)); break; } } } for (int k = 0; k < parentNodes.size(); ++k) { MindMapNode n = (MindMapNode) parentNodes.get(k); if (n.getParentNode() == parent) { // addUndoAction(n); } } return pastedNode; } /** */ private void insertNodeInto(MindMapNodeModel node, MindMapNode parent, int i) { mMindMapController.insertNodeInto(node, parent, i); } private void insertNodeInto(MindMapNode node, MindMapNode parent) { mMindMapController.insertNodeInto(node, parent); } private TransferableContent getTransferableContent(Transferable t, UndoPasteNodeAction pUndoAction) { boolean amountAlreadySet = false; try { TransferableContent trans = new TransferableContent(); if (t.isDataFlavorSupported(MindMapNodesSelection.mindMapNodesFlavor)) { String textFromClipboard; textFromClipboard = (String) t .getTransferData(MindMapNodesSelection.mindMapNodesFlavor); trans.setTransferable(HtmlTools.makeValidXml(textFromClipboard)); if (pUndoAction != null && !amountAlreadySet) { pUndoAction .setNodeAmount(Tools.countOccurrences( textFromClipboard, ControllerAdapter.NODESEPARATOR) + 1); amountAlreadySet = true; } } if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { String textFromClipboard; textFromClipboard = (String) t .getTransferData(DataFlavor.stringFlavor); trans.setTransferableAsPlainText(HtmlTools .makeValidXml(textFromClipboard)); if (pUndoAction != null && !amountAlreadySet) { // determine amount of new nodes using the algorithm: final int childCount = determineAmountOfNewNodes(t); pUndoAction.setNodeAmount(childCount); amountAlreadySet = true; } } if (t.isDataFlavorSupported(MindMapNodesSelection.rtfFlavor)) { // byte[] textFromClipboard = (byte[]) // t.getTransferData(MindMapNodesSelection.rtfFlavor); // trans.setTransferableAsRTF(textFromClipboard.toString()); } if (t.isDataFlavorSupported(MindMapNodesSelection.htmlFlavor)) { String textFromClipboard; textFromClipboard = (String) t .getTransferData(MindMapNodesSelection.htmlFlavor); trans.setTransferableAsHtml(HtmlTools .makeValidXml(textFromClipboard)); if (pUndoAction != null && !amountAlreadySet) { // on html paste, the string text is taken and "improved". // Thus, we count its lines. final int childCount; try { childCount = determineAmountOfNewNodes(t); pUndoAction.setNodeAmount(childCount); } catch (Exception e) { freemind.main.Resources.getInstance().logException(e); // ok, something went wrong, but this breaks undo, only. pUndoAction.setNodeAmount(1); } amountAlreadySet = true; } } if (t.isDataFlavorSupported(MindMapNodesSelection.fileListFlavor)) { /* * Since the JAXB-generated interface TransferableContent * doesn't supply a setTranserableAsFileList method, we have to * get the fileList, clear it, and then set it to the new value. */ List fileList = (List) t .getTransferData(MindMapNodesSelection.fileListFlavor); for (Iterator iter = fileList.iterator(); iter.hasNext();) { File fileName = (File) iter.next(); TransferableFile transferableFile = new TransferableFile(); transferableFile.setFileName(fileName.getAbsolutePath()); trans.addTransferableFile(transferableFile); } if (pUndoAction != null && !amountAlreadySet) { pUndoAction.setNodeAmount(fileList.size()); amountAlreadySet = true; } } if (t.isDataFlavorSupported(DataFlavor.imageFlavor)) { logger.info("image..."); try { // Get data from clipboard and assign it to an image. // clipboard.getData() returns an object, so we need to cast // it to a BufferdImage. BufferedImage image = (BufferedImage) t .getTransferData(DataFlavor.imageFlavor); TransferableImage timg = new TransferableImage(); File mindmapFile = mMindMapController.getMap().getFile(); if (mindmapFile == null) { JOptionPane.showMessageDialog( mMindMapController.getView(), mMindMapController.getText("map_not_saved"), "FreeMind", JOptionPane.ERROR_MESSAGE); return null; } File tempFile = File .createTempFile( mindmapFile .getName() .replace( FreeMindCommon.FREEMIND_FILE_EXTENSION, "_"), ".jpeg", mindmapFile .getParentFile()); String imgfilepath = tempFile.getName(); timg.setImage(imgfilepath); trans.addTransferableImage(timg); // class to write image to disk. You specify the image to be // saved, its type, // and then the file in which to write the image data. logger.info("Starting to write clipboard image " + image + " to " + tempFile); ImageIO.write(image, "jpg", tempFile); trans.setTransferableAsImage(imgfilepath); if (pUndoAction != null && !amountAlreadySet) { pUndoAction.setNodeAmount(1); amountAlreadySet = true; } } // getData throws this. catch (UnsupportedFlavorException ufe) { freemind.main.Resources.getInstance().logException(ufe); } catch (IOException ioe) { freemind.main.Resources.getInstance().logException(ioe); } } return trans; } catch (UnsupportedFlavorException e) { freemind.main.Resources.getInstance().logException(e); } catch (IOException e) { freemind.main.Resources.getInstance().logException(e); } return null; } /* * TODO: This is a bit dirty here. Better would be to separate the algorithm * from the node creation and use the pure algo. */ protected int determineAmountOfNewNodes(Transferable t) throws UnsupportedFlavorException, IOException { // create a new node for testing purposes. MindMapNodeModel parent = new MindMapNodeModel( mMindMapController.getFrame(), mMindMapController.getMap()); pasteStringWithoutRedisplay(t, parent, false, false); final int childCount = parent.getChildCount(); return childCount; } private Transferable getTransferable(TransferableContent trans) { // create Transferable: // Add file list to this selection. Vector fileList = new Vector(); for (Iterator iter = trans.getListTransferableFileList().iterator(); iter .hasNext();) { TransferableFile tFile = (TransferableFile) iter.next(); fileList.add(new File(tFile.getFileName())); } Transferable copy = new MindMapNodesSelection(trans.getTransferable(), trans.getTransferableAsImage(), trans.getTransferableAsPlainText(), trans.getTransferableAsRTF(), trans.getTransferableAsHtml(), trans.getTransferableAsDrop(), fileList, null); return copy; } }