001    /*
002     * $Id: ParameterTree.java,v 1.5 2012/02/19 17:35:50 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
007     * Space Science and Engineering Center (SSEC)
008     * University of Wisconsin - Madison
009     * 1225 W. Dayton Street, Madison, WI 53706, USA
010     * https://www.ssec.wisc.edu/mcidas
011     * 
012     * All Rights Reserved
013     * 
014     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015     * some McIDAS-V source code is based on IDV and VisAD source code.  
016     * 
017     * McIDAS-V is free software; you can redistribute it and/or modify
018     * it under the terms of the GNU Lesser Public License as published by
019     * the Free Software Foundation; either version 3 of the License, or
020     * (at your option) any later version.
021     * 
022     * McIDAS-V is distributed in the hope that it will be useful,
023     * but WITHOUT ANY WARRANTY; without even the implied warranty of
024     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025     * GNU Lesser Public License for more details.
026     * 
027     * You should have received a copy of the GNU Lesser Public License
028     * along with this program.  If not, see http://www.gnu.org/licenses.
029     */
030    
031    package edu.wisc.ssec.mcidasv.ui;
032    
033    
034    import java.awt.Component;
035    import java.awt.Dimension;
036    import java.awt.event.KeyAdapter;
037    import java.awt.event.KeyEvent;
038    import java.awt.event.MouseAdapter;
039    import java.awt.event.MouseEvent;
040    import java.util.ArrayList;
041    import java.util.Enumeration;
042    import java.util.Hashtable;
043    import java.util.List;
044    
045    import javax.swing.ImageIcon;
046    import javax.swing.JComponent;
047    import javax.swing.JFrame;
048    import javax.swing.JMenu;
049    import javax.swing.JMenuBar;
050    import javax.swing.JPanel;
051    import javax.swing.JPopupMenu;
052    import javax.swing.JScrollPane;
053    import javax.swing.JTree;
054    import javax.swing.SwingUtilities;
055    import javax.swing.tree.DefaultMutableTreeNode;
056    import javax.swing.tree.DefaultTreeCellRenderer;
057    import javax.swing.tree.DefaultTreeModel;
058    import javax.swing.tree.TreePath;
059    import javax.swing.tree.TreeSelectionModel;
060    
061    import ucar.unidata.idv.IdvPersistenceManager;
062    import ucar.unidata.ui.DndTree;
063    import ucar.unidata.util.GuiUtils;
064    import ucar.unidata.util.LogUtil;
065    import edu.wisc.ssec.mcidasv.ParameterSet;
066    import edu.wisc.ssec.mcidasv.PersistenceManager;
067    
068    
069    /**
070     * Class ParameterTree Gives a tree gui for editing parameter sets
071     *
072     *
073     * @author IDV Development Team
074     * @version $Revision: 1.5 $
075     */
076    public class ParameterTree extends DndTree {
077    
078        /** The window */
079        private JFrame frame;
080    
081        /** What is the type of the parameter set we are showing */
082        private String parameterType;
083    
084        /** The root of the tree */
085        private DefaultMutableTreeNode treeRoot;
086    
087        /** The tree model */
088        private DefaultTreeModel treeModel;
089    
090        /** A mapping from tree node to, either, category or ParameterSet */
091        private Hashtable nodeToData;
092    
093        /** The ui manager */
094        private UIManager uiManager;
095        
096        /** The persistence manager */
097        private PersistenceManager persistenceManager;
098    
099        /** Icon to use for categories */
100        private ImageIcon categoryIcon;
101    
102        /** Icon to use for parameters */
103        private ImageIcon parameterIcon;
104    
105    
106    
107    
108        /**
109         * Create the tree with the given parameter set type
110         *
111         *
112         * @param uiManager The UI manager
113         * @param parameterType The type of the parameter set we are showing
114         */
115        public ParameterTree(UIManager uiManager, String parameterType) {
116    
117            categoryIcon = GuiUtils.getImageIcon("/auxdata/ui/icons/folder.png",
118                                                 getClass());
119            parameterIcon = GuiUtils.getImageIcon("/auxdata/ui/icons/page.png",
120                                               getClass());
121    
122            this.uiManager = uiManager;
123            this.persistenceManager = (PersistenceManager)(uiManager.getPersistenceManager());
124    
125            setToolTipText(
126                "<html>Right click to show popup menu.<br>Drag to move parameter sets or categories</html>");
127    
128            this.parameterType = parameterType;
129            treeRoot = new DefaultMutableTreeNode("Parameter Sets");
130    
131            //        setRootVisible(false);
132            setShowsRootHandles(true);
133            treeModel = new DefaultTreeModel(treeRoot);
134            setModel(treeModel);
135            DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
136                public Component getTreeCellRendererComponent(JTree theTree,
137                        Object value, boolean sel, boolean expanded,
138                        boolean leaf, int row, boolean hasFocus) {
139    
140                    super.getTreeCellRendererComponent(theTree, value, sel,
141                            expanded, leaf, row, hasFocus);
142                    if ((nodeToData == null) || (value == null)) {
143                        return this;
144                    }
145                    Object data = nodeToData.get(value);
146                    if (data == null) {
147                        setIcon(categoryIcon);
148                        return this;
149                    }
150                    if (data instanceof ParameterSet) {
151                        setToolTipText(
152                            "<html>Right click to show parameter set menu.<br>Drag to move parameter set</html>");
153                        setIcon(parameterIcon);
154                    } else {
155                        setToolTipText(
156                            "<html>Right click to show category menu.<br>Drag to move parameter sets or categories</html><");
157                        setIcon(categoryIcon);
158                    }
159                    return this;
160                }
161            };
162            setCellRenderer(renderer);
163    
164    
165    
166    
167            addKeyListener(new KeyAdapter() {
168                public void keyPressed(KeyEvent e) {
169                    if (e.getKeyCode() == e.VK_DELETE) {
170                        deleteSelected();
171                    }
172                }
173            });
174    
175            getSelectionModel().setSelectionMode(
176                TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
177            //            TreeSelectionModel.SINGLE_TREE_SELECTION);
178    
179    
180            addMouseListener(new MouseAdapter() {
181                public void mouseClicked(MouseEvent event) {
182                    TreePath path = getPathForLocation(event.getX(),
183                                        event.getY());
184                    Object data = findDataAtPath(path);
185                    if ( !SwingUtilities.isRightMouseButton(event)) {
186    //                    if (event.getClickCount() > 1) {
187    //                        if ((data != null) && (data instanceof SavedBundle)) {
188    //                            doOpen((SavedBundle) data);
189    //                        }
190    //                    }
191                        return;
192                    }
193                    clearSelection();
194                    addSelectionPath(path);
195                    final DefaultMutableTreeNode parentNode =
196                        (DefaultMutableTreeNode) path.getLastPathComponent();
197    
198                    JPopupMenu popup = new JPopupMenu();
199                    if (data == null) {
200                        popup.add(GuiUtils.makeMenuItem("Add Subcategory",
201                                ParameterTree.this, "addCategory", parentNode));
202                    } else {
203                        if (data instanceof ParameterSet) {
204                            ParameterSet set = (ParameterSet) data;
205                            popup.add(GuiUtils.makeMenuItem("Rename",
206                                            ParameterTree.this, "doRename", set));
207                            popup.add(GuiUtils.makeMenuItem("Delete",
208                                            ParameterTree.this, "deleteParameterSet", set));
209                        } else {
210                            popup.add(GuiUtils.makeMenuItem("Delete Category",
211                                            ParameterTree.this, "deleteCategory",
212                                    data.toString()));
213                            popup.add(GuiUtils.makeMenuItem("Add Subcategory",
214                                            ParameterTree.this, "addCategory", parentNode));
215                        }
216                    }
217                    popup.show((Component) event.getSource(), event.getX(),
218                               event.getY());
219                }
220            });
221            loadParameterSets();
222    
223            String title = "Parameter Set Manager";
224    
225            Dimension defaultDimension = new Dimension(300, 400);
226            JScrollPane sp = GuiUtils.makeScrollPane(this,
227                                 (int) defaultDimension.getWidth(),
228                                 (int) defaultDimension.getHeight());
229            sp.setPreferredSize(defaultDimension);
230    
231    
232            JMenuBar menuBar  = new JMenuBar();
233            JMenu    fileMenu = new JMenu("File");
234            JMenu    helpMenu = new JMenu("Help");
235            menuBar.add(fileMenu);
236            //        menuBar.add(helpMenu);
237    //        fileMenu.add(GuiUtils.makeMenuItem("Export to File", this,
238    //                                           "doExport"));
239    //        fileMenu.add(GuiUtils.makeMenuItem("Export to Plugin", this,
240    //                                           "doExportToPlugin"));
241    //        fileMenu.addSeparator();
242            fileMenu.add(GuiUtils.makeMenuItem("Close", this, "doClose"));
243    
244    
245    
246    
247            JComponent bottom = GuiUtils.wrap(GuiUtils.makeButton("Close", this,
248                                    "doClose"));
249    
250            JPanel contents = GuiUtils.topCenterBottom(menuBar, sp, bottom);
251            frame = GuiUtils.createFrame(title);
252            frame.getContentPane().add(contents);
253            frame.pack();
254            frame.setLocation(100, 100);
255        }
256    
257        /**
258         * close
259         */
260        public void doClose() {
261            frame.dispose();
262        }
263    
264    
265        /**
266         * Get the list of selected parameter sets
267         *
268         * @return The selected parameter sets
269         */
270        private List getSelectedParameterSets() {
271            TreePath[] paths = getSelectionModel().getSelectionPaths();
272            if ((paths == null) || (paths.length == 0)) {
273                return new ArrayList();
274            }
275            List sets = new ArrayList();
276            for (int i = 0; i < paths.length; i++) {
277                Object data = findDataAtPath(paths[i]);
278                if (data == null) {
279                    continue;
280                }
281                if ( !(data instanceof ParameterSet)) {
282                    continue;
283                }
284                sets.add(data);
285            }
286            return sets;
287        }
288    
289        /**
290         * Show the window
291         */
292        public void setVisible(boolean visibility) {
293            frame.setVisible(visibility);
294        }
295    
296    
297        /**
298         * Ok to drag the node
299         *
300         * @param sourceNode The node to drag
301         *
302         * @return Ok to drag
303         */
304        protected boolean okToDrag(DefaultMutableTreeNode sourceNode) {
305            return sourceNode.getParent() != null;
306        }
307    
308    
309        /**
310         * Ok to drop the node
311         *
312         *
313         * @param sourceNode The dragged node
314         * @param destNode Where to drop
315         *
316         * @return Ok to drop
317         */
318        protected boolean okToDrop(DefaultMutableTreeNode sourceNode,
319                                   DefaultMutableTreeNode destNode) {
320    
321    
322            //Don't drop a parameter set onto the root. It must be in a category
323            if (sourceNode.getUserObject() instanceof ParameterSet) {
324                if (destNode.getParent() == null) {
325                    return false;
326                }
327            }
328    
329            if (destNode.getUserObject() instanceof ParameterSet) {
330                return false;
331            }
332            if (destNode == sourceNode.getParent()) {
333                return false;
334            }
335            while (destNode != null) {
336                if (destNode == sourceNode) {
337                    return false;
338                }
339                destNode = (DefaultMutableTreeNode) destNode.getParent();
340            }
341            return true;
342        }
343    
344    
345    
346        /**
347         * Handle the DND drop
348         *
349         *
350         * @param sourceNode The dragged node
351         * @param destNode Where to drop
352         */
353        protected void doDrop(DefaultMutableTreeNode sourceNode,
354                              DefaultMutableTreeNode destNode) {
355            if (sourceNode.getUserObject() instanceof ParameterSet) {
356                persistenceManager.moveParameterSet(
357                    parameterType,
358                    (ParameterSet) sourceNode.getUserObject(),
359                    getCategoryList(destNode));
360            } else {
361                    persistenceManager.moveParameterSetCategory(
362                            parameterType,
363                    getCategoryList(sourceNode),
364                    getCategoryList(destNode));
365            }
366    
367            loadParameterSets();
368        }
369    
370    
371    
372    
373        /**
374         * Create the list of categories
375         *
376         * @param destNode From where
377         *
378         * @return List of String categories
379         */
380        private List getCategoryList(DefaultMutableTreeNode destNode) {
381            List categories = new ArrayList();
382            while (destNode.getParent() != null) {
383                categories.add(0, destNode.getUserObject().toString());
384                destNode = (DefaultMutableTreeNode) destNode.getParent();
385            }
386            return categories;
387        }
388    
389        /**
390         * Load the parameter sets into the tree
391         */
392        protected void loadParameterSets() {
393    
394            Enumeration paths =
395                getExpandedDescendants(new TreePath(treeRoot.getPath()));
396            Hashtable expandedState =
397                GuiUtils.initializeExpandedPathsBeforeChange(this, treeRoot);
398    
399            List allCategories = persistenceManager.getAllParameterSetCategories(parameterType);
400            nodeToData = new Hashtable();
401            treeRoot.removeAllChildren();
402            Hashtable catNodes    = new Hashtable();
403            Hashtable fakeSets    = new Hashtable();
404            List      sets        = new ArrayList();
405    
406            //We use a set of fake parameter sets to we include all categories into the tree
407            for (int i = 0; i < allCategories.size(); i++) {
408                List categories = persistenceManager.stringToCategories((String) allCategories.get(i));
409                ParameterSet fakeSet = new ParameterSet("", categories, parameterType);
410                fakeSets.put(fakeSet, fakeSet);
411                sets.add(fakeSet);
412            }
413            sets.addAll(persistenceManager.getAllParameterSets(parameterType));
414            for (int i = 0; i < sets.size(); i++) {
415                ParameterSet           set     = (ParameterSet) sets.get(i);
416                List                   categories = set.getCategories();
417                DefaultMutableTreeNode catNode    = treeRoot;
418                String                 fullCat    = "";
419                for (int catIdx = 0; catIdx < categories.size(); catIdx++) {
420                    String cat = (String) categories.get(catIdx);
421                    if (fullCat.length() > 0) {
422                        fullCat = fullCat + IdvPersistenceManager.CATEGORY_SEPARATOR;
423                    }
424                    fullCat = fullCat + cat;
425                    DefaultMutableTreeNode tmpNode =  (DefaultMutableTreeNode) catNodes.get(fullCat);
426                    if (tmpNode == null) {
427                        tmpNode = new DefaultMutableTreeNode(cat);
428                        nodeToData.put(tmpNode, fullCat);
429                        catNode.add(tmpNode);
430                        catNodes.put(fullCat, tmpNode);
431                    }
432                    catNode = tmpNode;
433                }
434                //Skip over the fake ones
435                if (fakeSets.get(set) == null) {
436                    DefaultMutableTreeNode setNode = new DefaultMutableTreeNode(set);
437                    nodeToData.put(setNode, set);
438                    catNode.add(setNode);
439                }
440            }
441            treeModel.nodeStructureChanged(treeRoot);
442            GuiUtils.expandPathsAfterChange(this, expandedState, treeRoot);
443        }
444    
445    
446        /**
447         * Find the data (either a ParameterSet or a category)
448         * associated with the given  tree path
449         *
450         * @param path The path
451         *
452         * @return The data
453         */
454    
455        private Object findDataAtPath(TreePath path) {
456            if ((path == null) || (nodeToData == null)) {
457                return null;
458            }
459            DefaultMutableTreeNode last =
460                (DefaultMutableTreeNode) path.getLastPathComponent();
461            if (last == null) {
462                return null;
463            }
464            return nodeToData.get(last);
465        }
466    
467        /**
468         * Delete the selected item in the tree
469         */
470        public void deleteSelected() {
471            System.out.println("deleteSelected");
472            TreePath[] paths = getSelectionModel().getSelectionPaths();
473            if ((paths == null) || (paths.length == 0)) {
474                return;
475            }
476            Object data = findDataAtPath(paths[0]);
477            if (data == null) {
478                return;
479            }
480            if (data instanceof ParameterSet) {
481                deleteParameterSet((ParameterSet) data);
482            } else {
483                deleteCategory(data.toString());
484            }
485        }
486    
487    
488        /**
489         * Delete the given parameter set
490         *
491         * @param parameterSet The parameter set to delete
492         */
493        public void deleteParameterSet(ParameterSet parameterSet) {
494            if ( !GuiUtils.askYesNo(
495                    "Parameter set delete confirmation",
496                    "Are you sure you want to delete the parameter set \"" + parameterSet
497                    + "\"  ?")) {
498                return;
499            }
500            System.out.println("deleteParameterSet");
501            persistenceManager.deleteParameterSet(parameterType, parameterSet);
502            loadParameterSets();
503        }
504    
505    
506        /**
507         * Delete the given parameter set category
508         *
509         * @param category The category to delete
510         */
511        public void deleteCategory(String category) {
512            if ( !GuiUtils.askYesNo(
513                    "Parameter Set Category Delete Confirmation",
514                    "<html>Are you sure you want to delete the category:<p> <center>\""
515                    + category
516                    + "\"</center> <br> and all parameter sets and categories under it?</html>")) {
517                return;
518            }
519            System.out.println("deleteCategory");
520            persistenceManager.deleteParameterSetCategory(parameterType, category);
521            loadParameterSets();
522        }
523    
524        /**
525         * Create a new category under the given node
526         *
527         * @param parentNode The parent tree node
528         */
529        public void addCategory(DefaultMutableTreeNode parentNode) {
530            String cat =
531                GuiUtils.getInput("Please enter the new category name",
532                                  "Name: ", "");
533            if (cat == null) {
534                return;
535            }
536            String parentCat = (String) nodeToData.get(parentNode);
537            String fullCat   = ((parentCat == null)
538                                ? cat
539                                : (parentCat
540                                   + IdvPersistenceManager.CATEGORY_SEPARATOR
541                                   + cat));
542            System.out.println("addCategory");
543            if ( !persistenceManager.addParameterSetCategory(parameterType, fullCat)) {
544                LogUtil.userMessage(
545                    "A category with the given name already exists");
546                return;
547            }
548            DefaultMutableTreeNode newCatNode = new DefaultMutableTreeNode(cat);
549            nodeToData.put(newCatNode, fullCat);
550            parentNode.add(newCatNode);
551    
552    
553            Hashtable expandedState =
554                GuiUtils.initializeExpandedPathsBeforeChange(this, treeRoot);
555            treeModel.nodeStructureChanged(treeRoot);
556            GuiUtils.expandPathsAfterChange(this, expandedState, treeRoot);
557        }
558    
559    
560        /**
561         * Rename the parameter set
562         * @param parameterSet the parameter set
563         */
564        public void doRename(ParameterSet parameterSet) {
565            System.out.println("doRename");
566            persistenceManager.renameParameterSet(parameterType, parameterSet);
567        }
568    
569    }
570