Package com.cburch.logisim.gui.main

Source Code of com.cburch.logisim.gui.main.ProjectExplorer$MyCellRenderer

/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.gui.main;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.dnd.DnDConstants;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.ToolTipManager;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import com.cburch.logisim.circuit.Circuit;
import com.cburch.logisim.circuit.CircuitEvent;
import com.cburch.logisim.circuit.CircuitListener;
import com.cburch.logisim.circuit.SubcircuitFactory;
import com.cburch.logisim.comp.ComponentFactory;
import com.cburch.logisim.comp.ComponentDrawContext;
import com.cburch.logisim.file.LibraryEventSource;
import com.cburch.logisim.file.LogisimFile;
import com.cburch.logisim.file.LibraryEvent;
import com.cburch.logisim.file.LibraryListener;
import com.cburch.logisim.prefs.AppPreferences;
import com.cburch.logisim.proj.Project;
import com.cburch.logisim.proj.ProjectEvent;
import com.cburch.logisim.proj.ProjectListener;
import com.cburch.logisim.tools.AddTool;
import com.cburch.logisim.tools.Library;
import com.cburch.logisim.tools.Tool;
import com.cburch.logisim.util.JTreeDragController;
import com.cburch.logisim.util.JTreeUtil;
import com.cburch.logisim.util.LocaleListener;
import com.cburch.logisim.util.LocaleManager;

public class ProjectExplorer extends JTree implements LocaleListener {
  private static final String DIRTY_MARKER = "*";
 
  static final Color MAGNIFYING_INTERIOR = new Color(200, 200, 255, 64);
 
  public static class Event {
    private TreePath path;
   
    private Event(TreePath path) {
      this.path = path;
    }
   
    public TreePath getTreePath() {
      return path;
    }
   
    public Object getTarget() {
      return path == null ? null : path.getLastPathComponent();
    }
  }
 
  public static interface Listener {
    public void selectionChanged(Event event);
    public void doubleClicked(Event event);
    public void moveRequested(Event event, AddTool dragged, AddTool target);
    public void deleteRequested(Event event);
    public JPopupMenu menuRequested(Event event);
  }

  private class MyModel implements TreeModel {
    ArrayList<TreeModelListener> listeners = new ArrayList<TreeModelListener>();

    public void addTreeModelListener(TreeModelListener l) {
      listeners.add(l);
    }
    public void removeTreeModelListener(TreeModelListener l) {
      listeners.remove(l);
    }
    public Object getRoot() {
      return proj.getLogisimFile();
    }
    private List<?> getChildren(Object parent) {
      if (parent == proj.getLogisimFile()) {
        return ((Library) parent).getElements();
      } else if (parent instanceof Library) {
        return ((Library) parent).getTools();
      } else {
        return Collections.EMPTY_LIST;
      }
    }
    public Object getChild(Object parent, int index) {
      return getChildren(parent).get(index);
    }
    public int getChildCount(Object parent) {
      return getChildren(parent).size();
    }
    public int getIndexOfChild(Object parent, Object query) {
      if (parent == null || query == null) return -1;
      int index = -1;
      for (Object child : getChildren(parent)) {
        index++;
        if (child == query) return index;
      }
      return -1;
    }
    public boolean isLeaf(Object node) {
      return node != proj && !(node instanceof Library);
    }
   
    public void valueForPathChanged(TreePath path, Object value) {
      TreeModelEvent e = new TreeModelEvent(ProjectExplorer.this, path);
      fireNodesChanged(Collections.singletonList(e));
    }
     
    private void fireNodesChanged(List<TreeModelEvent> events) {
      for (TreeModelEvent e : events) {
        for (TreeModelListener l : listeners) {
          l.treeNodesChanged(e);
        }
      }
    }

    void fireStructureChanged() {
      TreeModelEvent e = new TreeModelEvent(ProjectExplorer.this,
        new Object[] { model.getRoot() });
      for (TreeModelListener l : listeners) {
        l.treeStructureChanged(e);
      }
      ProjectExplorer.this.repaint();
    }

    private ArrayList<TreeModelEvent> findPaths(Object value) {
      ArrayList<TreeModelEvent> ret = new ArrayList<TreeModelEvent>();
      ArrayList<Object> stack = new ArrayList<Object>();
      findPathsSub(value, getRoot(), stack, ret);
      return ret;
    }

    private void findPathsSub(Object value, Object node,
        ArrayList<Object> stack, ArrayList<TreeModelEvent> paths) {
      stack.add(node);
      if (node == value) {
        TreePath path = new TreePath(stack.toArray());
        paths.add(new TreeModelEvent(ProjectExplorer.this, path));
      }
      for (Object child : getChildren(node)) {
        findPathsSub(value, child, stack, paths);
      }
      stack.remove(stack.size() - 1);
    }

