001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
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 https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.ui;
030
031
032import java.awt.Component;
033import java.awt.Dimension;
034import java.awt.event.KeyAdapter;
035import java.awt.event.KeyEvent;
036import java.awt.event.MouseAdapter;
037import java.awt.event.MouseEvent;
038import java.util.ArrayList;
039import java.util.Enumeration;
040import java.util.Hashtable;
041import java.util.List;
042
043import javax.swing.ImageIcon;
044import javax.swing.JComponent;
045import javax.swing.JFrame;
046import javax.swing.JMenu;
047import javax.swing.JMenuBar;
048import javax.swing.JPanel;
049import javax.swing.JPopupMenu;
050import javax.swing.JScrollPane;
051import javax.swing.JTree;
052import javax.swing.SwingUtilities;
053import javax.swing.tree.DefaultMutableTreeNode;
054import javax.swing.tree.DefaultTreeCellRenderer;
055import javax.swing.tree.DefaultTreeModel;
056import javax.swing.tree.TreePath;
057import javax.swing.tree.TreeSelectionModel;
058
059import ucar.unidata.idv.IdvPersistenceManager;
060import ucar.unidata.ui.DndTree;
061import ucar.unidata.util.GuiUtils;
062import ucar.unidata.util.LogUtil;
063import edu.wisc.ssec.mcidasv.ParameterSet;
064import 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 */
074public 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