Package org.freeplane.view.swing.features.filepreview

Source Code of org.freeplane.view.swing.features.filepreview.ViewerController

package org.freeplane.view.swing.features.filepreview;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.KeyboardFocusManager;
import java.awt.dnd.DropTarget;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.border.MatteBorder;
import javax.swing.filechooser.FileFilter;

import org.freeplane.core.extension.IExtension;
import org.freeplane.core.resources.ResourceController;
import org.freeplane.core.undo.IActor;
import org.freeplane.core.util.HtmlUtils;
import org.freeplane.core.util.LogUtils;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.link.LinkController;
import org.freeplane.features.map.INodeView;
import org.freeplane.features.map.MapModel;
import org.freeplane.features.map.NodeModel;
import org.freeplane.features.map.mindmapmode.MMapController;
import org.freeplane.features.mode.Controller;
import org.freeplane.features.mode.ModeController;
import org.freeplane.features.mode.NodeHookDescriptor;
import org.freeplane.features.mode.PersistentNodeHook;
import org.freeplane.features.ui.INodeViewLifeCycleListener;
import org.freeplane.features.ui.ViewController;
import org.freeplane.features.url.UrlManager;
import org.freeplane.n3.nanoxml.XMLElement;
import org.freeplane.view.swing.features.progress.mindmapmode.ProgressIcons;
import org.freeplane.view.swing.map.MainView;
import org.freeplane.view.swing.map.MapView;
import org.freeplane.view.swing.map.NodeView;

@NodeHookDescriptor(hookName = "ExternalObject", //
onceForMap = false)
public class ViewerController extends PersistentNodeHook implements INodeViewLifeCycleListener, IExtension {
  private static final MExternalImageDropListener DTL = new MExternalImageDropListener();

  private final class CombiFactory implements IViewerFactory {
    private IViewerFactory factory;
    private ScalableComponent component;

    public ScalableComponent createViewer(final URI uri,
        final Dimension preferredSize) throws MalformedURLException,
        IOException {
      factory = getViewerFactory(uri);
      component = (factory == null ? null : factory.createViewer(uri,
          preferredSize));
      return component;
    }

    public ScalableComponent createViewer(final ExternalResource resource,
        final URI absoluteUri, final int maximumWidth)
            throws MalformedURLException, IOException {
      factory = getViewerFactory(absoluteUri);
      component = factory.createViewer(resource, absoluteUri,
          maximumWidth);
      return component;
    }

    public String getDescription() {
      final StringBuilder sb = new StringBuilder();
      for (final IViewerFactory factory : factories) {
        if (sb.length() != 0) {
          sb.append(", ");
        }
        sb.append(factory.getDescription());
      }
      return sb.toString();
    }

    public boolean accept(final URI uri) {
      return getViewerFactory(uri) != null;
    }

    public ScalableComponent getComponent() {
      return component;
    }

    public ScalableComponent createViewer(URI uri, float zoom)
        throws MalformedURLException, IOException {
      factory = getViewerFactory(uri);
      component = (factory == null ? null : factory.createViewer(uri,
          zoom));
      return component;
    }

  }

  static final class FactoryFileFilter extends FileFilter {
    private final IViewerFactory factory;

    protected IViewerFactory getFactory() {
      return factory;
    }

    private FactoryFileFilter(final IViewerFactory factory) {
      this.factory = factory;
    }

    @Override
    public boolean accept(final File f) {
      return f.isDirectory() || factory.accept(f.toURI());
    }

    @Override
    public String getDescription() {
      return factory.getDescription();
    }
  }

  private class MyMouseListener implements MouseListener, MouseMotionListener {
    private boolean isActive = false;
    private boolean sizeChanged = false;

    public void mouseClicked(final MouseEvent e) {
      if (resetSize(e)) {
        return;
      }
      if (showNext(e)) {
        return;
      }
    }

