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