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.IdentityHashMap; 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.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import com.framsticks.core.Path; import com.framsticks.core.Tree; import com.framsticks.gui.tree.AbstractNode; import com.framsticks.gui.tree.MetaNode; import com.framsticks.gui.tree.TreeCellRenderer; import com.framsticks.gui.tree.TreeModel; import com.framsticks.gui.tree.TreeNode; import com.framsticks.util.dispatching.Dispatching; import com.framsticks.util.dispatching.FutureHandler; 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.lang.Casting; 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 = LogManager.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 TreeModel treeModel; protected MetaNode rootNode; protected JPanel treePanel; protected JPopupMenu treePopupMenu; protected JMenuItem treePopupMenuHeader; 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 EmptyPanel emptyPanel; protected final Map treeAtFrames = new IdentityHashMap<>(); 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); cardPanel = new JPanel(); cardPanel.setName("card"); cardPanelLayout = new CardLayout(); cardPanel.setLayout(cardPanelLayout); emptyPanel = new EmptyPanel(this); Container contentPane = getSwing().getContentPane(); treePopupMenu = new JPopupMenu("title"); treePopupMenu.setName("popup"); treePanel = new JPanel(); treePanel.setLayout(new BorderLayout()); rootNode = new MetaNode(this); rootNode.setName("root"); treeModel = new TreeModel(this); 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) { treeModel.chooseTreeNode(e.getNewLeadSelectionPath()); } }); jtree.addTreeExpansionListener(new TreeExpansionListener() { @Override public void treeCollapsed(TreeExpansionEvent e) { } @Override public void treeExpanded(TreeExpansionEvent e) { treeModel.expandTreeNode(e.getPath()); } }); jtree.setExpandsSelectedPaths(true); jtree.setEditable(false); 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(treeModel)); treeScrollPane = new JScrollPane(jtree); treeScrollPane.setBorder(BorderFactory.createEmptyBorder()); treePanel.add(treeScrollPane); 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); 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"); 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(); } }); new MenuConstructor(fileMenu).add(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.CTRL_MASK), new AbstractAction("Reload current") { @Override public void actionPerformed(ActionEvent actionEvent) { treeModel.loadPath(treeModel.convertToPath(jtree.getSelectionPath()), true); } }); } protected JPanel createLeftTopPanel() { return null; } public void addRootPath(final Path path) { assert isActive(); Tree tree = path.getTree(); log.debug("trying mount: {}", path); if (!tree.getAssignedRoot().isResolved()) { tree.get(path, new FutureHandler(this) { @Override protected void result(Path result) { addRootPath(result); } }); return; } assert browser.getTrees().contains(tree); TreeAtFrame e = new TreeAtFrame(tree, this); treeAtFrames.put(tree, e); Path rootPath = Path.to(tree, "/"); rootNode.getChildren().add(new TreeNode(e, rootPath)); e.rootNode = tree.getAssignedRoot(); treeModel.treeStructureChanged(new TreePath(rootNode)); // jtree.expandPath(new TreePath(rootNode)); } public void showPanel(AbstractPanel panel) { assert isActive(); assert panel != null; log.debug("showing panel: {}", panel); cardPanelLayout.show(cardPanel, panel.getUniqueName()); // cardPanel.revalidate(); } private void showPopup(MouseEvent e) { assert isActive(); if (!e.isPopupTrigger()) { return; } TreePath treePath = jtree.getPathForLocation(e.getX(), e.getY()); Path path = treeModel.convertToPath(treePath); if (path == null) { return; } treePopupMenu.removeAll(); for (PopupMenuEntryProvider provider : browser.popupMenuEntryProviders) { provider.provide(treePopupMenu, path); } treePopupMenu.show(e.getComponent(), e.getX(), e.getY()); } public void clear() { cardPanel.removeAll(); cardPanel.updateUI(); jtree.setEnabled(false); } public void updatePanelIfIsLeadSelection(Path path) { assert isActive(); TreePath treePath = treeModel.convertToTreePath(path, true); if (treePath == null) { return; } if (treePath.equals(jtree.getSelectionPath())) { log.debug("updating: {} -> {}", treePath, path); showPanelForTreePath(treePath); } } public void showPanelForTreePath(TreePath treePath) { assert isActive(); AbstractNode node = Casting.assertCast(AbstractNode.class, treePath.getLastPathComponent()); AbstractPanel panel = node.getPanel(); if (panel == null) { log.error("no panel for {} found", treePath); return; } panel.fillPanelWith(node); showPanel(panel); // cardPanel.revalidate(); } // public void goTo(Path path) { // assert isActive(); // final TreePath treePath = treeModel.convertToTreePath(path); // this.dispatch(new RunAt(this) { // @Override // protected void runAt() { // log.info("executed"); // jtree.setSelectionPath(treePath); // jtree.makeVisible(treePath); // assert jtree.isVisible(treePath); // } // }); // } @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); } } /** * @return the jtree */ public JTree getJtree() { return jtree; } /** * @return the treeModel */ public TreeModel getTreeModel() { return treeModel; } /** * @return the rootNode */ public MetaNode getRootNode() { return rootNode; } /** * @return the emptyPanel */ public EmptyPanel getEmptyPanel() { return emptyPanel; } /** * @return the treeAtFrames */ public Map getTreeAtFrames() { return treeAtFrames; } }