    private ArrayList<TreeModelEvent> findPathsForTools(Library value) {
      ArrayList<TreeModelEvent> ret = new ArrayList<TreeModelEvent>();
      ArrayList<Object> stack = new ArrayList<Object>();
      findPathsForToolsSub(value, getRoot(), stack, ret);
      return ret;
    }

    private void findPathsForToolsSub(Library value, Object node,
        ArrayList<Object> stack, ArrayList<TreeModelEvent> paths) {
      stack.add(node);
      if (node == value) {
        TreePath path = new TreePath(stack.toArray());
        List<? extends Tool> toolList = value.getTools();
        int[] indices = new int[toolList.size()];
        Object[] tools = new Object[indices.length];
        for (int i = 0; i < indices.length; i++) {
          indices[i] = i;
          tools[i] = toolList.get(i);
        }
        paths.add(new TreeModelEvent(ProjectExplorer.this, path, indices, tools));
      }
      for (Object child : getChildren(node)) {
        findPathsForToolsSub(value, child, stack, paths);
      }
      stack.remove(stack.size() - 1);
    }
  }

  private class ToolIcon implements Icon {
    Tool tool;
    Circuit circ = null;

    ToolIcon(Tool tool) {
      this.tool = tool;
      if (tool instanceof AddTool) {
        ComponentFactory fact = ((AddTool) tool).getFactory(false);
        if (fact instanceof SubcircuitFactory) {
          circ = ((SubcircuitFactory) fact).getSubcircuit();
        }
      }
    }

    public int getIconHeight() {
      return 20;
    }

    public int getIconWidth() {
      return 20;
    }

    public void paintIcon(java.awt.Component c, Graphics g,
        int x, int y) {
      // draw halo if appropriate
      if (tool == haloedTool && AppPreferences.ATTRIBUTE_HALO.getBoolean()) {
        g.setColor(Canvas.HALO_COLOR);
        g.fillRoundRect(x, y, 20, 20, 10, 10);
        g.setColor(Color.BLACK);
      }

      // draw tool icon
      Graphics gIcon = g.create();
      ComponentDrawContext context = new ComponentDrawContext(ProjectExplorer.this, null, null, g, gIcon);
      tool.paintIcon(context, x, y);
      gIcon.dispose();

      // draw magnifying glass if appropriate
      if (circ == proj.getCurrentCircuit()) {
        int tx = x + 13;
        int ty = y + 13;
        int[] xp = { tx - 1, x + 18, x + 20, tx + 1 };
        int[] yp = { ty + 1, y + 20, y + 18, ty - 1 };
        g.setColor(MAGNIFYING_INTERIOR);
        g.fillOval(x + 5, y + 5, 10, 10);
        g.setColor(Color.BLACK);
        g.drawOval(x + 5, y + 5, 10, 10);
        g.fillPolygon(xp, yp, xp.length);
      }
    }
  }

  private class MyCellRenderer extends DefaultTreeCellRenderer {
    @Override
    public java.awt.Component getTreeCellRendererComponent(
        JTree tree, Object value, boolean selected,
        boolean expanded, boolean leaf, int row,
        boolean hasFocus) {
      java.awt.Component ret;
      ret = super.getTreeCellRendererComponent(tree, value,
        selected, expanded, leaf, row, hasFocus);

      if (ret instanceof JComponent) {
        JComponent comp = (JComponent) ret;
        comp.setToolTipText(null);
      }
      if (value instanceof Tool) {
        Tool tool = (Tool) value;
        if (ret instanceof JLabel) {
          ((JLabel) ret).setText(tool.getDisplayName());
          ((JLabel) ret).setIcon(new ToolIcon(tool));
          ((JLabel) ret).setToolTipText(tool.getDescription());
        }
      } else if (value instanceof Library) {
        if (ret instanceof JLabel) {
          Library lib = (Library) value;
          String text = lib.getDisplayName();
          if (lib.isDirty()) text += DIRTY_MARKER;
          ((JLabel) ret).setText(text);
        }
      }
      return ret;
    }
  }

  private class MySelectionModel extends DefaultTreeSelectionModel {
    @Override
    public void addSelectionPath(TreePath path) {
      if (isPathValid(path)) super.addSelectionPath(path);
    }

    @Override
    public void setSelectionPath(TreePath path) {
      if (isPathValid(path)) super.setSelectionPath(path);
    }

    @Override
    public void addSelectionPaths(TreePath[] paths) {
      paths = getValidPaths(paths);
      if (paths != null) super.addSelectionPaths(paths);
    }

    @Override
    public void setSelectionPaths(TreePath[] paths) {
      paths = getValidPaths(paths);
      if (paths != null) super.setSelectionPaths(paths);
    }

