package com.framsticks.gui; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.HashMap; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.ToolTipManager; import javax.swing.UIManager; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import org.apache.log4j.Logger; import com.framsticks.core.Path; import com.framsticks.core.Tree; import com.framsticks.gui.view.TreeCellRenderer; import com.framsticks.util.FramsticksException; import com.framsticks.util.dispatching.Dispatching; import com.framsticks.util.dispatching.ExceptionResultHandler; import com.framsticks.util.dispatching.Joinable; import com.framsticks.util.dispatching.JoinableCollection; import com.framsticks.util.dispatching.JoinableParent; import com.framsticks.util.dispatching.JoinableState; import com.framsticks.util.dispatching.RunAt; import com.framsticks.util.lang.ScopeEnd; import com.framsticks.util.swing.KeyboardModifier; import com.framsticks.util.swing.MenuConstructor; /** * @author Piotr Sniegowski */ @SuppressWarnings("serial") public class Frame extends FrameJoinable implements JoinableParent { private static final Logger log = Logger.getLogger(Frame.class.getName()); protected final Browser browser; protected final Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); protected CardLayout cardPanelLayout; protected JPanel cardPanel; protected JScrollPane treeScrollPane; protected JTree jtree; protected DefaultTreeModel treeModel; protected javax.swing.tree.MutableTreeNode rootNode; protected JPanel treePanel; protected JPopupMenu treePopupMenu; protected JMenuItem treePopupMenuHeader; TreeNode currentlyPoppedTreeNode; protected JPanel mainPanel; protected JPanel leftPanel; protected JPanel normalWorkPanel; protected CardLayout mainPanelLayout; protected JMenuBar menuBar; protected JMenu fileMenu; protected JMenu editMenu; protected JMenu viewMenu; protected JMenu windowMenu; protected JMenu helpMenu; protected final Map treeAtFrames = new HashMap(); protected JoinableCollection trees = new JoinableCollection(); public Frame(Browser browser) { this.browser = browser; } protected void initializeGui() { super.initializeGui(); /** this is done to remove the current value label from above the slider, * because it cannot put to work properly with floating-point value sliders, * nor it can be removed in normal way through JSlider methods */ UIManager.put("Slider.paintValue", false); log.debug("creating " + this); Container contentPane = getSwing().getContentPane(); treePopupMenu = new JPopupMenu("title"); treePopupMenu.setName("popup"); treePanel = new JPanel(); treePanel.setLayout(new BorderLayout()); treeModel = new DefaultTreeModel(null); treeModel.addTreeModelListener(new TreeModelListener() { @Override public void treeNodesChanged(TreeModelEvent arg0) { log.trace("treeNodesChanged: " + arg0); } @Override public void treeNodesInserted(TreeModelEvent arg0) { // log.trace("treeNodesInserted: " + arg0); } @Override public void treeNodesRemoved(TreeModelEvent arg0) { log.trace("treeNodesRemoved: " + arg0); } @Override public void treeStructureChanged(TreeModelEvent arg0) { log.trace("treeStructureChanged: " + arg0); } }); jtree = new JTree(treeModel); jtree.setName("tree"); jtree.setRootVisible(false); jtree.setExpandsSelectedPaths(true); jtree.setSelectionModel(new DefaultTreeSelectionModel()); ToolTipManager.sharedInstance().registerComponent(jtree); jtree.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { chooseTreeNode(e.getNewLeadSelectionPath()); } }); jtree.setExpandsSelectedPaths(true); jtree.setEditable(false); jtree.setDoubleBuffered(true); jtree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); jtree.setShowsRootHandles(true); jtree.setRowHeight(26); jtree.setDoubleBuffered(true); jtree.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { assert isActive(); showPopup(e); } @Override public void mouseReleased(MouseEvent e) { assert isActive(); showPopup(e); } }); new KeyboardModifier(jtree, JComponent.WHEN_FOCUSED).join(KeyStroke.getKeyStroke('h'), KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)).join(KeyStroke.getKeyStroke('j'), KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)).join(KeyStroke.getKeyStroke('k'), KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0)).join(KeyStroke.getKeyStroke('l'), KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0)); jtree.setCellRenderer(new TreeCellRenderer()); treeScrollPane = new JScrollPane(jtree); treeScrollPane.setBorder(BorderFactory.createEmptyBorder()); treePanel.add(treeScrollPane); rootNode = new DefaultMutableTreeNode(); rootNode.setUserObject("root"); treeModel.setRoot(rootNode); normalWorkPanel = new JPanel(); normalWorkPanel.setLayout(new BorderLayout()); normalWorkPanel.setName("browser"); mainPanel = new JPanel(); mainPanel.setName("main"); mainPanelLayout = new CardLayout(); mainPanel.setLayout(mainPanelLayout); mainPanel.add(normalWorkPanel, "browser"); menuBar = new JMenuBar(); fileMenu = menuBar.add(new JMenu("File")); editMenu = menuBar.add(new JMenu("Edit")); viewMenu = menuBar.add(new JMenu("View")); windowMenu = menuBar.add(new JMenu("Window")); helpMenu = menuBar.add(new JMenu("Help")); contentPane.add(menuBar, BorderLayout.NORTH); contentPane.add(mainPanel, BorderLayout.CENTER); leftPanel = new JPanel(); leftPanel.setLayout(new BorderLayout()); //leftPanel.add(new ViewerTest(), BorderLayout.PAGE_START); // JPanel leftTopPanel = createLeftTopPanel(); // if (leftTopPanel != null) { // leftPanel.add(leftTopPanel, BorderLayout.PAGE_START); // } leftPanel.add(treePanel, BorderLayout.CENTER); leftPanel.setBackground(Color.WHITE); leftPanel.setForeground(Color.WHITE); cardPanel = new JPanel(); cardPanel.setName("card"); JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, cardPanel); split.setPreferredSize(browser.defaultFrameDimension); split.setMaximumSize(screenDimension); split.setOneTouchExpandable(true); split.setDividerLocation(250); split.setDividerSize(5); split.setName("split"); normalWorkPanel.add(split); //this.setVisible(true); mainPanelLayout.show(mainPanel, "browser"); cardPanelLayout = new CardLayout(); cardPanel.setLayout(cardPanelLayout); getSwing().pack(); jtree.requestFocusInWindow(); log.debug("frame configured: " + this); new MenuConstructor(fileMenu).add(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK), new AbstractAction("Close") { @Override public void actionPerformed(ActionEvent actionEvent) { interrupt(); } }); } protected JPanel createLeftTopPanel() { return null; } public void addRootPath(Path path) { assert isActive(); Tree tree = path.getTree(); assert browser.getTrees().contains(tree); TreeAtFrame e = new TreeAtFrame(tree, this); tree.addListener(e); treeAtFrames.put(tree, e); TreeNode node = new TreeNode(e, path); e.rootTreeNode = node; treeModel.insertNodeInto(node, rootNode, rootNode.getChildCount()); jtree.expandPath(new TreePath(rootNode)); } public void showPanel(Panel panel) { assert isActive(); assert panel != null; cardPanelLayout.show(cardPanel, panel.getUniqueName()); } private void showPopup(MouseEvent e) { assert isActive(); if (!e.isPopupTrigger()) { return; } currentlyPoppedTreeNode = findTreeNodeByTreePath(jtree.getPathForLocation(e.getX(), e.getY())); if (currentlyPoppedTreeNode == null) { return; } Path path = currentlyPoppedTreeNode.getTreePath(); treePopupMenu.removeAll(); for (PopupMenuEntryProvider provider : browser.popupMenuEntryProviders) { provider.provide(treePopupMenu, path); } // treePopupMenuHeader.setText(path.getTree().getName() + path.getTextual()); treePopupMenu.show(e.getComponent(), e.getX(), e.getY()); //currentlyPoppedPanel.getNode().getFramsClass().getName() } public TreeNode getCurrentlyPoppedTreeNode() { assert isActive(); return currentlyPoppedTreeNode; } public void clear() { treeModel.setRoot(null); cardPanel.removeAll(); cardPanel.updateUI(); jtree.setEnabled(false); } public ScopeEnd startChange(final DefaultMutableTreeNode node) { assert isActive(); final TreePath selection = jtree.getSelectionPath(); return new ScopeEnd() { @Override public void close() { assert isActive(); treeModel.nodeChanged(node); jtree.setSelectionPath(selection); } }; } public void selectTreeNode(final TreeNode treeNode) { assert isActive(); /* final Panel panel = treeNode.getOrCreatePanel(); if (panel == null) { return; } panel.setCurrentTreeNode(treeNode); treeNode.updateData(); showPanel(panel);*/ } public TreeNode findTreeNodeByTreePath(TreePath treePath) { assert isActive(); if (treePath == null) { return null; } if (!(treePath.getLastPathComponent() instanceof TreeNode)) { return null; } return (TreeNode) treePath.getLastPathComponent(); } public void chooseTreeNode(TreePath treePath) { assert isActive(); final TreeNode treeNode = findTreeNodeByTreePath(treePath); if (treeNode == null) { return; } treeNode.select(); } protected final ExceptionResultHandler dialogHandler = new ExceptionResultHandler() { @Override public void handle(FramsticksException exception) { //TODO TEH throw exception; } }; public void goTo(Path path) { assert isActive(); final TreePath treePath = treeAtFrames.get(path.getTree()).getTreePath(path, false); log.info("go to path: " + path + "(" + treePath + ")"); this.dispatch(new RunAt(dialogHandler) { @Override protected void runAt() { log.info("executed"); jtree.setSelectionPath(treePath); jtree.makeVisible(treePath); assert jtree.isVisible(treePath); } }); } public void addNode(TreeNode child, DefaultMutableTreeNode parent) { assert isActive(); try (ScopeEnd e = startChange(parent)) { treeModel.insertNodeInto(child, parent, parent.getChildCount()); } } @Override public String toString() { return title + "@" + browser.getName(); } @Override protected void joinableStart() { super.joinableStart(); Dispatching.use(trees, this); } @Override protected void joinableInterrupt() { Dispatching.drop(trees, this); super.joinableInterrupt(); } @Override protected void joinableFinish() { super.joinableFinish(); } @Override protected void joinableJoin() throws InterruptedException { Dispatching.join(trees); super.joinableJoin(); } @Override public void childChangedState(Joinable joinable, JoinableState state) { if (joinable == trees) { proceedToState(state); } } }