Java tutorial
/* * Copyright 2016 Igor Maznitsa. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.igormaznitsa.sciareto.ui.editors; import static com.igormaznitsa.mindmap.swing.panel.StandardTopicAttribute.ATTR_BORDER_COLOR; import static com.igormaznitsa.mindmap.swing.panel.StandardTopicAttribute.ATTR_FILL_COLOR; import static com.igormaznitsa.mindmap.swing.panel.StandardTopicAttribute.ATTR_TEXT_COLOR; import static com.igormaznitsa.mindmap.swing.panel.StandardTopicAttribute.doesContainOnlyStandardAttributes; import static com.igormaznitsa.sciareto.ui.UiUtils.BUNDLE; import java.awt.Color; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.DataFlavor; import java.awt.dnd.DnDConstants; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.geom.Rectangle2D; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JViewport; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileFilter; import org.apache.commons.io.FileUtils; import com.igormaznitsa.meta.annotation.MustNotContainNull; import com.igormaznitsa.meta.common.utils.Assertions; import com.igormaznitsa.mindmap.model.Extra; import com.igormaznitsa.mindmap.model.ExtraFile; import com.igormaznitsa.mindmap.model.ExtraLink; import com.igormaznitsa.mindmap.model.ExtraNote; import com.igormaznitsa.mindmap.model.ExtraTopic; import com.igormaznitsa.mindmap.model.MMapURI; import com.igormaznitsa.mindmap.model.MindMap; import com.igormaznitsa.mindmap.model.MindMapController; import com.igormaznitsa.mindmap.model.Topic; import com.igormaznitsa.mindmap.model.logger.Logger; import com.igormaznitsa.mindmap.model.logger.LoggerFactory; import com.igormaznitsa.mindmap.plugins.api.CustomJob; import com.igormaznitsa.mindmap.plugins.api.PopUpMenuItemPlugin; import com.igormaznitsa.mindmap.plugins.misc.AboutPlugin; import com.igormaznitsa.mindmap.plugins.misc.OptionsPlugin; import com.igormaznitsa.mindmap.plugins.processors.ExtraFilePlugin; import com.igormaznitsa.mindmap.plugins.processors.ExtraJumpPlugin; import com.igormaznitsa.mindmap.plugins.processors.ExtraNotePlugin; import com.igormaznitsa.mindmap.plugins.processors.ExtraURIPlugin; import com.igormaznitsa.mindmap.plugins.tools.ChangeColorPlugin; import com.igormaznitsa.mindmap.swing.panel.DialogProvider; import com.igormaznitsa.mindmap.swing.panel.MindMapListener; import com.igormaznitsa.mindmap.swing.panel.MindMapPanel; import com.igormaznitsa.mindmap.swing.panel.MindMapPanelConfig; import com.igormaznitsa.mindmap.swing.panel.MindMapPanelController; import com.igormaznitsa.mindmap.swing.panel.ui.AbstractElement; import com.igormaznitsa.mindmap.swing.panel.ui.ElementPart; import com.igormaznitsa.mindmap.swing.panel.utils.Utils; import com.igormaznitsa.sciareto.Context; import com.igormaznitsa.sciareto.Main; import com.igormaznitsa.sciareto.preferences.PreferencesManager; import com.igormaznitsa.sciareto.ui.tree.NodeProject; import com.igormaznitsa.sciareto.ui.misc.AboutPanel; import com.igormaznitsa.sciareto.ui.editors.mmeditors.ColorAttributePanel; import com.igormaznitsa.sciareto.ui.misc.ColorChooserButton; import com.igormaznitsa.sciareto.ui.DialogProviderManager; import com.igormaznitsa.sciareto.ui.editors.mmeditors.FileEditPanel; import com.igormaznitsa.sciareto.ui.editors.mmeditors.MindMapTreePanel; import com.igormaznitsa.sciareto.ui.tabs.TabTitle; import com.igormaznitsa.sciareto.ui.UiUtils; import com.igormaznitsa.sciareto.ui.tree.FileTransferable; public final class MMDEditor extends AbstractScrollPane implements MindMapPanelController, MindMapController, MindMapListener, DropTargetListener { private static final long serialVersionUID = -1011638261448046208L; private static final Logger LOGGER = LoggerFactory.getLogger(MMDEditor.class); private final MindMapPanel mindMapPanel; private final TabTitle title; private static final String FILELINK_ATTR_OPEN_IN_SYSTEM = "useSystem"; //NOI18N private final Context context; private boolean dragAcceptableType; public void refreshConfig() { this.mindMapPanel.refreshConfiguration(); } public static final FileFilter MMD_FILE_FILTER = new FileFilter() { @Override public boolean accept(@Nonnull final File f) { return f.isDirectory() || f.getName().endsWith(".mmd"); } @Override @Nonnull public String getDescription() { return "Mind Map document (*.mmd)"; } }; @Override @Nonnull public FileFilter getFileFilter() { return MMD_FILE_FILTER; } public MMDEditor(@Nonnull final Context context, @Nullable File file) throws IOException { super(); this.context = context; this.title = new TabTitle(context, this, file); this.mindMapPanel = new MindMapPanel(this); this.mindMapPanel.addMindMapListener(this); this.setViewportView(this.mindMapPanel); this.mindMapPanel.setDropTarget(new DropTarget(this.mindMapPanel, this)); final MindMap map; if (file == null) { map = new MindMap(this, true); } else { map = new MindMap(this, new StringReader(FileUtils.readFileToString(file, "UTF-8"))); } this.mindMapPanel.setModel(Assertions.assertNotNull(map)); loadContent(file); } @Override public void loadContent(@Nullable File file) throws IOException { final MindMap map; if (file == null) { map = new MindMap(this, true); } else { map = new MindMap(this, new StringReader(FileUtils.readFileToString(file, "UTF-8"))); } this.mindMapPanel.setModel(Assertions.assertNotNull(map)); this.revalidate(); } @Override public boolean saveDocument() throws IOException { boolean result = false; if (this.title.isChanged()) { File file = this.title.getAssociatedFile(); if (file == null) { file = DialogProviderManager.getInstance().getDialogProvider().msgSaveFileDialog( "mmd-editor-document", "Save Mind Map", null, true, getFileFilter(), "Save"); if (file == null) { return result; } } FileUtils.write(file, this.mindMapPanel.getModel().write(new StringWriter(16384)).toString(), "UTF-8", false); this.title.setChanged(false); result = true; } else { result = true; } return result; } @Override public boolean isUnfoldCollapsedTopicDropTarget(@Nonnull final MindMapPanel source) { return PreferencesManager.getInstance().getPreferences().getBoolean("unfoldCollapsedTarget", true); } @Override public boolean isCopyColorInfoFromParentToNewChildAllowed(@Nonnull final MindMapPanel source) { return PreferencesManager.getInstance().getPreferences().getBoolean("copyColorInfoToNewChildAllowed", true); } @Override public boolean isSelectionAllowed(@Nonnull final MindMapPanel source) { return true; } @Override public boolean isElementDragAllowed(@Nonnull final MindMapPanel source) { return true; } @Override public boolean isMouseMoveProcessingAllowed(@Nonnull final MindMapPanel source) { return true; } @Override public boolean isMouseWheelProcessingAllowed(@Nonnull final MindMapPanel source) { return true; } @Override public boolean isMouseClickProcessingAllowed(@Nonnull final MindMapPanel source) { return true; } @Override @Nonnull public MindMapPanelConfig provideConfigForMindMapPanel(@Nonnull final MindMapPanel source) { final MindMapPanelConfig config = new MindMapPanelConfig(); config.loadFrom(PreferencesManager.getInstance().getPreferences()); return config; } private Map<Class<? extends PopUpMenuItemPlugin>, CustomJob> customProcessors = null; @Override @Nonnull public TabTitle getTabTitle() { return this.title; } @Override @Nonnull public JComponent getMainComponent() { return this; } @Override public void requestFocus() { this.mindMapPanel.requestFocus(); } public void topicToCentre(@Nullable final Topic topic) { if (topic != null) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { final AbstractElement element = (AbstractElement) Assertions.assertNotNull(topic).getPayload(); if (element != null) { final Rectangle2D bounds = element.getBounds(); final Dimension viewPortSize = getViewport().getExtentSize(); final int x = Math.max(0, (int) Math .round(bounds.getX() - (viewPortSize.getWidth() - bounds.getWidth()) / 2)); final int y = Math.max(0, (int) Math .round(bounds.getY() - (viewPortSize.getHeight() - bounds.getHeight()) / 2)); getViewport().setViewPosition(new Point(x, y)); } } }); } } @Override public void onMindMapModelChanged(@Nonnull final MindMapPanel source) { this.title.setChanged(true); this.getViewport().revalidate(); this.repaint(); } @Override public void onMindMapModelRealigned(@Nonnull final MindMapPanel source, @Nonnull final Dimension coveredAreaSize) { this.getViewport().revalidate(); this.repaint(); } @Override public void onEnsureVisibilityOfTopic(@Nonnull final MindMapPanel source, @Nullable final Topic topic) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (topic == null) { return; } final AbstractElement element = (AbstractElement) topic.getPayload(); if (element == null) { return; } final Rectangle2D orig = element.getBounds(); final int GAP = 30; final Rectangle bounds = orig.getBounds(); bounds.setLocation(Math.max(0, bounds.x - GAP), Math.max(0, bounds.y - GAP)); bounds.setSize(bounds.width + GAP * 2, bounds.height + GAP * 2); final JViewport viewport = getViewport(); final Rectangle visible = viewport.getViewRect(); if (visible.contains(bounds)) { return; } bounds.setLocation(bounds.x - visible.x, bounds.y - visible.y); viewport.scrollRectToVisible(bounds); } }); } @Override public void onClickOnExtra(@Nonnull final MindMapPanel source, final int clicks, @Nonnull final Topic topic, @Nonnull final Extra<?> extra) { if (clicks > 1) { switch (extra.getType()) { case FILE: { Main.getApplicationFrame().endFullScreenIfActive(); final MMapURI uri = (MMapURI) extra.getValue(); final File theFile = uri.asFile(getProjectFolder()); if (Boolean.parseBoolean(uri.getParameters().getProperty(FILELINK_ATTR_OPEN_IN_SYSTEM, "false"))) { //NOI18N UiUtils.openInSystemViewer(theFile); } else if (theFile.isDirectory()) { this.context.openProject(theFile, false); } else if (!this.context.openFileAsTab(theFile)) { UiUtils.openInSystemViewer(theFile); } } break; case LINK: { Main.getApplicationFrame().endFullScreenIfActive(); final MMapURI uri = ((ExtraLink) extra).getValue(); if (!UiUtils.browseURI(uri.asURI(), PreferencesManager.getInstance().getPreferences().getBoolean("useInsideBrowser", false))) { //NOI18N DialogProviderManager.getInstance().getDialogProvider().msgError(String.format( BUNDLE.getString("MMDGraphEditor.onClickOnExtra.msgCantBrowse"), uri.toString())); } } break; case NOTE: { editTextForTopic(topic); } break; case TOPIC: { final Topic theTopic = source.getModel().findTopicForLink((ExtraTopic) extra); if (theTopic == null) { // not presented DialogProviderManager.getInstance().getDialogProvider() .msgWarn(BUNDLE.getString("MMDGraphEditor.onClickOnExtra.msgCantFindTopic")); } else { // detected this.mindMapPanel.focusTo(theTopic); } } break; } } } @Override public void onChangedSelection(@Nonnull final MindMapPanel source, @Nonnull @MustNotContainNull final Topic[] currentSelectedTopics) { } @Override public boolean allowedRemovingOfTopics(@Nonnull final MindMapPanel source, @Nonnull @MustNotContainNull final Topic[] topics) { boolean topicsNotImportant = true; for (final Topic t : topics) { topicsNotImportant &= t.canBeLost(); if (!topicsNotImportant) { break; } } final boolean result; if (topicsNotImportant) { result = true; } else { result = DialogProviderManager.getInstance().getDialogProvider().msgConfirmYesNo( BUNDLE.getString("MMDGraphEditor.allowedRemovingOfTopics,title"), String.format( BUNDLE.getString("MMDGraphEditor.allowedRemovingOfTopics.message"), topics.length)); } return result; } @Override public void dragEnter(@Nonnull final DropTargetDragEvent dtde) { this.dragAcceptableType = checkDragType(dtde); if (!this.dragAcceptableType) { dtde.rejectDrag(); } else { dtde.acceptDrag(DnDConstants.ACTION_MOVE); } repaint(); } @Override public void dragOver(@Nonnull final DropTargetDragEvent dtde) { if (acceptOrRejectDragging(dtde)) { dtde.acceptDrag(DnDConstants.ACTION_MOVE); } else { dtde.rejectDrag(); } repaint(); } @Override public void dropActionChanged(@Nonnull final DropTargetDragEvent dtde) { } @Override public void dragExit(@Nonnull final DropTargetEvent dte) { } @Override public void drop(@Nonnull final DropTargetDropEvent dtde) { dtde.acceptDrop(DnDConstants.ACTION_MOVE); File detectedFileObject = null; for (final DataFlavor df : dtde.getCurrentDataFlavors()) { final Class<?> representation = df.getRepresentationClass(); if (FileTransferable.class.isAssignableFrom(representation)) { final FileTransferable t = (FileTransferable) dtde.getTransferable(); final List<File> listOfFiles = t.getFiles(); detectedFileObject = listOfFiles.isEmpty() ? null : listOfFiles.get(0); break; } else if (df.isFlavorJavaFileListType()) { try { final List list = (List) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); if (list != null && !list.isEmpty()) { detectedFileObject = (File) list.get(0); } break; } catch (Exception ex) { LOGGER.error("Can't extract file from DnD", ex); } } } if (detectedFileObject != null) { addFileToElement(detectedFileObject, this.mindMapPanel.findTopicUnderPoint(dtde.getLocation())); } } private void addFileToElement(@Nonnull final File theFile, @Nullable final AbstractElement element) { if (element != null) { final Topic topic = element.getModel(); final MMapURI theURI; if (PreferencesManager.getInstance().getPreferences().getBoolean("makeRelativePathToProject", true)) { //NOI18N final File projectFolder = getProjectFolder(); if (theFile.equals(projectFolder)) { theURI = new MMapURI(projectFolder, new File("."), null); } else { theURI = new MMapURI(projectFolder, theFile, null); } } else { theURI = new MMapURI(null, theFile, null); } if (topic.getExtras().containsKey(Extra.ExtraType.FILE)) { if (!DialogProviderManager.getInstance().getDialogProvider().msgConfirmOkCancel( BUNDLE.getString("MMDGraphEditor.addDataObjectToElement.confirmTitle"), BUNDLE.getString("MMDGraphEditor.addDataObjectToElement.confirmMsg"))) { return; } } topic.setExtra(new ExtraFile(theURI)); this.mindMapPanel.invalidate(); this.mindMapPanel.repaint(); onMindMapModelChanged(this.mindMapPanel); } } protected boolean acceptOrRejectDragging(@Nonnull final DropTargetDragEvent dtde) { final int dropAction = dtde.getDropAction(); boolean result = false; if (this.dragAcceptableType && (dropAction & DnDConstants.ACTION_COPY_OR_MOVE) != 0 && this.mindMapPanel.findTopicUnderPoint(dtde.getLocation()) != null) { result = true; } return result; } protected static boolean checkDragType(@Nonnull final DropTargetDragEvent dtde) { boolean result = false; for (final DataFlavor flavor : dtde.getCurrentDataFlavors()) { final Class dataClass = flavor.getRepresentationClass(); if (FileTransferable.class.isAssignableFrom(dataClass) || flavor.isFlavorJavaFileListType()) { result = true; break; } } return result; } @Nonnull private Map<Class<? extends PopUpMenuItemPlugin>, CustomJob> getCustomProcessors() { if (this.customProcessors == null) { this.customProcessors = new HashMap<>(); this.customProcessors.put(ExtraNotePlugin.class, new CustomJob() { @Override public void doJob(@Nonnull final PopUpMenuItemPlugin plugin, @Nonnull final MindMapPanel panel, @Nonnull final DialogProvider dialogProvider, @Nullable final Topic topic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) { if (topic != null) { editTextForTopic(topic); panel.requestFocus(); } } }); this.customProcessors.put(ExtraFilePlugin.class, new CustomJob() { @Override public void doJob(@Nonnull final PopUpMenuItemPlugin plugin, @Nonnull final MindMapPanel panel, @Nonnull final DialogProvider dialogProvider, @Nullable final Topic topic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) { editFileLinkForTopic(topic); panel.requestFocus(); } }); this.customProcessors.put(ExtraURIPlugin.class, new CustomJob() { @Override public void doJob(@Nonnull final PopUpMenuItemPlugin plugin, @Nonnull final MindMapPanel panel, @Nonnull final DialogProvider dialogProvider, @Nullable final Topic topic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) { editLinkForTopic(topic); panel.requestFocus(); } }); this.customProcessors.put(ExtraJumpPlugin.class, new CustomJob() { @Override public void doJob(@Nonnull final PopUpMenuItemPlugin plugin, @Nonnull final MindMapPanel panel, @Nonnull final DialogProvider dialogProvider, @Nullable final Topic topic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) { editTopicLinkForTopic(topic); panel.requestFocus(); } }); this.customProcessors.put(ChangeColorPlugin.class, new CustomJob() { @Override public void doJob(@Nonnull final PopUpMenuItemPlugin plugin, @Nonnull final MindMapPanel panel, @Nonnull final DialogProvider dialogProvider, @Nullable final Topic topic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) { processColorDialogForTopics(panel, selectedTopics.length > 0 ? selectedTopics : new Topic[] { topic }); } }); this.customProcessors.put(AboutPlugin.class, new CustomJob() { @Override public void doJob(@Nonnull final PopUpMenuItemPlugin plugin, @Nonnull final MindMapPanel panel, @Nonnull final DialogProvider dialogProvider, @Nullable final Topic topic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) { showAbout(); } }); this.customProcessors.put(OptionsPlugin.class, new CustomJob() { @Override public void doJob(@Nonnull final PopUpMenuItemPlugin plugin, @Nonnull final MindMapPanel panel, @Nonnull final DialogProvider dialogProvider, @Nullable final Topic topic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) { startOptionsEdit(); } }); } return this.customProcessors; } private void editTextForTopic(@Nonnull final Topic topic) { final ExtraNote note = (ExtraNote) topic.getExtras().get(Extra.ExtraType.NOTE); final String result; if (note == null) { // create new result = UiUtils .editText(String.format(BUNDLE.getString("MMDGraphEditor.editTextForTopic.dlfAddNoteTitle"), Utils.makeShortTextVersion(topic.getText(), 16)), ""); //NOI18N } else { // edit result = UiUtils .editText(String.format(BUNDLE.getString("MMDGraphEditor.editTextForTopic.dlgEditNoteTitle"), Utils.makeShortTextVersion(topic.getText(), 16)), note.getValue()); } if (result != null) { if (result.isEmpty()) { topic.removeExtra(Extra.ExtraType.NOTE); } else { topic.setExtra(new ExtraNote(result)); } this.mindMapPanel.invalidate(); this.mindMapPanel.repaint(); onMindMapModelChanged(this.mindMapPanel); } } @Nullable private File getProjectFolder() { File result = null; final File associatedFile = this.title.getAssociatedFile(); if (associatedFile != null) { final NodeProject project = context.findProjectForFile(associatedFile); result = project == null ? null : project.getFolder(); } return result; } private void editFileLinkForTopic(@Nullable final Topic topic) { if (topic != null) { final ExtraFile file = (ExtraFile) topic.getExtras().get(Extra.ExtraType.FILE); final FileEditPanel.DataContainer path; final File projectFolder = getProjectFolder(); if (file == null) { path = UiUtils.editFilePath(BUNDLE.getString("MMDGraphEditor.editFileLinkForTopic.dlgTitle"), projectFolder, null); } else { final MMapURI uri = file.getValue(); final boolean flagOpenInSystem = Boolean .parseBoolean(uri.getParameters().getProperty(FILELINK_ATTR_OPEN_IN_SYSTEM, "false")); //NOI18N final FileEditPanel.DataContainer origPath; origPath = new FileEditPanel.DataContainer(uri.asFile(projectFolder).getAbsolutePath(), flagOpenInSystem); path = UiUtils.editFilePath(BUNDLE.getString("MMDGraphEditor.editFileLinkForTopic.addPathTitle"), projectFolder, origPath); } if (path != null) { final boolean valueChanged; if (path.isEmpty()) { valueChanged = topic.removeExtra(Extra.ExtraType.FILE); } else { final Properties props = new Properties(); if (path.isShowWithSystemTool()) { props.put(FILELINK_ATTR_OPEN_IN_SYSTEM, "true"); //NOI18N } final MMapURI fileUri = MMapURI.makeFromFilePath( PreferencesManager.getInstance().getPreferences() .getBoolean("makeRelativePathToProject", true) ? projectFolder : null, path.getPath(), props); //NOI18N final File theFile = fileUri.asFile(projectFolder); LOGGER.info(String.format("Path %s converted to uri: %s", path.getPath(), fileUri.asString(false, true))); //NOI18N if (theFile.exists()) { topic.setExtra(new ExtraFile(fileUri)); valueChanged = true; } else { DialogProviderManager.getInstance().getDialogProvider() .msgError(String.format( BUNDLE.getString("MMDGraphEditor.editFileLinkForTopic.errorCantFindFile"), path.getPath())); valueChanged = false; } } if (valueChanged) { this.mindMapPanel.invalidate(); this.mindMapPanel.repaint(); onMindMapModelChanged(this.mindMapPanel); } } } } private void editTopicLinkForTopic(@Nullable final Topic topic) { if (topic != null) { final ExtraTopic link = (ExtraTopic) topic.getExtras().get(Extra.ExtraType.TOPIC); ExtraTopic result = null; final ExtraTopic remove = new ExtraTopic("_______"); //NOI18N if (link == null) { final MindMapTreePanel treePanel = new MindMapTreePanel(this.mindMapPanel.getModel(), null, true, null); if (DialogProviderManager.getInstance().getDialogProvider().msgOkCancel( BUNDLE.getString("MMDGraphEditor.editTopicLinkForTopic.dlgSelectTopicTitle"), treePanel)) { final Topic selected = treePanel.getSelectedTopic(); treePanel.dispose(); if (selected != null) { result = ExtraTopic.makeLinkTo(this.mindMapPanel.getModel(), selected); } else { result = remove; } } } else { final MindMapTreePanel panel = new MindMapTreePanel(this.mindMapPanel.getModel(), link, true, null); if (DialogProviderManager.getInstance().getDialogProvider().msgOkCancel( BUNDLE.getString("MMDGraphEditor.editTopicLinkForTopic.dlgEditSelectedTitle"), panel)) { final Topic selected = panel.getSelectedTopic(); if (selected != null) { result = ExtraTopic.makeLinkTo(this.mindMapPanel.getModel(), selected); } else { result = remove; } } } if (result != null) { if (result == remove) { topic.removeExtra(Extra.ExtraType.TOPIC); } else { topic.setExtra(result); } this.mindMapPanel.invalidate(); this.mindMapPanel.repaint(); onMindMapModelChanged(this.mindMapPanel); } } } private void editLinkForTopic(@Nullable final Topic topic) { if (topic != null) { final ExtraLink link = (ExtraLink) topic.getExtras().get(Extra.ExtraType.LINK); final MMapURI result; if (link == null) { // create new result = UiUtils .editURI(String.format(BUNDLE.getString("MMDGraphEditor.editLinkForTopic.dlgAddURITitle"), Utils.makeShortTextVersion(topic.getText(), 16)), null); } else { // edit result = UiUtils .editURI(String.format(BUNDLE.getString("MMDGraphEditor.editLinkForTopic.dlgEditURITitle"), Utils.makeShortTextVersion(topic.getText(), 16)), link.getValue()); } if (result != null) { if (result == UiUtils.EMPTY_URI) { topic.removeExtra(Extra.ExtraType.LINK); } else { topic.setExtra(new ExtraLink(result)); } this.mindMapPanel.invalidate(); this.mindMapPanel.repaint(); onMindMapModelChanged(this.mindMapPanel); } } } private void processColorDialogForTopics(@Nonnull final MindMapPanel source, @Nonnull @MustNotContainNull final Topic[] topics) { final Color borderColor = UiUtils.extractCommonColorForColorChooserButton(ATTR_BORDER_COLOR.getText(), topics); final Color fillColor = UiUtils.extractCommonColorForColorChooserButton(ATTR_FILL_COLOR.getText(), topics); final Color textColor = UiUtils.extractCommonColorForColorChooserButton(ATTR_TEXT_COLOR.getText(), topics); final ColorAttributePanel panel = new ColorAttributePanel(borderColor, fillColor, textColor); if (DialogProviderManager.getInstance().getDialogProvider().msgOkCancel( String.format(BUNDLE.getString("MMDGraphEditor.colorEditDialogTitle"), topics.length), panel)) { ColorAttributePanel.Result result = panel.getResult(); if (result.getBorderColor() != ColorChooserButton.DIFF_COLORS) { Utils.setAttribute(ATTR_BORDER_COLOR.getText(), Utils.color2html(result.getBorderColor(), false), topics); } if (result.getTextColor() != ColorChooserButton.DIFF_COLORS) { Utils.setAttribute(ATTR_TEXT_COLOR.getText(), Utils.color2html(result.getTextColor(), false), topics); } if (result.getFillColor() != ColorChooserButton.DIFF_COLORS) { Utils.setAttribute(ATTR_FILL_COLOR.getText(), Utils.color2html(result.getFillColor(), false), topics); } onMindMapModelChanged(source); source.updateView(true); } } private void showAbout() { JOptionPane.showMessageDialog(Main.getApplicationFrame(), new AboutPanel(), "About", JOptionPane.PLAIN_MESSAGE); } private void startOptionsEdit() { this.context.editPreferences(); } @Override @Nonnull public JPopupMenu makePopUpForMindMapPanel(@Nonnull final MindMapPanel source, @Nonnull final Point point, @Nullable final AbstractElement elementUnderMouse, @Nullable final ElementPart elementPartUnderMouse) { return Utils.makePopUp(source, DialogProviderManager.getInstance().getDialogProvider(), elementUnderMouse == null ? null : elementUnderMouse.getModel(), source.getSelectedTopics(), getCustomProcessors()); } @Override @Nonnull public DialogProvider getDialogProvider(@Nonnull final MindMapPanel source) { return DialogProviderManager.getInstance().getDialogProvider(); } @Override public boolean processDropTopicToAnotherTopic(@Nonnull final MindMapPanel source, @Nonnull final Point dropPoint, @Nullable final Topic draggedTopic, @Nullable final Topic destinationTopic) { boolean result = false; if (draggedTopic != null && destinationTopic != null && draggedTopic != destinationTopic) { if (destinationTopic.getExtras().containsKey(Extra.ExtraType.TOPIC)) { if (!DialogProviderManager.getInstance().getDialogProvider().msgConfirmOkCancel( BUNDLE.getString("MMDGraphEditor.addTopicToElement.confirmTitle"), BUNDLE.getString("MMDGraphEditor.addTopicToElement.confirmMsg"))) { return result; } } final ExtraTopic topicLink = ExtraTopic.makeLinkTo(this.mindMapPanel.getModel(), draggedTopic); destinationTopic.setExtra(topicLink); result = true; } return result; } @Override public boolean canBeDeletedSilently(@Nonnull final MindMap map, @Nonnull final Topic topic) { return topic.getText().isEmpty() && topic.getExtras().isEmpty() && doesContainOnlyStandardAttributes(topic); } }