    private TreePath[] getValidPaths(TreePath[] paths) {
      int count = 0;
      for (int i = 0; i < paths.length; i++) {
        if (isPathValid(paths[i])) ++count;
      }
      if (count == 0) {
        return null;
      } else if (count == paths.length) {
        return paths;
      } else {
        TreePath[] ret = new TreePath[count];
        int j = 0;
        for (int i = 0; i < paths.length; i++) {
          if (isPathValid(paths[i])) ret[j++] = paths[i];
        }
        return ret;
      }
    }

    private boolean isPathValid(TreePath path) {
      if (path == null || path.getPathCount() > 3) return false;
      Object last = path.getLastPathComponent();
      return last instanceof Tool;
    }
  }
 
  private class DragController implements JTreeDragController {
    public boolean canPerformAction(JTree targetTree,
        Object draggedNode, int action, Point location) {
      TreePath pathTarget = targetTree.getPathForLocation(location.x, location.y);
      if (pathTarget == null) {
        targetTree.setSelectionPath(null);
        return false;
      }
      targetTree.setSelectionPath(pathTarget);
      if (action == DnDConstants.ACTION_COPY) {
        return false;
      } else if (action == DnDConstants.ACTION_MOVE) {
        Object targetNode = pathTarget.getLastPathComponent();
        return canMove(draggedNode, targetNode);
      } else {
        return false;
      }
    }