    private boolean resetSize(final MouseEvent e) {
      if (e.getClickCount() != 2) {
        return false;
      }
      final JComponent viewer = (JComponent) e.getComponent();
      final int x = e.getX();
      final int width = viewer.getWidth();
      final int y = e.getY();
      final int height = viewer.getHeight();
      if (x < width - 4 * BORDER_SIZE || y < height - 4 * BORDER_SIZE) {
        return false;
      }
      final IViewerFactory factory = (IViewerFactory) viewer.getClientProperty(IViewerFactory.class);
      if (factory == null) {
        return true;
      }
      final MapView mapView = (MapView) SwingUtilities.getAncestorOfClass(MapView.class, viewer);
      setZoom(mapView.getModeController(), mapView.getModel(), (ExternalResource) viewer
          .getClientProperty(ExternalResource.class), 1f);
      sizeChanged = false;
      return true;
    }

    private boolean showNext(final MouseEvent e) {
      //double left click
      final JComponent component = (JComponent) e.getComponent();
      final int cursorType = component.getCursor().getType();
      if ((e.getClickCount() != 2) || (e.getButton() != MouseEvent.BUTTON1)
              || (cursorType == Cursor.SE_RESIZE_CURSOR)) {
        return false;
      }
      final ExternalResource activeView = getModel(e);
      NodeModel node = null;
      //get node from mouse click
      for (int i = 0; i < e.getComponent().getParent().getComponentCount(); i++) {
        if (e.getComponent().getParent().getComponent(i) instanceof MainView) {
          final MainView mv = (MainView) e.getComponent().getParent().getComponent(i);
          node = mv.getNodeView().getModel();
          break;
        }
      }
      if (node == null) {
        node = Controller.getCurrentModeController().getMapController().getSelectedNode();
      }
      final MapModel map = node.getMap();
      URI absoluteUri = activeView.getAbsoluteUri(map);
      if(absoluteUri == null)
        return false;
      final String sActUri = absoluteUri.toString();
      if (!sActUri.matches(".*_[0-9]{2}\\.[a-zA-Z0-9]*")) {
        return false;
      }
      int i = Integer.parseInt(sActUri.substring(sActUri.lastIndexOf("_") + 1, sActUri.lastIndexOf("_") + 3));
      //show previous with ctrl + double click
      if (e.isControlDown()) {
        if (i > 0) {
          i--;
        }
        else {
          //remove view if 0 and down
          if (activeView.getUri().toString().matches(ProgressIcons.EXTENDED_PROGRESS_ICON_IDENTIFIER)) {
            ProgressIcons.removeProgressIcons(node);
          }
          remove(node, activeView);
          Controller.getCurrentModeController().getMapController().nodeChanged(node,
              NodeModel.UNKNOWN_PROPERTY, null, null);
          return true;
        }
      }
      else {
        i++;
      }
      final String sNextNum;
      if (i < 10) {
        sNextNum = "0" + Integer.toString(i);
      }
      else {
        sNextNum = Integer.toString(i);
      }
      URI nextUri = null;
      try {
        nextUri = new URI(sActUri.replaceFirst("_[0-9]{2}\\.", "_" + sNextNum + "."));
      }
      catch (final URISyntaxException e1) {
        e1.printStackTrace();
      }
      final String sNextURI = nextUri.getPath();
      if ((sNextURI.contains("_tenth_")&& (i > 10))|| ((sNextURI.contains("_quarter_"))&& (i > 4))) {
        return false;
      }
      final ExternalResource nextView = new ExternalResource(nextUri);
      nextView.setZoom(activeView.getZoom());
      remove(node, activeView);
      add(node, nextView);
      ProgressIcons.updateExtendedProgressIcons(node, sNextURI);
      return true;
    }

    public void mouseEntered(final MouseEvent e) {
      if (isActive) {
        return;
      }
      final ExternalResource model = getModel(e);
      if (model == null) {
        return;
      }
      Controller.getCurrentController().getViewController().out(model.getUri().toString());
      setCursor(e);
    }

    private ExternalResource getModel(final MouseEvent e) {
      final JComponent component = (JComponent) e.getComponent();
      final ExternalResource model = (ExternalResource) component.getClientProperty(ExternalResource.class);
      return model;
    }

