package org.wordcorr.gui; import java.awt.Component; import java.awt.Dimension; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.tree.*; import org.wordcorr.db.*; import org.wordcorr.gui.action.ShowTree; import org.wordcorr.gui.input.InputTable; import org.wordcorr.gui.tree.*; /** * Pane that holds information about a database. * @author Keith Hamasaki, Jim Shiba **/ public final class DatabasePane extends JSplitPane implements Refreshable { private static final JLabel EMPTY_LABEL = new JLabel(""); /** * Constructor. **/ DatabasePane(Database db) throws DatabaseException { _db = db; setRightComponent(EMPTY_LABEL); dbTree = new DatabaseTree(); JScrollPane scrollPane = new JScrollPane(dbTree); scrollPane.setMinimumSize(new Dimension(0,0)); setLeftComponent(scrollPane); addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { AppPrefs.getInstance().setProperty(AppPrefs.DIVIDER_LOCATION, String.valueOf(getDividerLocation())); ShowTree.getInstance().setLabel(); } }); } /** * Refresh this pane. **/ public void refresh() throws DatabaseException { // Refreshing the LeftComponent (DatabaseTree) rebuilds all tree nodes // (User, Collection), triggering a reset of the selected pane. // The current pane is saved and later restored after the refresh to fix this. int lastPane = AppPrefs.getInstance().getIntProperty(AppPrefs.LAST_PANE, 0); dbTree.refresh(); Object o = getRightComponent(); if (o instanceof Refreshable) { ((Refreshable) o).refresh(); } // set current pane if (o instanceof JTabbedPane) ((JTabbedPane) o).setSelectedIndex(lastPane); } /** * Insert a new user. **/ public void addUser(User user) throws DatabaseException { dbTree.addUser(user); } /** * Insert a new collection. **/ public void addCollection(WordCollection collection) throws DatabaseException { dbTree.addCollection(collection); } /** * Get the current user. **/ public User getCurrentUser() { UserNode node = dbTree.getCurrentUser(); return node == null ? null : node.getUser(); } /** * Get the current collection. **/ public WordCollection getCurrentCollection() { CollectionNode node = dbTree.getCurrentCollection(); return node == null ? null : node.getCollection(); } /** * Get the current CollectionNode. **/ public CollectionNode getCurrentCollectionNode() { return dbTree.getCurrentCollection(); } /** * Set the current object being edited. **/ public void setCurrentEditObject(Persistent obj) { setCurrentEditObject(obj, null); } /** * Set the current object being edited and InputTable for validation. **/ public void setCurrentEditObject(Persistent obj, InputTable tab) { _currentEditObject = obj; _currentEditInputTable = tab; } /** * Save the current object being edited. **/ public boolean saveCurrentEditObject() { // save if (_currentEditObject != null) { if (_currentEditObject.isDirty()) { try { _currentEditObject.save(); } catch (DatabaseException e) { _currentEditObject = null; return true; } } } return true; } /** * Validate the current object being edited. **/ public boolean validateCurrentEditObject() { if (_currentEditInputTable == null) return true; // validate if (!_currentEditInputTable.validateFields()) return false; try { String msg = _currentEditObject.checkValidation(); if (msg != null) { _currentEditInputTable.setValues(_currentEditObject); Dialogs.msgbox(msg); return false; } } catch (DatabaseException e) { } return true; } /** * Left hand pane, which contains a tree of users and * collections in this database. **/ private final class DatabaseTree extends JTree implements Refreshable { DatabaseTree() throws DatabaseException { super(new BranchNode(AppPrefs.getInstance().getMessages().getString("lblUsers"))); MainFrame mf = MainFrame.getInstance(); setShowsRootHandles(true); getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent evt) { if (_cancelValueChanged) { _cancelValueChanged = false; return; } Object obj = evt.getPath().getLastPathComponent(); // we save the location and restore it after because swing // likes to reformat everything and it's kind of annoying int loc = getDividerLocation(); MainFrame.getInstance().setUserOnlyEnabled(false); if (obj instanceof WNode) { // check validation if (!validateCurrentEditObject()) { _cancelValueChanged = true; setSelectionPath(_lastPath); return; } _lastPath = getSelectionPath(); WNode node = (WNode) obj; Component comp = node.getRightComponent(); if (comp != null) { // save/set current edit object when changing user/collection saveCurrentEditObject(); // UserNode, CollectionNode setRightComponent(comp); // set current edit object when changing user/collection if (node instanceof UserNode) { // user setCurrentEditObject((Persistent)node.getUserObject(), ((SavePane)comp).getPropertyPane().getInfo()); } else { // set to default pane if (node instanceof CollectionNode) { ((CollectionNode)node).resetSelectedPane(); } if (comp instanceof Refreshable) { try { ((Refreshable)comp).refresh(); } catch (DatabaseException ignored) { } } } try { node.updateSetting(_db.getCurrentSetting()); } catch (DatabaseException ignored) { } MainFrame.getInstance().setUserOnlyEnabled(true); } else { // Users label BranchNode setRightComponent(EMPTY_LABEL); // set current edit object to null setCurrentEditObject(null); } } else { // should never happen, top root is a BranchNode setRightComponent(EMPTY_LABEL); } setDividerLocation(loc); MainFrame.getInstance().updateStatus(); } }); refresh(); setExpandedState(getPathForRow(0), true); setCellRenderer(new DatabaseTreeCellRenderer()); } void addUser(User user) throws DatabaseException { DefaultTreeModel model = (DefaultTreeModel) getModel(); DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot(); UserNode newNode = new UserNode(user, model); model.insertNodeInto(newNode, root, root.getChildCount()); // check for the placeholder if (root.getChildAt(0) instanceof HolderNode) { model.removeNodeFromParent((HolderNode) root.getChildAt(0)); } setSelectionPath(new TreePath(newNode.getPath())); } UserNode getCurrentUser() { // find the selected user TreePath path = getSelectionPath(); if (path == null) return null; UserNode userNode = null; for (int i = 0; i < path.getPathCount(); i++) { Object o = path.getPathComponent(i); if (o instanceof UserNode) { userNode = ((UserNode) o); break; } } return userNode; } CollectionNode getCurrentCollection() { // find the selected collection TreePath path = getSelectionPath(); if (path == null) return null; CollectionNode node = null; for (int i = 0; i < path.getPathCount(); i++) { Object o = path.getPathComponent(i); if (o instanceof CollectionNode) { node = ((CollectionNode) o); break; } } return node; } void addCollection(WordCollection collection) throws DatabaseException { UserNode userNode = getCurrentUser(); DefaultTreeModel model = (DefaultTreeModel) getModel(); DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot(); CollectionNode newNode = new CollectionNode(collection, model); model.insertNodeInto(newNode, userNode, userNode.getChildCount()); setSelectionPath(new TreePath(newNode.getPath())); } public void refresh() throws DatabaseException { // store expanded paths List expands = new ArrayList(); List newExpands = new ArrayList(); for (Enumeration _enum = getExpandedDescendants(getPathForRow(0)); _enum.hasMoreElements(); ) { expands.add(_enum.nextElement()); } DefaultTreeModel model = (DefaultTreeModel) getModel(); MutableTreeNode root = (MutableTreeNode) model.getRoot(); // remove everything first for (int i = root.getChildCount() - 1; i >= 0; i--) { root.remove(i); } // now add new stuff TreePath newPath = null; Setting setting = _db.getCurrentSetting(); for (Iterator it = _db.getUsers().iterator(); it.hasNext(); ) { User user = (User) it.next(); UserNode node = new UserNode(user, model); root.insert(node, root.getChildCount()); TreePath nodePath = new TreePath(node.getPath()); if (setting.getUserID() == user.getID()) { newPath = nodePath; } if (containsPath(expands, nodePath)) { newExpands.add(nodePath); } for (Iterator it2 = user.getCollections().iterator(); it2.hasNext(); ) { WordCollection col = (WordCollection) it2.next(); CollectionNode cnode = new CollectionNode(col, model); node.insert(cnode, node.getChildCount()); if (setting.getCollectionID() == col.getID()) { newPath = new TreePath(cnode.getPath()); } } } if (root.getChildCount() == 0) { root.insert(new HolderNode(AppPrefs.getInstance().getMessages().getString("msgNoUsers")), 0); } model.nodeStructureChanged(root); // reapply selected and expanded paths if (newPath != null) { setSelectionPath(newPath); } for (Iterator it = newExpands.iterator(); it.hasNext(); ) { TreePath path = (TreePath) it.next(); setExpandedState(path, true); } } private boolean containsPath(List list, TreePath path) { for (Iterator it = list.iterator(); it.hasNext(); ) { TreePath test = (TreePath) it.next(); if (path.getLastPathComponent().equals(test.getLastPathComponent())) { it.remove(); return true; } } return false; } } /** * Tree cell renderer for database tree. **/ private final class DatabaseTreeCellRenderer extends DefaultTreeCellRenderer { public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (value instanceof HolderNode) { setIcon(null); } else if (value instanceof WNode) { ImageIcon icon = ((WNode) value).getIcon(); if (icon != null) { setIcon(icon); } } return this; } } private final Database _db; private Persistent _currentEditObject = null; private InputTable _currentEditInputTable = null; private TreePath _lastPath = null; private boolean _cancelValueChanged = false; private DatabaseTree dbTree; }