    public boolean executeDrop(JTree targetTree, Object draggedNode,
        Object targetNode, int action) {
      if (action == DnDConstants.ACTION_COPY) {
        return false;
      } else if (action == DnDConstants.ACTION_MOVE) {
        if (canMove(draggedNode, targetNode)) {
          if (draggedNode == targetNode) return true;
          listener.moveRequested(new Event(null), (AddTool) draggedNode, (AddTool) targetNode);
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    }

    private boolean canMove(Object draggedNode, Object targetNode) {
      if (listener == null) return false;
      if (!(draggedNode instanceof AddTool) || !(targetNode instanceof AddTool)) return false;
      LogisimFile file = proj.getLogisimFile();
      AddTool dragged = (AddTool) draggedNode;
      AddTool target = (AddTool) targetNode;
      int draggedIndex = file.getTools().indexOf(dragged);
      int targetIndex = file.getTools().indexOf(target);
      if (targetIndex < 0 || draggedIndex < 0) return false;
      return true;
    }
  }
 
  private class DeleteAction extends AbstractAction {
    public void actionPerformed(ActionEvent event) {
      TreePath path = getSelectionPath();
      if (listener != null && path != null && path.getPathCount() == 2) {
        listener.deleteRequested(new Event(path));
      }
      ProjectExplorer.this.requestFocus();
    }
  }

  private class MyListener
      implements MouseListener, TreeSelectionListener,
        ProjectListener, LibraryListener, CircuitListener, PropertyChangeListener {
    //
    // MouseListener methods
    //
    public void mouseEntered(MouseEvent e) { }
    public void mouseExited(MouseEvent e) { }
    public void mousePressed(MouseEvent e) {
      ProjectExplorer.this.requestFocus();
      checkForPopup(e);
    }
    public void mouseReleased(MouseEvent e) {
      checkForPopup(e);
    }
    private void checkForPopup(MouseEvent e) {
      if (e.isPopupTrigger()) {
        TreePath path = getPathForLocation(e.getX(), e.getY());
        if (path != null && listener != null) {
          JPopupMenu menu = listener.menuRequested(new Event(path));
          if (menu != null) {
            menu.show(ProjectExplorer.this, e.getX(), e.getY());
          }
        }
      }
    }
    public void mouseClicked(MouseEvent e) {
      if (e.getClickCount() == 2) {
        TreePath path = getPathForLocation(e.getX(), e.getY());
        if (path != null && listener != null) {
          listener.doubleClicked(new Event(path));
        }
      }
    }

    //
    // TreeSelectionListener methods
    //
    public void valueChanged(TreeSelectionEvent e) {
      TreePath path = e.getNewLeadSelectionPath();
      if (listener != null) {
        listener.selectionChanged(new Event(path));
      }
    }
   
    //
    // project/library file/circuit listener methods
    //
    public void projectChanged(ProjectEvent event) {
      int act = event.getAction();
      if (act == ProjectEvent.ACTION_SET_TOOL) {
        TreePath path = getSelectionPath();
        if (path != null && path.getLastPathComponent() != event.getTool()) {
          clearSelection();
        }
      } else if (act == ProjectEvent.ACTION_SET_FILE) {
        setFile(event.getLogisimFile());
      } else if (act == ProjectEvent.ACTION_SET_CURRENT) {
        ProjectExplorer.this.repaint();
      }
    }

    public void libraryChanged(LibraryEvent event) {
      int act = event.getAction();
      if (act == LibraryEvent.ADD_TOOL) {
        if (event.getData() instanceof AddTool) {
          AddTool tool = (AddTool) event.getData();
          if (tool.getFactory() instanceof SubcircuitFactory) {
            SubcircuitFactory fact = (SubcircuitFactory) tool.getFactory();
            fact.getSubcircuit().addCircuitListener(this);
          }
        }
      } else if (act == LibraryEvent.REMOVE_TOOL) {
        if (event.getData() instanceof AddTool) {
          AddTool tool = (AddTool) event.getData();
          if (tool.getFactory() instanceof SubcircuitFactory) {
            SubcircuitFactory fact = (SubcircuitFactory) tool.getFactory();
            fact.getSubcircuit().removeCircuitListener(this);
          }
        }
      } else if (act == LibraryEvent.ADD_LIBRARY) {
        if (event.getData() instanceof LibraryEventSource) {
          ((LibraryEventSource) event.getData()).addLibraryListener(subListener);
        }
      } else if (act == LibraryEvent.REMOVE_LIBRARY) {
        if (event.getData() instanceof LibraryEventSource) {
          ((LibraryEventSource) event.getData()).removeLibraryListener(subListener);
        }
      }
      Library lib = event.getSource();
      switch (act) {
      case LibraryEvent.DIRTY_STATE:
      case LibraryEvent.SET_NAME:
        model.fireNodesChanged(model.findPaths(lib));
        break;
      case LibraryEvent.MOVE_TOOL:
        model.fireNodesChanged(model.findPathsForTools(lib));
        break;
      case LibraryEvent.SET_MAIN:
        break;
      default:
        model.fireStructureChanged();
      }
    }

    public void circuitChanged(CircuitEvent event) {
      int act = event.getAction();
      if (act == CircuitEvent.ACTION_SET_NAME) {
        model.fireStructureChanged();
        // The following almost works - but the labels aren't made
        // bigger, so you get "..." behavior with longer names.
        // model.fireNodesChanged(model.findPaths(event.getCircuit()));
      }
    }

    private void setFile(LogisimFile lib) {
      model.fireStructureChanged();
      expandRow(0);

      for (Circuit circ : lib.getCircuits()) {
        circ.addCircuitListener(this);
      }
     
      subListener = new SubListener(); // create new one so that old listeners die away
      for (Library sublib : lib.getLibraries()) {
        if (sublib instanceof LibraryEventSource) {
          ((LibraryEventSource) sublib).addLibraryListener(subListener);
        }
      }
    }
   
    //
    // PropertyChangeListener methods
    //
    public void propertyChange(PropertyChangeEvent event) {
      if (AppPreferences.GATE_SHAPE.isSource(event)) {
        repaint();
      }
    }
  }
 
  private class SubListener implements LibraryListener {
    public void libraryChanged(LibraryEvent event) {
      model.fireStructureChanged();
    }
  }

  private Project proj;
  private MyListener myListener = new MyListener();
  private SubListener subListener = new SubListener();
  private MyModel model = new MyModel();
  private MyCellRenderer renderer = new MyCellRenderer();
  private DeleteAction deleteAction = new DeleteAction();
  private Listener listener = null;
  private Tool haloedTool = null;

  public ProjectExplorer(Project proj) {
    super();
    this.proj = proj;

    setModel(model);
    setRootVisible(true);
    addMouseListener(myListener);
    ToolTipManager.sharedInstance().registerComponent(this);

    MySelectionModel selector = new MySelectionModel();
    selector.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    setSelectionModel(selector);
    setCellRenderer(renderer);
    JTreeUtil.configureDragAndDrop(this, new DragController());
    addTreeSelectionListener(myListener);
   
    InputMap imap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), deleteAction);
    ActionMap amap = getActionMap();
    amap.put(deleteAction, deleteAction);

    proj.addProjectListener(myListener);
    proj.addLibraryListener(myListener);
    AppPreferences.GATE_SHAPE.addPropertyChangeListener(myListener);
    myListener.setFile(proj.getLogisimFile());
    LocaleManager.addLocaleListener(this);
  }
 
  public Tool getSelectedTool() {
    TreePath path = getSelectionPath();
    if (path == null) return null;
    Object last = path.getLastPathComponent();
    return last instanceof Tool ? (Tool) last : null;
  }
 
  public void setListener(Listener value) {
    listener = value;
  }

  public void setHaloedTool(Tool t) {
    if (haloedTool == t) return;
    haloedTool = t;
    repaint();
  }

  public void localeChanged() {
    model.fireStructureChanged();
  }
}
TOP

Related Classes of com.cburch.logisim.gui.main.ProjectExplorer$MyCellRenderer

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.