    public void mouseExited(final MouseEvent e) {
      if (isActive) {
        return;
      }
      setCursor(e);
    }

    private void setCursor(final MouseEvent e) {
      final Component component = e.getComponent();
      final int cursorType;
      final int x = e.getX();
      final int width = component.getWidth();
      final int y = e.getY();
      final int height = component.getHeight();
      if (width - 6 * BORDER_SIZE <= x && x <= width && height - 6 * BORDER_SIZE <= y && y <= height) {
        cursorType = Cursor.SE_RESIZE_CURSOR;
      }
      else {
        cursorType = Cursor.DEFAULT_CURSOR;
      }
      final Cursor cursor = component.getCursor();
      if (cursor.getType() != cursorType) {
        final Cursor predefinedCursor = cursorType == Cursor.DEFAULT_CURSOR ? null : Cursor
            .getPredefinedCursor(cursorType);
        component.setCursor(predefinedCursor);
      }
    }

    public void mousePressed(final MouseEvent e) {
      final JComponent component = (JComponent) e.getComponent();
      final int cursorType = component.getCursor().getType();
      if (cursorType == Cursor.SE_RESIZE_CURSOR) {
        final IViewerFactory factory = (IViewerFactory) component.getClientProperty(IViewerFactory.class);
        if (factory == null) {
          return;
        }
        isActive = true;
        return;
      }
      else {
        imagePopupMenu.maybeShowPopup(e);
        return;
      }
    }

    public void mouseReleased(final MouseEvent e) {
      if (sizeChanged) {
        final JComponent component = (JComponent) e.getComponent();
        final int x = component.getWidth();
        final int y = component.getHeight();
        final double r = Math.sqrt(x * x + y * y);
        final Dimension originalSize = ((ScalableComponent) component).getOriginalSize();
        final int w = originalSize.width;
        final int h = originalSize.height;
        final double r0 = Math.sqrt(w * w + h * h);
        final MapView mapView = (MapView) SwingUtilities.getAncestorOfClass(MapView.class, component);
        final float zoom = mapView.getZoom();
        final float modelSize = (float) (r / r0 / zoom);
        setZoom(mapView.getModeController(), mapView.getModel(), (ExternalResource) component
            .getClientProperty(ExternalResource.class), modelSize);
        sizeChanged = false;
      }
      else {
        imagePopupMenu.maybeShowPopup(e);
      }
      isActive = false;
      setCursor(e);
    }

    public void mouseDragged(final MouseEvent e) {
      if (!isActive) {
        return;
      }
      setSize(e);
    }

    private boolean setSize(final MouseEvent e) {
      if (!isActive) {
        return false;
      }
      final JComponent component = (JComponent) e.getComponent();
      final int cursorType = component.getCursor().getType();
      sizeChanged = true;
      final Dimension size;
      switch (cursorType) {
        case Cursor.SE_RESIZE_CURSOR:
          final Dimension minimumSize = new Dimension(10, 10);
          int x = e.getX() - 4 * BORDER_SIZE;
          int y = e.getY() - 4 * BORDER_SIZE;
          if (x <= 0 || y <= 0) {
            return true;
          }
          final double r = Math.sqrt(x * x + y * y);
          final Dimension preferredSize = ((ScalableComponent) component).getOriginalSize();
          final int width = preferredSize.width;
          final int height = preferredSize.height;
          final double r0 = Math.sqrt(width * width + height * height);
          x = (int) (width * r / r0);
          y = (int) (height * r / r0);
          final MapView mapView = (MapView) SwingUtilities.getAncestorOfClass(MapView.class, component);
          if (x < mapView.getZoomed(minimumSize.width) || y < mapView.getZoomed(minimumSize.height)) {
            return true;
          }
          size = new Dimension(x, y);
          ((ScalableComponent) component).setDraftViewerSize(size);
          component.revalidate();
          break;
        default:
      }
      return true;
    }

    public void mouseMoved(final MouseEvent e) {
      if (isActive) {
        return;
      }
      setCursor(e);
    }
  }

