/* * 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.link.mindmapmode; import java.awt.Color; import java.awt.Component; import java.awt.EventQueue; import java.awt.GraphicsEnvironment; import java.awt.Point; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Set; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListCellRenderer; import javax.swing.FocusManager; import javax.swing.Icon; import javax.swing.InputMap; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; import org.freeplane.core.resources.ResourceController; import org.freeplane.core.ui.AFreeplaneAction; import org.freeplane.core.ui.components.UITools; import org.freeplane.core.undo.IActor; import org.freeplane.core.util.TextUtils; import org.freeplane.features.link.ArrowType; import org.freeplane.features.link.ConnectorModel; import org.freeplane.features.link.HyperTextLinkModel; import org.freeplane.features.link.LinkController; import org.freeplane.features.link.LinkModel; import org.freeplane.features.link.MapLinks; import org.freeplane.features.link.NodeLinkModel; import org.freeplane.features.link.NodeLinks; import org.freeplane.features.link.ConnectorModel.Shape; import org.freeplane.features.map.IExtensionCopier; import org.freeplane.features.map.IMapChangeListener; import org.freeplane.features.map.IMapSelection; import org.freeplane.features.map.MapChangeEvent; import org.freeplane.features.map.MapModel; import org.freeplane.features.map.NodeModel; import org.freeplane.features.map.mindmapmode.DocuMapAttribute; import org.freeplane.features.mode.Controller; import org.freeplane.features.mode.ModeController; import org.freeplane.features.mode.mindmapmode.MModeController; import org.freeplane.features.spellchecker.mindmapmode.SpellCheckerController; import org.freeplane.features.styles.LogicalStyleKeys; import org.freeplane.features.url.UrlManager; /** * @author Dimitry Polivaev */ public class MLinkController extends LinkController { private static class StyleCopier implements IExtensionCopier { public void copy(Object key, NodeModel from, NodeModel to) { if (!key.equals(LogicalStyleKeys.NODE_STYLE)) { return; } copy(from, to); } public void copy(NodeModel from, NodeModel to) { final Boolean formatNodeAsHyperlink = NodeLinks.formatNodeAsHyperlink(from); if(formatNodeAsHyperlink != null) NodeLinks.createLinkExtension(to).setFormatNodeAsHyperlink(formatNodeAsHyperlink); } public void remove(Object key, NodeModel from) { if (!key.equals(LogicalStyleKeys.NODE_STYLE)) { return; } final NodeLinks model = NodeLinks.getModel(from); if(model != null) model.setFormatNodeAsHyperlink(null); } public void remove(Object key, NodeModel from, NodeModel which) { if(NodeLinks.formatNodeAsHyperlink(which) != null) remove(key, from); } public void resolveParentExtensions(Object key, NodeModel to) { } } private final class CreateArrowLinkActor implements IActor { private final String targetID; private final NodeModel source; private ConnectorModel arrowLink; public ConnectorModel getArrowLink() { return arrowLink; } private CreateArrowLinkActor(final String targetID, final NodeModel source) { this.targetID = targetID; this.source = source; } public void act() { NodeLinks nodeLinks = (NodeLinks) source.getExtension(NodeLinks.class); if (nodeLinks == null) { nodeLinks = new NodeLinks(); source.addExtension(nodeLinks); } arrowLink = new ConnectorModel(source, targetID, getStandardConnectorColor(), getStandardConnectorAlpha(), getStandardConnectorShape(), getStandardConnectorWidth(), getStandardLabelFontFamily(), getStandardLabelFontSize()); nodeLinks.addArrowlink(arrowLink); Controller.getCurrentModeController().getMapController().nodeChanged(source); } public String getDescription() { return "addLink"; } public void undo() { final NodeLinks nodeLinks = (NodeLinks) source.getExtension(NodeLinks.class); nodeLinks.removeArrowlink(arrowLink); Controller.getCurrentModeController().getMapController().nodeChanged(source); } } private final class TargetLabelSetter implements IActor { private final String oldLabel; private final String label; private final ConnectorModel model; private TargetLabelSetter(final String oldLabel, final String label, final ConnectorModel model) { this.oldLabel = oldLabel; this.label = label; this.model = model; } public void act() { model.setTargetLabel(label); Controller.getCurrentModeController().getMapController().nodeChanged(model.getSource()); } public String getDescription() { return "setTargetLabel"; } public void undo() { model.setTargetLabel(oldLabel); Controller.getCurrentModeController().getMapController().nodeChanged(model.getSource()); } } private final class SourceLabelSetter implements IActor { private final ConnectorModel model; private final String label; private final String oldLabel; private SourceLabelSetter(final ConnectorModel model, final String label, final String oldLabel) { this.model = model; this.label = label; this.oldLabel = oldLabel; } public void act() { model.setSourceLabel(label); Controller.getCurrentModeController().getMapController().nodeChanged(model.getSource()); } public String getDescription() { return "setSourceLabel"; } public void undo() { model.setSourceLabel(oldLabel); Controller.getCurrentModeController().getMapController().nodeChanged(model.getSource()); } } private final class MiddleLabelSetter implements IActor { private final ConnectorModel model; private final String oldLabel; private final String label; private MiddleLabelSetter(final ConnectorModel model, final String oldLabel, final String label) { this.model = model; this.oldLabel = oldLabel; this.label = label; } public void act() { model.setMiddleLabel(label); Controller.getCurrentModeController().getMapController().nodeChanged(model.getSource()); } public String getDescription() { return "setMiddleLabel"; } public void undo() { model.setMiddleLabel(oldLabel); Controller.getCurrentModeController().getMapController().nodeChanged(model.getSource()); } } /** * @author Dimitry Polivaev */ private final class NodeDeletionListener implements IMapChangeListener { public void mapChanged(final MapChangeEvent event) { } public void onNodeDeleted(final NodeModel parent, final NodeModel child, final int index) { } public void onNodeInserted(final NodeModel parent, final NodeModel child, final int newIndex) { if (((MModeController) Controller.getCurrentModeController()).isUndoAction()) { return; } EventQueue.invokeLater(new Runnable() { public void run() { onChange(child, false); } }); } public void onNodeMoved(final NodeModel oldParent, final int oldIndex, final NodeModel newParent, final NodeModel child, final int newIndex) { } public void onPreNodeDelete(final NodeModel oldParent, final NodeModel model, final int oldIndex) { onChange(model, true); } private void onChange(final NodeModel model, final boolean delete) { if (((MModeController) Controller.getCurrentModeController()).isUndoAction()) { return; } final MapModel map = model.getMap(); final MapLinks links = (MapLinks) map.getExtension(MapLinks.class); if (links == null) { return; } removeLinksForDeletedSource(links, model, delete); removeLinksForDeletedTarget(links, model); } private void removeLinksForDeletedSource(final MapLinks links, final NodeModel model, final boolean delete) { final List<NodeModel> children = model.getChildren(); for (final NodeModel child : children) { removeLinksForDeletedSource(links, child, delete); } final NodeLinks nodeLinks = NodeLinks.getLinkExtension(model); if (nodeLinks == null) { return; } for (final LinkModel link : nodeLinks.getLinks()) { if (!(link instanceof NodeLinkModel)) { continue; } final IActor actor = new IActor() { public void act() { if (delete) { delete(); } else { insert(); } } public void undo() { if (delete) { insert(); } else { delete(); } } private void delete() { links.remove(link); } public String getDescription() { return null; } private void insert() { links.add(link); } }; final MapModel map = model.getMap(); Controller.getCurrentModeController().execute(actor, map); } } private void removeLinksForDeletedTarget(final MapLinks links, final NodeModel model) { final List<NodeModel> children = model.getChildren(); for (final NodeModel child : children) { removeLinksForDeletedTarget(links, child); } final String id = model.getID(); if (id == null) { return; } final Set<LinkModel> linkModels = links.get(id); if (linkModels == null || linkModels.isEmpty()) { return; } final IActor actor = new IActor() { public void act() { refresh(); } public void undo() { refresh(); } private void refresh() { for (final LinkModel link : linkModels) { if (link instanceof HyperTextLinkModel) { final NodeModel source = ((HyperTextLinkModel) link).getSource(); Controller.getCurrentModeController().getMapController().delayedNodeRefresh(source, NodeModel.NODE_ICON, null, null); } } } public String getDescription() { return null; } }; final MapModel map = model.getMap(); Controller.getCurrentModeController().execute(actor, map); } public void onPreNodeMoved(final NodeModel oldParent, final int oldIndex, final NodeModel newParent, final NodeModel child, final int newIndex) { } } static private SetLinkByFileChooserAction setLinkByFileChooser; static private SetLinkByTextFieldAction setLinkByTextField; private String anchorID; private ModeController modeController; public MLinkController() { super(); modeController = Controller.getCurrentModeController(); createActions(); anchorID = null; modeController.registerExtensionCopier(new StyleCopier()); (modeController.getMapController()).addMapChangeListener(new NodeDeletionListener()); } public ConnectorModel addConnector(final NodeModel source, final NodeModel target) { return addConnector(source, target.createID()); } public void changeArrowsOfArrowLink(final ConnectorModel link, final ArrowType startArrow, final ArrowType endArrow) { final IActor actor = new IActor() { final private ArrowType oldEndArrow = link.getEndArrow(); final private ArrowType oldStartArrow = link.getStartArrow(); public void act() { link.setStartArrow(startArrow); link.setEndArrow(endArrow); Controller.getCurrentModeController().getMapController().nodeChanged(link.getSource()); } public String getDescription() { return "changeArrowsOfArrowLink"; } public void undo() { link.setStartArrow(oldStartArrow); link.setEndArrow(oldEndArrow); Controller.getCurrentModeController().getMapController().nodeChanged(link.getSource()); } }; Controller.getCurrentModeController().execute(actor, link.getSource().getMap()); } /** * */ private void createActions() { final ModeController modeController = Controller.getCurrentModeController(); setLinkByFileChooser = new SetLinkByFileChooserAction(); modeController.addAction(setLinkByFileChooser); final AddConnectorAction addArrowLinkAction = new AddConnectorAction(); modeController.addAction(addArrowLinkAction); setLinkByTextField = new SetLinkByTextFieldAction(); modeController.addAction(setLinkByTextField); modeController.addAction(new AddLocalLinkAction()); modeController.addAction(new AddMenuItemLinkAction()); modeController.addAction(new ExtractLinkFromTextAction()); modeController.addAction(new SetLinkAnchorAction()); // new by Nnamdi Kohn modeController.addAction(new MakeLinkToAnchorAction()); // new by Nnamdi Kohn modeController.addAction(new MakeLinkFromAnchorAction()); // new by Nnamdi Kohn modeController.addAction(new ClearLinkAnchorAction()); // new by Nnamdi Kohn } @Override protected void createArrowLinkPopup(final ConnectorModel link, final JComponent arrowLinkPopup) { super.createArrowLinkPopup(link, arrowLinkPopup); addAction(arrowLinkPopup, new RemoveConnectorAction(this, link)); addSeparator(arrowLinkPopup); addAction(arrowLinkPopup, new ConnectorColorAction(this, link)); final JSlider transparencySlider = new JSlider(20, 255, link.getAlpha()); transparencySlider.setMinorTickSpacing(20); transparencySlider.setPaintTicks(true); transparencySlider.setSnapToTicks(true); transparencySlider.setPaintTrack(true); addPopupComponent(arrowLinkPopup, TextUtils.getText("edit_transparency_label"), transparencySlider); addSeparator(arrowLinkPopup); AFreeplaneAction[] arrowActions = new AFreeplaneAction[]{ new ChangeConnectorArrowsAction(this, "none", link, ArrowType.NONE, ArrowType.NONE), new ChangeConnectorArrowsAction(this, "forward", link, ArrowType.NONE, ArrowType.DEFAULT), new ChangeConnectorArrowsAction(this, "backward", link, ArrowType.DEFAULT, ArrowType.NONE), new ChangeConnectorArrowsAction(this, "both", link, ArrowType.DEFAULT, ArrowType.DEFAULT) }; final JComboBox connectorArrows = createActionBox(arrowActions); addPopupComponent(arrowLinkPopup, TextUtils.getText("connector_arrows"), connectorArrows); final boolean twoNodesConnector = ! link.getSource().equals(link.getTarget()); if(twoNodesConnector){ AFreeplaneAction[] shapeActions = new AFreeplaneAction[] { new ChangeConnectorShapeAction(this, link, Shape.CUBIC_CURVE), new ChangeConnectorShapeAction(this, link, Shape.LINE), new ChangeConnectorShapeAction(this, link, Shape.LINEAR_PATH), new ChangeConnectorShapeAction(this, link, Shape.EDGE_LIKE) }; final JComboBox connectorShapes = createActionBox(shapeActions); addPopupComponent(arrowLinkPopup, TextUtils.getText("connector_shapes"), connectorShapes); } AFreeplaneAction[] dashActions = new AFreeplaneAction[] { new ChangeConnectorDashAction(this, link, null), new ChangeConnectorDashAction(this, link, new int[]{3, 3}), new ChangeConnectorDashAction(this, link, new int[]{7, 7}), new ChangeConnectorDashAction(this, link, new int[]{2, 7}), new ChangeConnectorDashAction(this, link, new int[]{2, 7, 7, 7}) }; final JComboBox connectorDashes = createActionBox(dashActions); addPopupComponent(arrowLinkPopup, TextUtils.getText("connector_lines"), connectorDashes); final SpinnerNumberModel widthModel = new SpinnerNumberModel(link.getWidth(),1, 32, 1); final JSpinner widthSpinner = new JSpinner(widthModel); addPopupComponent(arrowLinkPopup, TextUtils.getText("edit_width_label"), widthSpinner); addSeparator(arrowLinkPopup); { final GraphicsEnvironment gEnv = GraphicsEnvironment.getLocalGraphicsEnvironment(); final String[] envFonts = gEnv.getAvailableFontFamilyNames(); DefaultComboBoxModel fonts = new DefaultComboBoxModel(envFonts); fonts.setSelectedItem(link.getLabelFontFamily()); JComboBox fontBox = new JComboBox(fonts); fontBox.setEditable(false); addPopupComponent(arrowLinkPopup, TextUtils.getText("edit_label_font_family"), fontBox); fontBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { final Object item = e.getItem(); if(item != null) setLabelFontFamily(link, item.toString()); } }); } { final Integer[] sizes = {4, 6, 8, 10, 12, 14, 16, 18, 24, 36}; DefaultComboBoxModel sizesModel = new DefaultComboBoxModel(sizes); sizesModel.setSelectedItem(link.getLabelFontSize()); JComboBox sizesBox = new JComboBox(sizesModel); sizesBox.setEditable(true); addPopupComponent(arrowLinkPopup, TextUtils.getText("edit_label_font_size"), sizesBox); sizesBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if(arrowLinkPopup.isVisible()) arrowLinkPopup.setVisible(false); final Object item = e.getItem(); if(item != null){ final int size; if(item instanceof Integer) size = (Integer)item; else{ try{ size = Integer.valueOf(item.toString()); if(size <=0) return; } catch (NumberFormatException ex){ return; } } setLabelFontSize(link, size); } } }); } final JTextArea sourceLabelEditor; if(twoNodesConnector){ sourceLabelEditor = new JTextArea(link.getSourceLabel()); addTextEditor(arrowLinkPopup, "edit_source_label", sourceLabelEditor); } else sourceLabelEditor = null; final JTextArea middleLabelEditor = new JTextArea(link.getMiddleLabel()); addTextEditor(arrowLinkPopup, twoNodesConnector ? "edit_middle_label" : "edit_end_label", middleLabelEditor); final JTextArea targetLabelEditor ; if(twoNodesConnector){ targetLabelEditor = new JTextArea(link.getTargetLabel()); } else{ targetLabelEditor = new JTextArea(link.getSourceLabel()); } addTextEditor(arrowLinkPopup, "edit_target_label", targetLabelEditor); arrowLinkPopup.addHierarchyListener(new HierarchyListener() { private Component focusOwner; private Window dialog; public void hierarchyChanged(HierarchyEvent e) { final JComponent component = (JComponent) e.getComponent(); if(component.isShowing()){ if(dialog == null){ dialog = SwingUtilities.getWindowAncestor(component); dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { component.putClientProperty(CANCEL, Boolean.TRUE); } }); } if(focusOwner == null) focusOwner = FocusManager.getCurrentManager().getFocusOwner(); return; } if(focusOwner == null || ! focusOwner.isShowing()) return; focusOwner.requestFocus(); if (Boolean.TRUE.equals(component.getClientProperty(CANCEL))) { return; } final IMapSelection selection = Controller.getCurrentController().getSelection(); if (selection == null || selection.getSelected() == null) return; if(twoNodesConnector){ setSourceLabel(link, sourceLabelEditor.getText()); setTargetLabel(link, targetLabelEditor.getText()); } else{ setSourceLabel(link, targetLabelEditor.getText()); } setMiddleLabel(link, middleLabelEditor.getText()); setAlpha(link, transparencySlider.getValue()); setWidth(link, widthModel.getNumber().intValue()); } }); } @SuppressWarnings("serial") protected JComboBox createActionBox(AFreeplaneAction[] items) { final JComboBox box = new JComboBox(); box.setEditable(false); box.setModel(new DefaultComboBoxModel(items)); for(AFreeplaneAction item : items){ if(item.isSelected()){ box.setSelectedItem(item); break; } } box.setRenderer(new DefaultListCellRenderer() { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Action action = (Action) value; Icon icon = (Icon)action.getValue(Action.SMALL_ICON); String text = (String)action.getValue(Action.NAME); Object renderedValue = text == null ? icon : text; DefaultListCellRenderer renderer = (DefaultListCellRenderer) super.getListCellRendererComponent(list, renderedValue, index, isSelected, cellHasFocus); if(text != null && icon != null) renderer.setIcon(icon); return renderer; } }); box.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { AFreeplaneAction item = (AFreeplaneAction)e.getItem(); final JComboBox box = (JComboBox) e.getSource(); item.actionPerformed(new ActionEvent(box, ActionEvent.ACTION_PERFORMED, null)); SwingUtilities.getWindowAncestor(box).setVisible(false); } }); return box; } private void addSeparator(JComponent arrowLinkPopup) { } private void addTextEditor(final JComponent popup, final String label, final JTextArea editor) { final InputMap inputMap = editor.getInputMap(); final ActionMap actionMap = editor.getActionMap(); final boolean enterConfirms = ResourceController.getResourceController().getBooleanProperty("el__enter_confirms_by_default"); final KeyStroke close = KeyStroke.getKeyStroke(enterConfirms ? "ENTER" : "alt ENTER"); inputMap.put(close, CLOSE); actionMap.put(CLOSE, new ClosePopupAction(CLOSE)); final KeyStroke enter = KeyStroke.getKeyStroke(! enterConfirms ? "ENTER" : "alt ENTER"); final KeyStroke enter2 = KeyStroke.getKeyStroke("shift ENTER"); inputMap.put(enter, "INSERT_EOL"); inputMap.put(enter2, "INSERT_EOL"); actionMap.put("INSERT_EOL", new UITools.InsertEolAction()); editor.setRows(5); editor.setColumns(30); final JPopupMenu popupMenu = new JPopupMenu(); SpellCheckerController spellCheckerController = SpellCheckerController.getController(); spellCheckerController.addSpellCheckerMenu(popupMenu ); spellCheckerController.enableAutoSpell(editor, true); editor.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { handlePopup(e); } @Override public void mouseReleased(MouseEvent e) { handlePopup(e); } private void handlePopup(MouseEvent e) { if(e.isPopupTrigger()){ e.consume(); popupMenu.show(e.getComponent(), e.getX(), e.getY()); } } }); final JScrollPane scrollPane = new JScrollPane(editor, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); addPopupComponent(popup, TextUtils.getText(label), scrollPane); } public void setConnectorColor(final ConnectorModel arrowLink, final Color color) { final Color oldColor = arrowLink.getColor(); if (color == oldColor || color != null && color.equals(oldColor)) { return; } final IActor actor = new IActor() { public void act() { arrowLink.setColor(color); final NodeModel node = arrowLink.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } public String getDescription() { return "setConnectorColor"; } public void undo() { arrowLink.setColor(oldColor); final NodeModel node = arrowLink.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } }; Controller.getCurrentModeController().execute(actor, arrowLink.getSource().getMap()); } public void setConnectorDash(final ConnectorModel arrowLink, final int[] dash) { final int[] oldDash = arrowLink.getDash(); if (dash == oldDash || dash != null && dash.equals(oldDash)) { return; } final IActor actor = new IActor() { public void act() { arrowLink.setDash(dash); final NodeModel node = arrowLink.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } public String getDescription() { return "setConnectorDash"; } public void undo() { arrowLink.setDash(oldDash); final NodeModel node = arrowLink.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } }; Controller.getCurrentModeController().execute(actor, arrowLink.getSource().getMap()); } public void setArrowLinkEndPoints(final ConnectorModel link, final Point startPoint, final Point endPoint) { final IActor actor = new IActor() { final private Point oldEndPoint = link.getEndInclination(); final private Point oldStartPoint = link.getStartInclination(); public void act() { link.setStartInclination(startPoint); link.setEndInclination(endPoint); Controller.getCurrentModeController().getMapController().nodeChanged(link.getSource()); } public String getDescription() { return "setArrowLinkEndPoints"; } public void undo() { link.setStartInclination(oldStartPoint); link.setEndInclination(oldEndPoint); Controller.getCurrentModeController().getMapController().nodeChanged(link.getSource()); } }; Controller.getCurrentModeController().execute(actor, link.getSource().getMap()); } public void setLink(final NodeModel node, final String link, final int linkType) { if (link != null && !"".equals(link)) { try { final URI uri = new URI(link); setLink(node, uri, linkType); } catch (final URISyntaxException e) { e.printStackTrace(); } return; } setLink(node, (URI) null, LINK_ABSOLUTE); } private URI relativeLink(final URI argUri, final NodeModel node, final int linkType) { if (linkType != LINK_ABSOLUTE && "file".equals(argUri.getScheme())) { try { final File mapFile = node.getMap().getFile(); return LinkController.toRelativeURI(mapFile, new File(argUri), linkType); } catch (Exception e) { } } return argUri; } public void setLinkTypeDependantLink(final NodeModel node, final URI argUri) { setLink(node, argUri, getLinkType()); } public void setLinkTypeDependantLink(final NodeModel node, final File file) { setLink(node, file.toURI(), getLinkType()); } public void setLinkTypeDependantLink(final NodeModel node, final String link) { setLink(node, link, getLinkType()); } public void setLink(final NodeModel node, final URI argUri, final int linkType) { final URI uri = relativeLink(argUri, node, linkType); final IActor actor = new IActor() { private URI oldlink; private String oldTargetID; public void act() { NodeLinks links = NodeLinks.getLinkExtension(node); if (links != null) { oldlink = links.getHyperLink(); oldTargetID = links.removeLocalHyperLink(node); } else { links = NodeLinks.createLinkExtension(node); } if (uri != null && uri.toString().startsWith("#")) { links.setLocalHyperlink(node, uri.toString().substring(1)); } //FIXME: DOCEAR - replace old nodeChanged event and use new LinkChanged property URI oldHyperLink = links.getHyperLink(); links.setHyperLink(uri); //Controller.getCurrentModeController().getMapController().nodeChanged(node); Controller.getCurrentModeController().getMapController().nodeChanged(node, NodeModel.HYPERLINK_CHANGED, oldHyperLink, uri); } public String getDescription() { return "setLink"; } public void undo() { final NodeLinks links = NodeLinks.getLinkExtension(node); links.setLocalHyperlink(node, oldTargetID); links.setHyperLink(oldlink); Controller.getCurrentModeController().getMapController().nodeChanged(node); } }; Controller.getCurrentModeController().execute(actor, node.getMap()); } public void setLinkByFileChooser() { setLinkByFileChooser.setLinkByFileChooser(); } public void setMiddleLabel(final ConnectorModel model, String label) { if ("".equals(label)) { label = null; } String oldLabel = model.getMiddleLabel(); if ("".equals(oldLabel)) { oldLabel = null; } if (label == oldLabel || label != null && label.equals(oldLabel)) { return; } final IActor actor = new MiddleLabelSetter(model, oldLabel, label); Controller.getCurrentModeController().execute(actor, model.getSource().getMap()); } public void setSourceLabel(final ConnectorModel model, String label) { if ("".equals(label)) { label = null; } String oldLabel = model.getSourceLabel(); if ("".equals(oldLabel)) { oldLabel = null; } if (label == oldLabel || label != null && label.equals(oldLabel)) { return; } final IActor actor = new SourceLabelSetter(model, label, oldLabel); Controller.getCurrentModeController().execute(actor, model.getSource().getMap()); } public void setTargetLabel(final ConnectorModel model, String label) { if ("".equals(label)) { label = null; } String oldLabel = model.getTargetLabel(); if ("".equals(oldLabel)) { oldLabel = null; } if (label == oldLabel || label != null && label.equals(oldLabel)) { return; } final IActor actor = new TargetLabelSetter(oldLabel, label, model); Controller.getCurrentModeController().execute(actor, model.getSource().getMap()); } public ConnectorModel addConnector(final NodeModel source, final String targetID) { final CreateArrowLinkActor actor = new CreateArrowLinkActor(targetID, source); Controller.getCurrentModeController().execute(actor, source.getMap()); return actor.getArrowLink(); } public void removeArrowLink(final NodeLinkModel arrowLink) { final IActor actor = new IActor() { public void act() { final NodeModel source = arrowLink.getSource(); final NodeLinks nodeLinks = (NodeLinks) source.getExtension(NodeLinks.class); nodeLinks.removeArrowlink(arrowLink); Controller.getCurrentModeController().getMapController().nodeChanged(source); } public String getDescription() { return "removeArrowLink"; } public void undo() { final NodeModel source = arrowLink.getSource(); NodeLinks nodeLinks = (NodeLinks) source.getExtension(NodeLinks.class); if (nodeLinks == null) { nodeLinks = new NodeLinks(); source.addExtension(nodeLinks); } nodeLinks.addArrowlink(arrowLink); Controller.getCurrentModeController().getMapController().nodeChanged(source); } }; Controller.getCurrentModeController().execute(actor, arrowLink.getSource().getMap()); } public void setShape(final ConnectorModel connector, final Shape shape) { final Shape oldShape = connector.getShape(); if (oldShape.equals(shape)) { return; } final IActor actor = new IActor() { public void act() { connector.setShape(shape); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } public String getDescription() { return "setConnectorShape"; } public void undo() { connector.setShape(oldShape); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } }; Controller.getCurrentModeController().execute(actor, connector.getSource().getMap()); } public void setWidth(final ConnectorModel connector, final int width) { final int oldWidth = connector.getWidth(); if (oldWidth == width) { return; } final IActor actor = new IActor() { public void act() { connector.setWidth(width); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } public String getDescription() { return "setConnectorWidth"; } public void undo() { connector.setWidth(oldWidth); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } }; Controller.getCurrentModeController().execute(actor, connector.getSource().getMap()); } public void setLabelFontSize(final ConnectorModel connector, final int width) { final int oldWidth = connector.getLabelFontSize(); if (oldWidth == width) { return; } final IActor actor = new IActor() { public void act() { connector.setLabelFontSize(width); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } public String getDescription() { return "setConnectorWidth"; } public void undo() { connector.setLabelFontSize(oldWidth); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } }; Controller.getCurrentModeController().execute(actor, connector.getSource().getMap()); } public void setLabelFontFamily(final ConnectorModel connector, final String family) { final String oldFamily = connector.getLabelFontFamily(); if (oldFamily.equals(family)) { return; } final IActor actor = new IActor() { public void act() { connector.setLabelFontFamily(family); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } public String getDescription() { return "setConnectorWidth"; } public void undo() { connector.setLabelFontFamily(oldFamily); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } }; Controller.getCurrentModeController().execute(actor, connector.getSource().getMap()); } public void setAlpha(final ConnectorModel connector, final int alpha) { final int oldAlpha = connector.getAlpha(); if (oldAlpha == alpha) { return; } final IActor actor = new IActor() { public void act() { connector.setAlpha(alpha); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } public String getDescription() { return "setConnectorAlpha"; } public void undo() { connector.setAlpha(oldAlpha); final NodeModel node = connector.getSource(); Controller.getCurrentModeController().getMapController().nodeChanged(node); } }; Controller.getCurrentModeController().execute(actor, connector.getSource().getMap()); } @SuppressWarnings("deprecation") public void loadURI(URI uri) { UrlManager.getController().loadURL(uri); } @Override protected void loadURL(final NodeModel node, final ActionEvent e) { ModeController modeController = Controller.getCurrentModeController(); // load as documentation map if the node belongs to a documentation map boolean addDocuMapAttribute = node.getMap().containsExtension(DocuMapAttribute.class) && ! modeController.containsExtension(DocuMapAttribute.class); if(addDocuMapAttribute){ modeController.addExtension(DocuMapAttribute.class, DocuMapAttribute.instance); } try{ super.loadURL(node, e); } finally{ if(addDocuMapAttribute){ modeController.removeExtension(DocuMapAttribute.class); } } } public String getAnchorID() { return anchorID; } public void setAnchorID(final String anchorID) { this.anchorID = anchorID; final String tooltip; AFreeplaneAction setLinkAnchorAction = modeController.getAction("SetLinkAnchorAction"); final boolean anchored = isAnchored(); if(anchored) tooltip = TextUtils.format(setLinkAnchorAction.getTooltipKey() + "_anchored", anchorID); else tooltip = TextUtils.getRawText(setLinkAnchorAction.getTooltipKey()); setLinkAnchorAction.putValue(Action.SHORT_DESCRIPTION, tooltip); setLinkAnchorAction.putValue(Action.LONG_DESCRIPTION, tooltip); setLinkAnchorAction.setSelected(anchored); modeController.getAction("ClearLinkAnchorAction").setEnabled(anchored); modeController.getAction("MakeLinkToAnchorAction").setEnabled(anchored); modeController.getAction("MakeLinkFromAnchorAction").setEnabled(anchored); } public boolean isAnchored() { return anchorID != null; } public String getAnchorIDforNode(final NodeModel node) { String targetID = getAnchorID(); final String link; // check if anchorID is valid, then set link in current node if (targetID != null && ! targetID.matches("\\w+://")) { // extract fileName from target map final String targetMapFileName = targetID.substring( targetID.indexOf("/") +1, targetID.indexOf("#") ); // get fileName of selected node (source) final File sourceMapFile = node.getMap().getFile(); if(sourceMapFile == null) { UITools.errorMessage(TextUtils.getRawText("map_not_saved")); return null; } // check if target and source reside within same map final String sourceMapFileNameURI = sourceMapFile.toURI().toString(); if( sourceMapFileNameURI.substring(sourceMapFileNameURI.indexOf("/")+1).equals(targetMapFileName) ) { // insert only targetNodeID as link link = targetID.substring(targetID.indexOf("#")); } else { // insert whole targetPath (including targetNodeID) as link for current node link = targetID; } } else{ link = null; } return link; } public void setFormatNodeAsHyperlink(final NodeModel node, final Boolean enabled){ final ModeController modeController = Controller.getCurrentModeController(); final NodeLinks links = NodeLinks.createLinkExtension(node); IActor actor = new IActor() { final Boolean old = links.formatNodeAsHyperlink(); public void act() { links.setFormatNodeAsHyperlink(enabled); modeController.getMapController().nodeChanged(node); } public void undo() { links.setFormatNodeAsHyperlink(old); modeController.getMapController().nodeChanged(node); } public String getDescription() { return "setFormatNodeAsHyperlink"; } }; modeController.execute(actor, node.getMap()); } }