package org.openswing.swing.mdi.client; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.tree.*; import org.openswing.swing.client.*; import org.openswing.swing.mdi.java.*; import org.openswing.swing.util.client.*; import org.openswing.swing.tree.java.OpenSwingTreeNode; import java.util.Enumeration; import org.openswing.swing.logger.client.Logger; /** * <p>Title: OpenSwing Framework</p> * <p>Description: Panel that contains the application functions, as a tree (in the MDI Frame). * </p> * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p> * * <p> This file is part of OpenSwing Framework. * This library is free software; you can redistribute it and/or * modify it under the terms of the (LGPL) Lesser General Public * License as published by the Free Software Foundation; * * GNU LESSER GENERAL PUBLIC LICENSE * Version 2.1, February 1999 * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * The author may be contacted at: * maurocarniel@tin.it</p> * * @author Mauro Carniel * @version 1.0 */ public class TreeMenu extends JPanel { private BorderLayout borderLayout1 = new BorderLayout(); private Tree menuTree = new Tree(); private JScrollPane treeScrollPane = new JScrollPane(menuTree); private JPanel titlePanel = new JPanel(); private JLabel titleLabel = new JLabel(); private ImagePanel lockPanel; private Image lockImage; private Image unlockImage; private Image findImage; private boolean locked; private GridBagLayout gridBagLayout1 = new GridBagLayout(); JLabel findLabel = new JLabel(); JTextField findTF = new JTextField(); /** tree nodes path used to find a function */ private TreePath currentPath = null; /** * Constructor invoked by the MDIFrame. * @param functions DefaultTreeModel object that MUST contains ApplicationFunction objects */ public TreeMenu(DefaultTreeModel functions) { createTree(functions); setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); try { jbInit(); MouseListener ml = new MouseAdapter() { public void mouseClicked(MouseEvent e) { if(e.getClickCount() == 2) treeDoubleClick(e); } }; KeyListener kl = new java.awt.event.KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyCode()==KeyEvent.VK_SPACE) treeKeyPressed(e); } }; menuTree.addMouseListener(ml); menuTree.addKeyListener(kl); } catch(Exception e) { e.printStackTrace(); } } public void setEnabled(boolean enabled) { menuTree.setEnabled(enabled); } private void jbInit() throws Exception { lockImage = ClientUtils.getImage(ClientSettings.LOCK_ON); unlockImage = ClientUtils.getImage(ClientSettings.LOCK_OFF); ImagePanel findFunction = null; if (ClientSettings.FIND_FUNCTION_ICON!=null) { findImage = ClientUtils.getImage(ClientSettings.FIND_FUNCTION_ICON); findFunction = new ImagePanel(); findFunction.setImage(findImage); findFunction.setMinimumSize(new Dimension(25,25)); findFunction.setScrollBarsPolicy(lockPanel.SCROLLBAR_NEVER); } lockPanel = new ImagePanel(); lockPanel.setImage(lockImage); lockPanel.setMinimumSize(new Dimension(25,25)); lockPanel.setScrollBarsPolicy(lockPanel.SCROLLBAR_NEVER); lockPanel.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { lockPanel_mouseClicked(e); } }); setLocked(true); this.setLayout(borderLayout1); titleLabel.setFont(new Font(titleLabel.getFont().getFontName(), Font.BOLD, titleLabel.getFont().getSize())); titleLabel.setText(ClientSettings.getInstance().getResources().getResource("Functions")); titleLabel.setPreferredSize(new Dimension(100,25)); titlePanel.setLayout(gridBagLayout1); findLabel.setText(ClientSettings.getInstance().getResources().getResource("Find Function")); findTF.setText(""); findTF.setToolTipText(ClientSettings.getInstance().getResources().getResource("Press ENTER to find function")); findTF.setColumns(15); findTF.addKeyListener(new java.awt.event.KeyAdapter() { public void keyTyped(KeyEvent e) { findTF_keyTyped(e); } }); findTF.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { findTF_actionPerformed(e); } }); if (ClientSettings.SHOW_FUNCTIONS_LABEL) titlePanel.add(titleLabel, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0 ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0)); if (ClientSettings.SHOW_PADLOCK_IN_TREE_MENU) titlePanel.add(lockPanel, new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0 ,GridBagConstraints.EAST, GridBagConstraints.VERTICAL, new Insets(5, 10, 0, 10), 0, 0)); int pos = 0; if (findFunction!=null) titlePanel.add(findFunction, new GridBagConstraints(pos++, 0, 1, 1, 0.0, 0.0 ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0)); if (ClientSettings.SHOW_FIND_FUNCTION_LABEL) titlePanel.add(findLabel, new GridBagConstraints(pos++, 0, 1, 1, 0.0, 0.0 ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0)); titlePanel.add( findTF, new GridBagConstraints(pos++, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, ClientSettings.FILL_FIND_FUNCTION_FIELD ? GridBagConstraints.HORIZONTAL : GridBagConstraints.NONE, new Insets(5, 0, 5, 5), 100, 0) ); this.add(treeScrollPane, BorderLayout.CENTER); this.add(titlePanel, BorderLayout.NORTH); findTF.requestFocus(); } void lockPanel_mouseClicked(MouseEvent e) { setLocked(! locked); } public boolean isLocked() { return(locked); } public void setLocked(boolean value) { this.locked = value; if (locked) lockPanel.setImage(lockImage); else lockPanel.setImage(unlockImage); } /** * Create the tree, baed on the DefaultTreeModel object. * @param functions ApplicationFunctions objects, organized as a tree */ private void createTree(DefaultTreeModel functions) { try { DefaultMutableTreeNode oldRoot = (DefaultMutableTreeNode)functions.getRoot(); DefaultMutableTreeNode newRoot = null; if (oldRoot instanceof OpenSwingTreeNode) newRoot = new OpenSwingTreeNode(); else if (oldRoot instanceof ApplicationFunction) newRoot = new ApplicationFunction(); else newRoot = new DefaultMutableTreeNode(); newRoot.setUserObject(oldRoot.getUserObject()); copyChildNodes(oldRoot,newRoot); DefaultTreeModel treeModel = new DefaultTreeModel(newRoot); // associo un renderer per le icone dei nodi dell'albero... menuTree.setModel(treeModel); // menuTree.setModel(functions); menuTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); if (!ClientSettings.SHOW_TREE_MENU_ROOT) menuTree.setRootVisible(false); TreeNodeRenderer renderer = new TreeNodeRenderer(menuTree); menuTree.setCellRenderer(renderer); menuTree.revalidate(); menuTree.setToolTipText(""); if (ClientSettings.AUTO_EXPAND_TREE_MENU) expandAllNodes(); else if (ClientSettings.AUTO_EXPAND_SUBTREE_MENU!=null) expandNodes(ClientSettings.getInstance().getResources().getResource(ClientSettings.AUTO_EXPAND_SUBTREE_MENU)); } catch (Throwable ex) { ex.printStackTrace(); } } /** * This method duplicates tree model retrieved from MDIController.getApplicationFunctions() method * and remove nodes that are menu item separators. * @param oldNode current node to duplicate * @param newNnode new node (duplicated) */ private void copyChildNodes(DefaultMutableTreeNode oldNode,DefaultMutableTreeNode newNode) { ApplicationFunction oldChildNode = null; ApplicationFunction newChildNode = null; for(int i=0;i<oldNode.getChildCount();i++) { oldChildNode = (ApplicationFunction)oldNode.getChildAt(i); if (oldChildNode.isSeparator()) continue; if (oldChildNode.isFolder()) { newChildNode = new ApplicationFunction( oldChildNode.getDescription(), oldChildNode.getIconName(), oldChildNode.getTooltipText() ); } else newChildNode = new ApplicationFunction( oldChildNode.getDescription(), oldChildNode.getFunctionId(), oldChildNode.getIconName(), oldChildNode.getMethodName(), oldChildNode.getTooltipText() ); newNode.add(newChildNode); copyChildNodes(oldChildNode,newChildNode); } } /** * Expand subtree whose root is identified by the specified description. * @param nodeDescription description of root node whose subtree has to be expanded */ public final void expandNodes(String nodeDescription) { DefaultMutableTreeNode node = findNode((DefaultMutableTreeNode)menuTree.getModel().getRoot(),nodeDescription); if (node!=null) { menuTree.expandPath(new TreePath(node.getPath())); Enumeration en = node.depthFirstEnumeration(); while(en.hasMoreElements()) { node = (DefaultMutableTreeNode)en.nextElement(); menuTree.expandPath(new TreePath(node.getPath())); } } else Logger.warn(this.getClass().getName(),"expandNodes","There is not a node having the specified description '"+nodeDescription+"'"); } /** * @param node node whose subtree must be analyzed * @param nodeDescription description of the node to find * @return node having the specified description and that is contained in the subtree identified by the specified node */ public DefaultMutableTreeNode findNode(DefaultMutableTreeNode node,String nodeDescription) { if (node.toString().equals(nodeDescription)) return node; for(int i=0;i<node.getChildCount();i++) if (findNode((DefaultMutableTreeNode)node.getChildAt(i),nodeDescription)!=null) return (DefaultMutableTreeNode)node.getChildAt(i); return null; } /** * Expand all tree nodes. */ public final void expandAllNodes() { int i = 0; try { while (i < menuTree.getRowCount()) { menuTree.expandRow(i++); } } catch (Exception ex) { } } /** * Collapse all tree nodes. */ public final void collapseAllNodes() { int i = menuTree.getRowCount()-1; while (i >0) menuTree.collapseRow(i--); } /** * Method called when the user double clicks the selected tree node: it calls executeFunction method. */ private void treeDoubleClick(MouseEvent e) { try { javax.swing.tree.TreePath selPath = menuTree.getPathForLocation(e.getX(), e.getY()); if (selPath!=null) { Object objNode = selPath.getPathComponent(selPath.getPathCount()-1); ApplicationFunction node = null; if (objNode!=null && objNode instanceof ApplicationFunction) node = (ApplicationFunction)objNode; if (node!=null && node.getFunctionId()!=null) executeFunction(node); } } catch (Exception ex) { ex.printStackTrace(); } } /** * Method called when the user press SPACE key in the selected tree node: it calls executeFunction method. */ private void treeKeyPressed(KeyEvent e) { try { javax.swing.tree.TreePath selPath = menuTree.getPathForRow(menuTree.getSelectionRows()[0]); Object objNode = selPath.getPathComponent(selPath.getPathCount()-1); ApplicationFunction node = null; if (objNode!=null && objNode instanceof ApplicationFunction) node = (ApplicationFunction)objNode; if (node!=null) executeFunction(node); } catch (Exception ex) { ex.printStackTrace(); } } /** * Method called by treeKeyPressed or treeDoubleClick method: execute the corrisponding method in the ClientFacade. */ private void executeFunction(final ApplicationFunction node) { if (node.isFolder()) return; if (node.isSeparator()) return; ClientUtils.fireBusyEvent(true); SwingUtilities.invokeLater(new Runnable() { public void run() { try { try { MDIFrame.getClientFacade().getClass().getMethod(node.getMethodName(),new Class[0]).invoke(MDIFrame.getClientFacade(),new Object[0]); ClientUtils.fireBusyEvent(false); } catch (NoSuchMethodException ex1) { MDIFrame.getClientFacade().getClass().getMethod(node.getMethodName(),new Class[]{String.class}).invoke(MDIFrame.getClientFacade(),new Object[]{node.getFunctionId()}); ClientUtils.fireBusyEvent(false); } } catch (Throwable ex) { ClientUtils.fireBusyEvent(false); ex.printStackTrace(); OptionPane.showMessageDialog( ClientUtils.getParentWindow(TreeMenu.this), ClientSettings.getInstance().getResources().getResource("Error while executing function")+" '"+node.getMethodName()+"'", ClientSettings.getInstance().getResources().getResource("Error"), JOptionPane.WARNING_MESSAGE ); } } }); } void findTF_actionPerformed(ActionEvent e) { if (currentPath==null) currentPath = new TreePath(new Object[]{(TreeNode)menuTree.getModel().getRoot()}); TreeNode currentNode = (TreeNode)currentPath.getLastPathComponent(); for(int i=0;i<currentNode.getChildCount();i++) if (findNextFunction(currentPath,currentNode.getChildAt(i),findTF.getText().toUpperCase().trim())) return; try { while (currentNode.getParent() != null) { Object[] newTreePath = new Object[currentPath.getPathCount()-1]; System.arraycopy(currentPath.getPath(),0,newTreePath,0,currentPath.getPathCount()-1); currentPath = new TreePath(newTreePath); for (int i = currentNode.getParent().getIndex(currentNode)+1;i<currentNode.getParent().getChildCount(); i++) { if (findNextFunction(currentPath, currentNode.getParent().getChildAt(i),findTF.getText().toUpperCase().trim())) { return; } } currentNode = currentNode.getParent(); } } catch (Exception ex) { } if (!currentPath.getLastPathComponent().equals(menuTree.getModel().getRoot())) { currentPath = new TreePath(new Object[] { (TreeNode) menuTree.getModel().getRoot()}); findTF_actionPerformed(null); } } /** * Find the next function in the tree menu, that matches the specified pattern. * @param thePath current tree nodes path * @param currentNode node to analyze and to expand * @param pattern pattern used to match functions * @return <code>true</code> if a funciton is found, <code>false</code> otherwise */ private boolean findNextFunction(TreePath thePath,TreeNode currentNode,String pattern) { Object[] newTreePath = new Object[thePath.getPathCount()+1]; System.arraycopy(thePath.getPath(),0,newTreePath,0,thePath.getPathCount()); newTreePath[newTreePath.length-1] = currentNode; TreePath path = new TreePath(newTreePath); if (currentNode.toString().toUpperCase().indexOf(pattern)!=-1) { menuTree.setSelectionPath(path); menuTree.scrollPathToVisible(path); currentPath = path; return true; } for(int i=0;i<currentNode.getChildCount();i++) if (findNextFunction(path,currentNode.getChildAt(i),pattern)) return true; return false; } void findTF_keyTyped(KeyEvent e) { if (e.getKeyChar()=='\n' && e.isControlDown() && menuTree.getSelectionPath()!=null) treeKeyPressed(null); } public JTextField getFindTF() { return findTF; } }