  static private ExternalImagePopupMenu imagePopupMenu;
  private static final int BORDER_SIZE = 1;
  private static final Color BORDER_COLOR = Color.BLACK;
  static final int VIEWER_POSITION = 5;
  private final MyMouseListener mouseListener = new MyMouseListener();
  final private Set<IViewerFactory> factories;

  public ViewerController() {
    super();
    factories = new HashSet<IViewerFactory>();
    final ModeController modeController = Controller.getCurrentModeController();
    modeController.addINodeViewLifeCycleListener(this);
    modeController.addExtension(this.getClass(), this);
    factories.add(new BitmapViewerFactory());
  }

  public void setZoom(final ModeController modeController, final MapModel map, final ExternalResource model,
                      final float size) {
    final float oldSize = model.getZoom();
    if (size == oldSize) {
      return;
    }
    final IActor actor = new IActor() {
      public void act() {
        model.setZoom(size);
        modeController.getMapController().setSaved(map, false);
      }

      public String getDescription() {
        return "setModelSize";
      }

      public void undo() {
        model.setZoom(oldSize);
        modeController.getMapController().setSaved(map, false);
      }
    };
    modeController.execute(actor, map);
  }

  @Override
  protected void add(final NodeModel node, final IExtension extension) {
    final ExternalResource preview = (ExternalResource) extension;
    for(NodeModel nodeClone : node.clones()){
      for (final INodeView iNodeView : nodeClone.getViewers()) {
        final NodeView view = (NodeView) iNodeView;
        createViewer(preview, view);
      }
    }
    super.add(node, extension);
  }

  @Override
  protected IExtension createExtension(final NodeModel node) {
    URI uri = createURI(node);
    if(uri == null)
      return null;
    File input = new File(uri.getPath());
    final ExternalResource preview = new ExternalResource(uri);
    ProgressIcons.updateExtendedProgressIcons(node, input.getName());
    return preview;
  }
 
  protected URI createURI(final NodeModel node) {
    final Controller controller = Controller.getCurrentController();
    final ViewController viewController = controller.getViewController();
    final MapModel map = node.getMap();
    final File file = map.getFile();
    final boolean useRelativeUri = ResourceController.getResourceController().getProperty("links").equals(
        "relative");
    if (file == null && useRelativeUri) {
      JOptionPane.showMessageDialog(viewController.getContentPane(), TextUtils
          .getText("not_saved_for_image_error"), "Freeplane", JOptionPane.WARNING_MESSAGE);
      return null;
    }
    final UrlManager urlManager = controller.getModeController().getExtension(UrlManager.class);
    final JFileChooser chooser = urlManager.getFileChooser(null, false);
    chooser.setAcceptAllFileFilterUsed(false);
    if (factories.size() > 1) {
      final FileFilter combiFileFilter = getCombiFileFilter();
      chooser.addChoosableFileFilter(combiFileFilter);
      for (final IViewerFactory factory : factories) {
        chooser.addChoosableFileFilter(new FactoryFileFilter(factory));
      }
      chooser.setFileFilter(combiFileFilter);
    }
    else {
      chooser.setFileFilter(new FactoryFileFilter(factories.iterator().next()));
    }
    chooser.setAccessory(new ImagePreview(chooser));
    final int returnVal = chooser.showOpenDialog(Controller.getCurrentController().getViewController()
        .getContentPane());
    if (returnVal != JFileChooser.APPROVE_OPTION) {
      return null;
    }
    final File input = chooser.getSelectedFile();
    if (input == null) {
      return null;
    }
    URI uri = uriOf(input);
    if (uri == null) {
      return null;
    }
    if (useRelativeUri && uri.getScheme().equals("file")) {
      uri = LinkController.toLinkTypeDependantURI(map.getFile(), input);
    }
    return uri;
  }


