/*
* Freeplane - mind map editor
* Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev
*
* This file is modified 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.view.swing.map;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import org.freeplane.core.ui.IMouseListener;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.map.NodeModel;
import org.freeplane.features.mode.Controller;
import org.freeplane.features.mode.ModeController;
import org.freeplane.features.nodestyle.NodeStyleController;
import org.freeplane.features.nodestyle.NodeStyleModel;
import org.freeplane.features.note.NoteController;
import org.freeplane.features.note.NoteModel;
import org.freeplane.features.text.DetailTextModel;
import org.freeplane.features.text.TextController;
import org.freeplane.view.swing.ui.DefaultMapMouseListener;
import org.freeplane.view.swing.ui.DetailsViewMouseListener;
import org.freeplane.view.swing.ui.LinkNavigatorMouseListener;
class NodeViewFactory {
private static NodeViewFactory factory;
static NodeViewFactory getInstance() {
if (NodeViewFactory.factory == null) {
NodeViewFactory.factory = new NodeViewFactory();
}
return NodeViewFactory.factory;
}
private NodeViewFactory() {
}
private void fireNodeViewCreated(final NodeView newView) {
newView.getMap().getModeController().onViewCreated(newView);
}
JComponent newContentPane(final NodeView view) {
return new ContentPane();
}
MainView newMainView(final NodeView node) {
String shape = shape(node);
final MainView oldView = node.getMainView();
if(oldView != null && oldView.getShape().equals(shape))
return oldView;
final ModeController modeController = node.getMap().getModeController();
final NodeModel model = node.getModel();
final MainView view;
if (shape.equals(NodeStyleModel.STYLE_BUBBLE)) {
if (model.isRoot())
view = new RootMainView(NodeStyleModel.STYLE_BUBBLE);
else
view = new BubbleMainView();
}
else {
if (shape != null && ! shape.equals(NodeStyleModel.STYLE_FORK))
System.err.println("Tried to create a NodeView of unknown Style " + String.valueOf(shape));
if (model.isRoot())
view = new RootMainView(NodeStyleModel.STYLE_FORK);
else
view = new ForkMainView();
}
NodeTooltipManager toolTipManager = NodeTooltipManager.getSharedInstance(modeController);
toolTipManager.registerComponent(view);
return view;
}
private String shape(NodeView node) {
final ModeController modeController = node.getMap().getModeController();
final NodeModel model = node.getModel();
String shape = NodeStyleController.getController(modeController).getShape(model);
if (shape.equals(NodeStyleModel.SHAPE_COMBINED)) {
if (Controller.getCurrentModeController().getMapController().isFolded(model)) {
shape= NodeStyleModel.STYLE_BUBBLE;
}
else {
shape = NodeStyleModel.STYLE_FORK;
}
}
else while(shape.equals(NodeStyleModel.SHAPE_AS_PARENT)){
node = node.getParentView();
if (node == null)
shape = NodeStyleModel.STYLE_FORK;
else
shape = node.getMainView().getShape();
}
return shape;
}
/**
* Factory method which creates the right NodeView for the model.
*/
NodeView newNodeView(final NodeModel model, final MapView map, final Container parent, final int index) {
final NodeView newView = new NodeView(model, map, parent);
parent.add(newView, index);
if(map.isDisplayable())
updateNewView(newView);
else
newView.addHierarchyListener(new HierarchyListener() {
public void hierarchyChanged(HierarchyEvent e) {
NodeView view = (NodeView) e.getComponent();
if(displayed(view, e)){
view.removeHierarchyListener(this);
updateNewView(view);
}
else if(removed(view, e)){
view.removeHierarchyListener(this);
}
}
private boolean removed(NodeView view, HierarchyEvent e) {
return 0 != (e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) && view.getParent() == null;
}
private boolean displayed(NodeView view, HierarchyEvent e) {
return 0 != (e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) && view.isDisplayable();
}
});
return newView;
}
private void updateNewView(final NodeView newView) {
newView.getModel().addViewer(newView);
newView.setLayout(SelectableLayout.getInstance());
newView.setMainView(newMainView(newView));
updateNoteViewer(newView);
newView.update();
fireNodeViewCreated(newView);
newView.addChildViews();
}
private static Map<Color, Icon> coloredNoteIcons = new HashMap<Color, Icon>();
private Icon coloredIcon = createColoredIcon();
private static final IMouseListener DETAILS_MOUSE_LISTENER = new DetailsViewMouseListener();
private static final LinkNavigatorMouseListener LINK_MOUSE_LISTENER = new LinkNavigatorMouseListener();
public ZoomableLabel createNoteViewer() {
final ZoomableLabel label = new ZoomableLabel();
label.addMouseListener(LINK_MOUSE_LISTENER);
label.addMouseMotionListener(LINK_MOUSE_LISTENER);
label.setIcon(coloredIcon);
label.setVerticalTextPosition(JLabel.TOP);
return label;
}
private Icon createColoredIcon() {
return new Icon() {
public void paintIcon(Component c, Graphics g, int x, int y) {
NodeView nodeView = (NodeView) SwingUtilities.getAncestorOfClass(NodeView.class, c);
if(nodeView == null)
return;
final Color iconColor = nodeView.getEdgeColor();
createColoredIcon(iconColor).paintIcon(c, g, x, y);
}
public int getIconWidth() {
return createColoredIcon(Color.BLACK).getIconWidth();
}
public int getIconHeight() {
return createColoredIcon(Color.BLACK).getIconHeight();
}
};
}
private Icon createColoredIcon(Color iconColor) {
Icon icon = coloredNoteIcons.get(iconColor);
if(icon == null){
final BufferedImage img;
try {
img = ImageIO.read(NoteController.bwNoteIconUrl);
final int oldRGB = 0xffffff & Color.BLACK.getRGB();
final int newRGB = 0xffffff & iconColor.getRGB();
if(oldRGB != newRGB){
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
final int rgb = img.getRGB(x, y);
if ((0xffffff &rgb) == oldRGB)
img.setRGB(x, y, 0xff000000 & rgb| newRGB);
}
}
}
icon = new ImageIcon(img);
coloredNoteIcons.put(iconColor, icon);
}
catch (IOException e) {
}
}
return icon;
}
void updateNoteViewer(NodeView nodeView) {
ZoomableLabel note = (ZoomableLabel) nodeView.getContent(NodeView.NOTE_VIEWER_POSITION);
String oldText = note != null ? note.getText() : null;
String newText = null;
if (nodeView.getMap().showNotes()) {
final TextController textController = TextController.getController();
final NodeModel model = nodeView.getModel();
final NoteModel extension = NoteModel.getNote(model);
if(extension != null){
final String originalText = extension.getHtml();
try {
newText = textController.getTransformedTextNoThrow(originalText, model, extension);
final boolean markTransformedText = TextController.isMarkTransformedTextSet();
if (markTransformedText && newText != originalText)
newText = colorize(newText, "green");
}
catch (Exception e) {
newText = colorize(TextUtils.format("MainView.errorUpdateText", originalText, e.getLocalizedMessage())
.replace("\n", "<br>"), "red");
}
}
}
if (oldText == null && newText == null) {
return;
}
final ZoomableLabel view;
if (oldText != null && newText != null) {
view = (ZoomableLabel) nodeView.getContent(NodeView.NOTE_VIEWER_POSITION);
}
else if (oldText == null && newText != null) {
view = NodeViewFactory.getInstance().createNoteViewer();
nodeView.addContent(view, NodeView.NOTE_VIEWER_POSITION);
}
else {
assert (oldText != null && newText == null);
nodeView.removeContent(NodeView.NOTE_VIEWER_POSITION);
return;
}
view.setFont(nodeView.getMap().getDefaultNoteFont());
view.updateText(newText);
}
private String colorize(final String text, String color) {
return "<span style=\"color:" + color + ";font-style:italic;\">" + text + "</span>";
}
void updateDetails(NodeView nodeView) {
final DetailTextModel detailText = DetailTextModel.getDetailText(nodeView.getModel());
if (detailText == null) {
nodeView.removeContent(NodeView.DETAIL_VIEWER_POSITION);
return;
}
DetailsView detailContent = (DetailsView) nodeView.getContent(NodeView.DETAIL_VIEWER_POSITION);
if (detailContent == null) {
detailContent = createDetailView();
nodeView.addContent(detailContent, NodeView.DETAIL_VIEWER_POSITION);
}
if (detailText.isHidden()) {
final ArrowIcon icon = new ArrowIcon(nodeView, true);
detailContent.setIcon(icon);
detailContent.setBackground(null);
detailContent.updateText("");
detailContent.setPreferredSize(new Dimension(icon.getIconWidth(), icon.getIconHeight()));
}
else {
final MapView map = nodeView.getMap();
detailContent.setFont(map.getDetailFont());
detailContent.setIcon(new ArrowIcon(nodeView, false));
detailContent.updateText(detailText.getHtml());
detailContent.setForeground(map.getDetailForeground());
detailContent.setBackground(nodeView.getDetailBackground());
detailContent.setPreferredSize(null);
}
}
private DetailsView createDetailView() {
DetailsView detailContent = new DetailsView();
final DefaultMapMouseListener mouseListener = new DefaultMapMouseListener();
detailContent.addMouseMotionListener(mouseListener);
detailContent.addMouseMotionListener(DETAILS_MOUSE_LISTENER);
detailContent.addMouseListener(DETAILS_MOUSE_LISTENER);
return detailContent;
}
}