001/*
002 * $Id: ParameterTree.java,v 1.4 2011/03/24 16:06:34 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
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
031package edu.wisc.ssec.mcidasv.ui;
032
033
034import java.awt.Component;
035import java.awt.Dimension;
036import java.awt.event.KeyAdapter;
037import java.awt.event.KeyEvent;
038import java.awt.event.MouseAdapter;
039import java.awt.event.MouseEvent;
040import java.util.ArrayList;
041import java.util.Enumeration;
042import java.util.Hashtable;
043import java.util.List;
044
045import javax.swing.ImageIcon;
046import javax.swing.JComponent;
047import javax.swing.JFrame;
048import javax.swing.JMenu;
049import javax.swing.JMenuBar;
050import javax.swing.JPanel;
051import javax.swing.JPopupMenu;
052import javax.swing.JScrollPane;
053import javax.swing.JTree;
054import javax.swing.SwingUtilities;
055import javax.swing.tree.DefaultMutableTreeNode;
056import javax.swing.tree.DefaultTreeCellRenderer;
057import javax.swing.tree.DefaultTreeModel;
058import javax.swing.tree.TreePath;
059import javax.swing.tree.TreeSelectionModel;
060
061import ucar.unidata.idv.IdvPersistenceManager;
062import ucar.unidata.ui.DndTree;
063import ucar.unidata.util.GuiUtils;
064import ucar.unidata.util.LogUtil;
065import edu.wisc.ssec.mcidasv.ParameterSet;
066import 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.4 $
075 */
076public 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