  private URI uriOf(final File input) {
    String path = input.getPath();
    try {
          for (String protocol : new String[]{"http:" + File.separatorChar, "https:" + File.separatorChar}){
            int uriStart = path.indexOf(protocol);
            if(uriStart != -1)
              return new URI(protocol.substring(0, protocol.length() - 1) + "//" + path.substring(uriStart + protocol.length()).replace('\\', '/'));
          }
        }
        catch (URISyntaxException e) {
          LogUtils.warn(e);
        }
      return input.toURI();
    }

  private IViewerFactory getViewerFactory(final URI uri) {
    for (final IViewerFactory factory : factories) {
      if (factory.accept(uri)) {
        return factory;
      }
    }
    return null;
  }

  @Override
  protected IExtension createExtension(final NodeModel node, final XMLElement element) {
    try {
      final String attrUri = element.getAttribute("URI", null);
      if (attrUri != null) {
        final URI uri = new URI(attrUri);
        final ExternalResource previewUrl = new ExternalResource(uri);
        final String attrSize = element.getAttribute("SIZE", null);
        if (attrSize != null) {
          final float size = Float.parseFloat(attrSize);
          previewUrl.setZoom(size);
        }
        Controller.getCurrentModeController().getMapController().nodeChanged(node);
        return previewUrl;
      }
    }
    catch (final URISyntaxException e) {
    }
    return null;
  }

