package com.framsticks.gui; import com.framsticks.core.Entity; import com.framsticks.core.Instance; import com.framsticks.core.Path; import com.framsticks.gui.view.*; import com.framsticks.gui.view.TreeCellRenderer; import com.framsticks.util.dispatching.Dispatcher; import com.framsticks.util.dispatching.JoinableCollection; import com.framsticks.util.lang.ScopeEnd; import com.framsticks.util.swing.KeyboardModifier; import com.framsticks.util.swing.MenuConstructor; import org.apache.log4j.Logger; import javax.swing.*; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.*; import java.awt.*; import java.awt.datatransfer.StringSelection; import java.awt.event.*; import java.util.HashMap; import java.util.Map; import com.framsticks.util.dispatching.RunAt; /** * @author Piotr Sniegowski */ @SuppressWarnings("serial") public class Frame extends JoinableCollection implements Entity, Dispatcher { 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 final String title; protected JFrame swing; protected JScrollPane treeScrollPane; protected JTree tree; protected DefaultTreeModel treeModel; protected javax.swing.tree.MutableTreeNode rootNode; //final Instance instance; protected JPanel treePanel; protected JPopupMenu treePopupMenu; protected JMenuItem treePopupMenuHeader; TreeNode currentlyPoppedTreeNode; protected JLabel statusBar; 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 instancesAtFrames = new HashMap(); public Frame(String title, Browser browser) { this.title = title; this.browser = browser; } public void configure() { swing = new JFrame(title); swing.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); swing.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { log.info("received closing"); joinableFinish(); } }); /** 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); statusBar = new JLabel("not connected"); Container contentPane = swing.getContentPane(); contentPane.setLayout(new BorderLayout()); treePopupMenu = new JPopupMenu("title"); treePopupMenu.setName("popup"); treePopupMenuHeader = new JMenuItem(); treePopupMenuHeader.setForeground(Color.BLUE); treePopupMenu.add(treePopupMenuHeader); treePopupMenu.addSeparator(); //TODO: add to favourites //TODO: remove from favourites //TODO: open in new window as root //TODO: refresh //TODO: open in console treePopupMenu.add(new JMenuItem("Refresh")); treePopupMenu.add(new JMenuItem("Open in new frame as root")); addNodeActionToTreePopupMenu("Copy path to clipboard", new NodeAction() { @Override public void actionPerformed(TreeNode treeNode) { Path path = treeNode.getInstancePath(); StringSelection selection = new StringSelection(path.toString()); swing.getToolkit().getSystemClipboard().setContents(selection, selection); } }); //this.add(createMenuItem("Add to favourites", null)); //this.add(createMenuItem("Remove from favourites", null)); 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); } }); tree = new JTree(treeModel); tree.setName("tree"); tree.setRootVisible(false); tree.setExpandsSelectedPaths(true); tree.setSelectionModel(new DefaultTreeSelectionModel()); ToolTipManager.sharedInstance().registerComponent(tree); tree.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { chooseTreeNode(e.getNewLeadSelectionPath()); } }); tree.setExpandsSelectedPaths(true); tree.setEditable(false); tree.setDoubleBuffered(true); tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); tree.setShowsRootHandles(true); tree.setRowHeight(26); tree.setDoubleBuffered(true); tree.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(tree, 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)); tree.setCellRenderer(new TreeCellRenderer()); treeScrollPane = new JScrollPane(tree); 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); contentPane.add(statusBar, BorderLayout.SOUTH); 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); swing.pack(); tree.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(); Instance instance = path.getInstance(); assert browser.getInstances().containsValue(instance); InstanceAtFrame e = new InstanceAtFrame(instance, this); instance.addListener(e); instancesAtFrames.put(instance, e); TreeNode node = new TreeNode(e, path); e.rootTreeNode = node; treeModel.insertNodeInto(node, rootNode, rootNode.getChildCount()); tree.expandPath(new TreePath(rootNode)); } @Override public boolean isActive() { return SwingDispatcher.instance.isActive(); } @Override public void invokeLater(RunAt runnable) { SwingDispatcher.getInstance().invokeLater(runnable); } public Action addActionToTreePopupMenu(Action action) { assert isActive(); treePopupMenu.add(action); return action; } public Action addNodeActionToTreePopupMenu(String title, final NodeAction nodeAction) { assert isActive(); return addActionToTreePopupMenu(new AbstractAction(title) { @Override public void actionPerformed(ActionEvent e) { TreeNode treeNode = getCurrentlyPoppedTreeNode(); if (treeNode == null) { return; } nodeAction.actionPerformed(treeNode); } }); } 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(tree.getPathForLocation(e.getX(), e.getY())); if (currentlyPoppedTreeNode == null) { return; } treePopupMenu.show(e.getComponent(), e.getX(), e.getY()); //currentlyPoppedPanel.getNode().getFramsClass().getName() //treePopupMenuHeader.setText(currentlyPoppedTreeNode.getNode().getPath()); } /** * @return the swing */ public JFrame getSwing() { return swing; } public TreeNode getCurrentlyPoppedTreeNode() { assert isActive(); return currentlyPoppedTreeNode; } public void clear() { treeModel.setRoot(null); cardPanel.removeAll(); cardPanel.updateUI(); tree.setEnabled(false); } public ScopeEnd startChange(final DefaultMutableTreeNode node) { assert isActive(); final TreePath selection = tree.getSelectionPath(); return new ScopeEnd() { @Override public void close() { assert isActive(); treeModel.nodeChanged(node); tree.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(); } public void goTo(Path path) { assert isActive(); final TreePath treePath = instancesAtFrames.get(path.getInstance()).getTreePath(path, false); log.info("go to path: " + path + "(" + treePath + ")"); new RunAt(this) { @Override public void run() { log.info("executed"); tree.setSelectionPath(treePath); tree.makeVisible(treePath); assert tree.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 joinableInterrupt() { assert isActive(); super.joinableInterrupt(); invokeLater(new RunAt() { @Override public void run() { finish(); } }); } @Override protected void joinableFinish() { assert isActive(); log.debug("disposing frame " + this); swing.dispose(); } // @Override // public boolean isDone() { // return super.isDone() && !swing.isDisplayable(); // } @Override public String getName() { return title; } }