  void createViewer(final ExternalResource model, final NodeView view) {
    final JComponent viewer = createViewer(view.getMap().getModel(), model);
    if (imagePopupMenu == null) {
      imagePopupMenu = new ExternalImagePopupMenu();
    }
    viewer.setBorder(new MatteBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_COLOR));
    final Set<NodeView> viewers = model.getViewers();
    viewers.add(view);
    viewer.setBounds(viewer.getX() - 5, viewer.getY() - 5, viewer.getWidth() + 15, viewer.getHeight() + 15);
    view.addContent(viewer, VIEWER_POSITION);
    if(view.getMap().getModeController().canEdit()){
      final DropTarget dropTarget = new DropTarget(viewer, DTL);
      dropTarget.setActive(true);
    }
    if(view.isShortened())
      viewer.setVisible(false);
    else {
      viewer.revalidate();
      viewer.repaint();
    }
  }

  void deleteViewer(final ExternalResource model, final NodeView nodeView) {
    final Set<NodeView> viewers = model.getViewers();
    if (!viewers.contains(nodeView)) {
      return;
    }
    nodeView.removeContent(VIEWER_POSITION);
    viewers.remove(nodeView);
  }

  @Override
  protected Class<ExternalResource> getExtensionClass() {
    return ExternalResource.class;
  }

  public void onViewCreated(final Container container) {
    final NodeView nodeView = (NodeView) container;
    final ExternalResource previewUri = nodeView.getModel().getExtension(ExternalResource.class);
    if (previewUri == null) {
      return;
    }
    createViewer(previewUri, nodeView);
  }

  public void onViewRemoved(final Container container) {
    final NodeView nodeView = (NodeView) container;
    final ExternalResource previewUri = nodeView.getModel().getExtension(ExternalResource.class);
    if (previewUri == null) {
      return;
    }
    deleteViewer(previewUri, nodeView);
  }

  @Override
  protected void remove(final NodeModel node, final IExtension extension) {
    final ExternalResource resource = (ExternalResource) extension;
    resource.removeViewers();
    super.remove(node, extension);
  }

  @Override
  protected void saveExtension(final IExtension extension, final XMLElement element) {
    final ExternalResource previewUri = (ExternalResource) extension;
    final URI uri = previewUri.getUri();
    if (uri != null) {
      element.setAttribute("URI", uri.toString());
    }
    final float size = previewUri.getZoom();
    if (size != -1) {
      element.setAttribute("SIZE", Float.toString(size));
    }
    super.saveExtension(extension, element);
  }

  private JComponent createViewer(final MapModel map, final ExternalResource model) {
    final URI uri = model.getUri();
    if (uri == null) {
      return new JLabel("no file set");
    }
    final URI absoluteUri = model.getAbsoluteUri(map);
    if (absoluteUri == null) {
      return new JLabel(uri.toString());
    }
    final IViewerFactory factory = getViewerFactory(absoluteUri);
    if (factory == null) {
      return new JLabel(uri.toString());
    }
    JComponent viewer = null;
    try {
      final int maxWidth = ResourceController.getResourceController().getIntProperty("max_image_width");
      viewer = (JComponent) factory.createViewer(model, absoluteUri, maxWidth);
    }
    catch (final Exception e) {
      final String info = HtmlUtils.combineTextWithExceptionInfo(uri.toString(), e);
      return new JLabel(info);
    }
    if (viewer == null) {
      return new JLabel(uri.toString());
    }
    viewer.putClientProperty(IViewerFactory.class, factory);
    viewer.putClientProperty(ExternalResource.class, model);
    viewer.addMouseListener(mouseListener);
    viewer.addMouseMotionListener(mouseListener);
    return viewer;
  }

  private FileFilter getCombiFileFilter() {
    return new FactoryFileFilter(new CombiFactory());
  }

  public void addFactory(final IViewerFactory factory) {
    factories.add(factory);
  }

  public void removeFactory(final IViewerFactory factory) {
    factories.remove(factory);
  }

  /**
   * This method attaches an image to a node, that is referenced with an uri
   * @param uri : The image that is to be attached to a node
   * @param node : The node that is worked upon
   * @return : true if successful, false otherwise
   */
  public boolean paste(final URI uri, final NodeModel node) {

    if (uri == null || getViewerFactory(uri) == null) {
      return false;
    }

    final ExternalResource preview = new ExternalResource(uri);
    undoableDeactivateHook(node);
    undoableActivateHook(node, preview);
    ProgressIcons.updateExtendedProgressIcons(node, uri.getPath());
    return true;
  }

  public static enum PasteMode{
    AS_SIBLING, AS_CHILD, INSIDE;
    public static PasteMode valueOf(boolean asSibling){
      return asSibling ? AS_SIBLING : AS_CHILD;
    }
  }

  public boolean paste(URI uri, final NodeModel node, final boolean isLeft) {
    return pasteImage(uri, node, PasteMode.INSIDE, isLeft);
  }

  public boolean paste(final File file, final NodeModel targetNode, final PasteMode mode, final boolean isLeft) {
    URI uri = uriOf(file);
    return pasteImage(uri, targetNode, mode, isLeft);
  }

  public boolean pasteImage(URI uri, final NodeModel targetNode, final PasteMode mode, final boolean isLeft) {
      if (uri == null || getViewerFactory(uri) == null) {
      return false;
    }
    File file = new File(uri.getPath());
    boolean isFile = uri.getScheme().equals("file");
    if (isFile) {
          if (!file.exists()) {
            return false;
          }
          final File mapFile = targetNode.getMap().getFile();
          if (mapFile == null && LinkController.getLinkType() == LinkController.LINK_RELATIVE_TO_MINDMAP) {
            JOptionPane.showMessageDialog(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(),
              TextUtils.getText("not_saved_for_image_error"), "Freeplane", JOptionPane.WARNING_MESSAGE);
            return false;
          }
          if (LinkController.getLinkType() != LinkController.LINK_ABSOLUTE) {
            uri = LinkController.toLinkTypeDependantURI(mapFile, file);
          }
        }
    final MMapController mapController = (MMapController) Controller.getCurrentModeController().getMapController();
    final NodeModel node;
    if (mode.equals(PasteMode.INSIDE)) {
      node = targetNode;
    }
    else {
      node = mapController.newNode(file.getName(), targetNode.getMap());
      mapController.insertNode(node, targetNode, mode.equals(PasteMode.AS_SIBLING), isLeft, isLeft);
    }
    final ExternalResource preview = new ExternalResource(uri);
    undoableDeactivateHook(node);
    undoableActivateHook(node, preview);
    ProgressIcons.updateExtendedProgressIcons(node, file.getName());
    return true;
    }

  public IViewerFactory getCombiFactory() {
      return new CombiFactory();
    }
}
TOP

Related Classes of org.freeplane.view.swing.features.filepreview.ViewerController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.