001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2018
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
029package edu.wisc.ssec.mcidasv;
030
031import static javax.swing.GroupLayout.Alignment.BASELINE;
032import static javax.swing.GroupLayout.Alignment.LEADING;
033import static javax.swing.GroupLayout.Alignment.TRAILING;
034import static javax.swing.GroupLayout.DEFAULT_SIZE;
035import static javax.swing.GroupLayout.PREFERRED_SIZE;
036import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
037
038import java.awt.BorderLayout;
039import java.awt.CardLayout;
040import java.awt.Color;
041import java.awt.Component;
042import java.awt.Container;
043import java.awt.Dimension;
044import java.awt.FlowLayout;
045import java.awt.Font;
046import java.awt.Graphics;
047import java.awt.Graphics2D;
048import java.awt.Insets;
049import java.awt.RenderingHints;
050import java.awt.event.ActionEvent;
051import java.awt.event.ActionListener;
052import java.beans.PropertyChangeEvent;
053import java.beans.PropertyChangeListener;
054import java.net.URL;
055import java.text.Collator;
056import java.text.DecimalFormat;
057import java.util.ArrayList;
058import java.util.Arrays;
059import java.util.Collection;
060import java.util.Collections;
061import java.util.Enumeration;
062import java.util.HashMap;
063import java.util.Hashtable;
064import java.util.LinkedHashSet;
065import java.util.List;
066import java.util.Map;
067import java.util.Set;
068import java.util.TimeZone;
069import java.util.TreeSet;
070
071import javax.swing.BorderFactory;
072import javax.swing.BoxLayout;
073import javax.swing.DefaultListCellRenderer;
074import javax.swing.DefaultListModel;
075import javax.swing.GroupLayout;
076import javax.swing.ImageIcon;
077import javax.swing.JButton;
078import javax.swing.JCheckBox;
079import javax.swing.JComboBox;
080import javax.swing.JComponent;
081import javax.swing.JLabel;
082import javax.swing.JList;
083import javax.swing.JOptionPane;
084import javax.swing.JPanel;
085import javax.swing.JRadioButton;
086import javax.swing.JScrollPane;
087import javax.swing.JSlider;
088import javax.swing.JTextField;
089import javax.swing.ListSelectionModel;
090import javax.swing.SwingConstants;
091import javax.swing.SwingUtilities;
092import javax.swing.border.BevelBorder;
093import javax.swing.event.ChangeEvent;
094import javax.swing.event.ChangeListener;
095import javax.swing.event.ListSelectionEvent;
096import javax.swing.event.ListSelectionListener;
097
098import edu.wisc.ssec.mcidasv.ui.ColorSwatchComponent;
099import edu.wisc.ssec.mcidasv.util.GetMem;
100
101import org.bushe.swing.event.EventBus;
102import org.bushe.swing.event.annotation.AnnotationProcessor;
103import org.bushe.swing.event.annotation.EventSubscriber;
104import org.slf4j.Logger;
105import org.slf4j.LoggerFactory;
106import org.w3c.dom.Element;
107import org.w3c.dom.NodeList;
108
109import ucar.unidata.data.DataUtil;
110import ucar.unidata.geoloc.ProjectionImpl;
111import ucar.unidata.idv.ControlDescriptor;
112import ucar.unidata.idv.DisplayControl;
113import ucar.unidata.idv.IdvConstants;
114import ucar.unidata.idv.IdvObjectStore;
115import ucar.unidata.idv.IdvPreferenceManager;
116import ucar.unidata.idv.IntegratedDataViewer;
117import ucar.unidata.idv.MapViewManager;
118import ucar.unidata.idv.ViewManager;
119import ucar.unidata.idv.control.DisplayControlImpl;
120import ucar.unidata.ui.CheckboxCategoryPanel;
121import ucar.unidata.ui.FontSelector;
122import ucar.unidata.ui.HelpTipDialog;
123import ucar.unidata.ui.XmlUi;
124import ucar.unidata.util.FileManager;
125import ucar.unidata.util.GuiUtils;
126import ucar.unidata.util.IOUtil;
127import ucar.unidata.util.LogUtil;
128import ucar.unidata.util.Misc;
129import ucar.unidata.util.Msg;
130import ucar.unidata.util.ObjectListener;
131import ucar.unidata.util.StringUtil;
132import ucar.unidata.util.TwoFacedObject;
133import ucar.unidata.xml.PreferenceManager;
134import ucar.unidata.xml.XmlObjectStore;
135import ucar.unidata.xml.XmlUtil;
136import ucar.visad.UtcDate;
137import visad.DateTime;
138import visad.Unit;
139import edu.wisc.ssec.mcidasv.servermanager.EntryStore;
140import edu.wisc.ssec.mcidasv.servermanager.AddePreferences;
141import edu.wisc.ssec.mcidasv.servermanager.AddePreferences.AddePrefConglomeration;
142import edu.wisc.ssec.mcidasv.startupmanager.Platform;
143import edu.wisc.ssec.mcidasv.startupmanager.StartupManager;
144import edu.wisc.ssec.mcidasv.ui.McvToolbarEditor;
145import edu.wisc.ssec.mcidasv.ui.UIManager;
146import edu.wisc.ssec.mcidasv.util.CollectionHelpers;
147import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
148import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
149
150/**
151 * <p>An extension of {@link ucar.unidata.idv.IdvPreferenceManager} that uses
152 * a JList instead of tabs to lay out the various PreferenceManagers.</p>
153 *
154 * @author McIDAS-V Dev Team
155 */
156public class McIdasPreferenceManager extends IdvPreferenceManager implements ListSelectionListener, Constants {
157    
158    /** Logger object. */
159    private static final Logger logger = LoggerFactory.getLogger(McIdasPreferenceManager.class);
160    
161    public static final float LOGO_SCALE_MIN = 0.1f;
162    public static final float LOGO_SCALE_MAX = 2.0f;
163    
164    /** 
165     * <p>Controls how the preference panel list is displayed. Want to modify 
166     * the preferences UI in some way? PREF_PANELS is your friend. Think of 
167     * it like a really brain-dead SQLite.</p>
168     * 
169     * <p>Each row is a panel, and <b>must</b> consist of three columns.</p>
170     *
171     * <ol start="0">
172     * <li>Name of the panel.</li>
173     * <li>Path to the icon associated with the panel.</li>
174     * <li>The panel's {@literal "help ID."}</li>
175     * </ol>
176     *
177     * <p>The {@link JList} in the preferences window will order the panels
178     * based upon {@code PREF_PANELS}.</p>
179     */
180    public static final String[][] PREF_PANELS = {
181        { Constants.PREF_LIST_GENERAL, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/mcidasv-round32.png", "idv.tools.preferences.generalpreferences" },
182        { Constants.PREF_LIST_VIEW, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/tab-new32.png", "idv.tools.preferences.displaywindowpreferences" },
183        { Constants.PREF_LIST_TOOLBAR, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/application-x-executable32.png", "idv.tools.preferences.toolbarpreferences" },
184        { Constants.PREF_LIST_DATA_CHOOSERS, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/preferences-desktop-remote-desktop32.png", "idv.tools.preferences.datapreferences" },
185        { Constants.PREF_LIST_ADDE_SERVERS, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/applications-internet32.png", "idv.tools.preferences.serverpreferences" },
186        { Constants.PREF_LIST_AVAILABLE_DISPLAYS, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/video-display32.png", "idv.tools.preferences.availabledisplayspreferences" },
187        { Constants.PREF_LIST_NAV_CONTROLS, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/input-mouse32.png", "idv.tools.preferences.navigationpreferences" },
188        { Constants.PREF_LIST_FORMATS_DATA,"/edu/wisc/ssec/mcidasv/resources/icons/prefs/preferences-desktop-theme32.png", "idv.tools.preferences.formatpreferences" },
189        { Constants.PREF_LIST_ADVANCED, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/applications-internet32.png", "idv.tools.preferences.advancedpreferences" }
190    };
191    
192    /** Desired rendering hints with their desired values. */
193    public static final Object[][] RENDER_HINTS = {
194        { RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON },
195        { RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY },
196        { RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON }
197    };
198    
199    /** Options for bundle loading */
200    public static final String[] loadComboOptions = {
201        "Create new window(s)",
202        "Merge with active tab(s)",
203        "Add new tab(s) to current window",
204        "Replace session"
205    };
206    
207    /**
208     * @return The rendering hints to use, as determined by RENDER_HINTS.
209     */
210    public static RenderingHints getRenderingHints() {
211        RenderingHints hints = new RenderingHints(null);
212        for (int i = 0; i < RENDER_HINTS.length; i++) {
213            hints.put(RENDER_HINTS[i][0], RENDER_HINTS[i][1]);
214        }
215        return hints;
216    }
217    
218    /** Help McV remember the last preference panel the user selected. */
219    private static final String LAST_PREF_PANEL = "mcv.prefs.lastpanel";
220    
221    private static final String LEGEND_TEMPLATE_DATA = "%datasourcename% - %displayname%";
222    private static final String DISPLAY_LIST_TEMPLATE_DATA = "%datasourcename% - %displayname% " + UtcDate.MACRO_TIMESTAMP;
223    private static final String TEMPLATE_IMAGEDISPLAY = "%longname% " + UtcDate.MACRO_TIMESTAMP;
224    
225    private static final String TEMPLATE_NO_DATA = "%displayname%";
226    
227    /** test value for formatting */
228    private static double latlonValue = -104.56284;
229    
230    /** Decimal format */
231    private static DecimalFormat latlonFormat = new DecimalFormat();
232    
233    /** Provide some default values for the lat-lon preference drop down. */
234    private static final Set<String> defaultLatLonFormats =
235        CollectionHelpers.set("##0",
236                              "##0.0",
237                              "##0.0#",
238                              "##0.0##",
239                              "0.0",
240                              "0.00",
241                              "0.000");
242    
243    private static final Set<String> probeFormatsList =
244        CollectionHelpers.set(DisplayControl.DEFAULT_PROBEFORMAT,
245                              "%rawvalue% [%rawunit%]",
246                              "%value%",
247                              "%rawvalue%",
248                              "%value% <i>%unit%</i>");
249
250    /** 
251     * Replacing the "incoming" IDV preference tab names with whatever's in
252     * this map.
253     */
254    private static final Map<String, String> replaceMap = 
255        CollectionHelpers.zipMap(
256            CollectionHelpers.arr("Toolbar", "View"), 
257            CollectionHelpers.arr(Constants.PREF_LIST_TOOLBAR, Constants.PREF_LIST_VIEW));
258            
259    /** Path to the McV choosers.xml */
260    private static final String MCV_CHOOSERS = "/edu/wisc/ssec/mcidasv/resources/choosers.xml";
261    
262    /** 
263     * Maps the {@literal "name"} of a panel to the actual thing holding the 
264     * PreferenceManager. 
265     */
266    private final Map<String, Container> prefMap = CollectionHelpers.concurrentMap();
267    
268    /** Maps the name of a panel to an icon. */
269    private final Map<String, ImageIcon> iconCache = CollectionHelpers.concurrentMap();
270    
271    /** 
272     * A table of the different preference managers that'll wind up in the
273     * list.
274     */
275    private final Map<String, PreferenceManager> managerMap = CollectionHelpers.concurrentMap();
276    
277    /**
278     * Each PreferenceManager has associated data contained in this table.
279     * TODO: bug Unidata about getting IdvPreferenceManager's dataList protected
280     */
281    private final Map<String, Object> dataMap = CollectionHelpers.concurrentMap();
282    
283    private final Set<String> labelSet = new LinkedHashSet<String>();
284    
285    /** 
286     * The list that'll contain all the names of the different 
287     * PreferenceManagers 
288     */
289    private JList labelList;
290    
291    /** The "M" in the MVC for JLists. Contains all the list data. */
292    private DefaultListModel listModel;
293    
294    /** Handle scrolling like a pro. */
295    private JScrollPane listScrollPane;
296    
297    /** Holds the main preference pane */
298    private JPanel mainPane;
299    
300    /** Holds the buttons at the bottom */
301    private JPanel buttonPane;
302    
303    /** Date formats */
304    private final Set<String> dateFormats = CollectionHelpers.set(
305        DEFAULT_DATE_FORMAT, "MM/dd/yy HH:mm z", "dd.MM.yy HH:mm z", 
306        "yyyy-MM-dd", "EEE, MMM dd yyyy HH:mm z", "HH:mm:ss", "HH:mm", 
307        "yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-dd'T'HH:mm:ssZ");
308    
309    /** The toolbar editor */
310    private McvToolbarEditor toolbarEditor;
311    
312    /**
313     * Prep as much as possible for displaying the preference window: load up
314     * icons and create some of the window features.
315     * 
316     * @param idv Reference to the supreme IDV object.
317     */
318    public McIdasPreferenceManager(IntegratedDataViewer idv) {
319        super(idv);
320        AnnotationProcessor.process(this);
321        init();
322        
323        for (int i = 0; i < PREF_PANELS.length; i++) {
324            URL url = getClass().getResource(PREF_PANELS[i][1]);
325            iconCache.put(PREF_PANELS[i][0], new ImageIcon(url));
326        }
327        
328        setEmptyPref("idv.displaylist.template.data", DISPLAY_LIST_TEMPLATE_DATA);
329        setEmptyPref("idv.displaylist.template.nodata", TEMPLATE_NO_DATA);
330        setEmptyPref("idv.displaylist.template.imagedisplay", TEMPLATE_IMAGEDISPLAY);
331        setEmptyPref("idv.legendlabel.template.data", LEGEND_TEMPLATE_DATA);
332        setEmptyPref("idv.legendlabel.template.nodata", TEMPLATE_NO_DATA);
333    }
334    
335    private boolean setEmptyPref(final String id, final String val) {
336        IdvObjectStore store = getIdv().getStore();
337        if (store.get(id, (String)null) == null) {
338            store.put(id, val);
339            return true;
340        }
341        return false;
342    }
343    
344    /**
345     * Overridden so McIDAS-V can direct users to specific help sections for
346     * each preference panel.
347     */
348    @Override public void actionPerformed(ActionEvent event) {
349        String cmd = event.getActionCommand();
350        if (!GuiUtils.CMD_HELP.equals(cmd) || labelList == null) {
351            super.actionPerformed(event);
352            return;
353        }
354        
355        int selectedIndex = labelList.getSelectedIndex();
356        getIdvUIManager().showHelp(PREF_PANELS[selectedIndex][2]);
357    }
358    
359    public void replaceServerPrefPanel(final JPanel panel) {
360        String name = "SERVER MANAGER";
361        mainPane.add(name, panel);
362        ((CardLayout)mainPane.getLayout()).show(mainPane, name);
363    }
364    
365    @EventSubscriber(eventClass=EntryStore.Event.class)
366    public void replaceServerPreferences(EntryStore.Event evt) {
367        EntryStore remoteAddeStore = ((McIDASV)getIdv()).getServerManager();
368        AddePreferences prefs = new AddePreferences(remoteAddeStore);
369        AddePrefConglomeration eww = prefs.buildPanel((McIDASV)getIdv());
370    }
371    
372    /**
373     * Add a PreferenceManager to the list of things that should be shown in
374     * the preference dialog.
375     * 
376     * @param tabLabel The label (or name) of the PreferenceManager.
377     * @param description Not used.
378     * @param listener The actual PreferenceManager.
379     * @param panel The container holding all of the PreferenceManager stuff.
380     * @param data Data passed to the preference manager.
381     */
382    @Override public void add(String tabLabel, String description, 
383        PreferenceManager listener, Container panel, Object data) {
384        
385        // if there is an alternate name for tabLabel, find and use it.
386        if (replaceMap.containsKey(tabLabel) == true) {
387            tabLabel = replaceMap.get(tabLabel);
388        }
389        
390        if (prefMap.containsKey(tabLabel) == true) {
391            return;
392        }
393        
394        // figure out the last panel that was selected.
395        int selected = getIdv().getObjectStore().get(LAST_PREF_PANEL, 0);
396        if (selected < 0 || selected >= PREF_PANELS.length) {
397            logger.warn("attempted to select an invalid preference panel: {}", selected);
398            selected = 0;
399        }
400        String selectedPanel = PREF_PANELS[selected][0];
401        
402        panel.setPreferredSize(null);
403        
404        Msg.translateTree(panel);
405        
406        managerMap.put(tabLabel, listener);
407        if (data == null) {
408            dataMap.put(tabLabel, new Hashtable());
409        } else {
410            dataMap.put(tabLabel, data);
411        }
412        prefMap.put(tabLabel, panel);
413        
414        if (labelSet.add(tabLabel)) {
415            JLabel label = new JLabel();
416            label.setText(tabLabel);
417            label.setIcon(iconCache.get(tabLabel));
418            listModel.addElement(label);
419            
420            labelList.setSelectedIndex(selected);
421            mainPane.add(tabLabel, panel);
422            if (selectedPanel.equals(tabLabel)) {
423                ((CardLayout)mainPane.getLayout()).show(mainPane, tabLabel);
424            }
425        }
426        
427        mainPane.repaint();
428    }
429    
430    /**
431     * Apply the preferences (taken straight from IDV). 
432     *
433     * @return Whether or not each of the preference managers applied properly.
434     */
435    // TODO(jon?): bug Unidata about making managers and dataList protected instead of private
436    @Override public boolean apply() {
437        try {
438            for (String id : labelSet) {
439                PreferenceManager manager = managerMap.get(id);
440                manager.applyPreference(getStore(), dataMap.get(id));
441            }
442            fixDisplayListFont();
443            getStore().save();
444            return true;
445        } catch (Exception exc) {
446            LogUtil.logException("Error applying preferences", exc);
447            return false;
448        }
449    }
450    
451    // For some reason the display list font can have a size of zero if your
452    // new font size didn't change after starting the prefs panel. 
453    private void fixDisplayListFont() {
454        IdvObjectStore s = getStore();
455        Font f = s.get(ViewManager.PREF_DISPLAYLISTFONT, FontSelector.DEFAULT_FONT);
456        if (f.getSize() == 0) {
457            f = f.deriveFont(8f);
458            s.put(ViewManager.PREF_DISPLAYLISTFONT, f);
459        }
460    }
461    
462    /**
463     * Select a list item and its corresponding panel that both live within the 
464     * preference window JList.
465     * 
466     * @param labelName The "name" of the JLabel within the JList.
467     */
468    public void selectListItem(String labelName) {
469        show();
470        toFront();
471        
472        for (int i = 0; i < listModel.getSize(); i++) {
473            String labelText = ((JLabel)listModel.get(i)).getText();
474            if (StringUtil.stringMatch(labelText, labelName)) {
475                // persist across restarts
476                getIdv().getObjectStore().put(LAST_PREF_PANEL, i);
477                labelList.setSelectedIndex(i);
478                return;
479            }
480        }
481    }
482    
483    /**
484     * Wrapper so that IDV code can still select which preference pane to show.
485     * 
486     * @param tabNameToShow The name of the pane to be shown. Regular
487     * expressions are supported.
488     */
489    public void showTab(String tabNameToShow) {
490        selectListItem(tabNameToShow);
491    }
492    
493    /**
494     * Handle the user clicking around.
495     * 
496     * @param e The event to be handled! Use your imagination!
497     */
498    public void valueChanged(ListSelectionEvent e) {
499        if (e.getValueIsAdjusting() == false) {
500            String name = getSelectedName();
501            ((CardLayout)mainPane.getLayout()).show(mainPane, name);
502        }
503    }
504    
505    /**
506     * Returns the container the corresponds to the currently selected label in
507     * the JList. Also stores the selected panel so that the next time a user
508     * tries to open the preferences they will start off in the panel they last
509     * selected.
510     * 
511     * @return The current container.
512     */
513    private String getSelectedName() {
514        // make sure the selected panel persists across restarts
515        getIdv().getObjectStore().put(LAST_PREF_PANEL, labelList.getSelectedIndex());
516        String key = ((JLabel)listModel.getElementAt(labelList.getSelectedIndex())).getText();
517        return key;
518    }
519    
520    /**
521     * Perform the GUI initialization for the preference dialog.
522     */
523    public void init() {
524        listModel = new DefaultListModel();
525        labelList = new JList(listModel);
526        
527        labelList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
528        labelList.setCellRenderer(new IconCellRenderer());
529        labelList.addListSelectionListener(new ListSelectionListener() {
530            public void valueChanged(ListSelectionEvent e) {
531                if (e.getValueIsAdjusting() == false) {
532                    String name = getSelectedName();
533                    if (Constants.PREF_LIST_NAV_CONTROLS.equals(name)) {
534                        mainPane.add(name, makeEventPanel());
535                        mainPane.validate();
536                    }
537                    ((CardLayout)mainPane.getLayout()).show(mainPane, name);
538                }
539            }
540        });
541        
542        listScrollPane = new JScrollPane(labelList);
543        listScrollPane.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
544        
545        mainPane = new JPanel(new CardLayout());
546        mainPane.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
547        mainPane.addPropertyChangeListener(new PropertyChangeListener() {
548            public void propertyChange(PropertyChangeEvent e) {
549//                System.err.println("prop change: prop="+e.getPropertyName()+" old="+e.getOldValue()+" new="+e.getNewValue());
550                String p = e.getPropertyName();
551                if (!"Frame.active".equals(p) && !"ancestor".equals(p)) {
552                    return;
553                }
554                
555                Object v = e.getNewValue();
556                boolean okay = false;
557                if (v instanceof Boolean) {
558                    okay = ((Boolean)v).booleanValue();
559                } else if (v instanceof JPanel) {
560                    okay = true;
561                } else {
562                    okay = false;
563                }
564                
565                if (okay) {
566                    if (getSelectedName().equals(Constants.PREF_LIST_NAV_CONTROLS)) {
567                        mainPane.add(Constants.PREF_LIST_NAV_CONTROLS, makeEventPanel());
568                        mainPane.validate();
569                        ((CardLayout)mainPane.getLayout()).show(mainPane, Constants.PREF_LIST_NAV_CONTROLS);
570                    }
571                }
572            }
573        });
574        
575        JPanel buttons = GuiUtils.makeApplyOkHelpCancelButtons(this);
576        buttonPane = McVGuiUtils.makePrettyButtons(buttons);
577        
578        contents = new JPanel();
579        GroupLayout layout = new GroupLayout(contents);
580        contents.setLayout(layout);
581        layout.setHorizontalGroup(
582            layout.createParallelGroup(LEADING)
583            .addGroup(layout.createSequentialGroup()
584                .addComponent(listScrollPane, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
585                .addPreferredGap(RELATED)
586                .addComponent(mainPane, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
587                .addComponent(buttonPane, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
588        );
589        layout.setVerticalGroup(
590            layout.createParallelGroup(LEADING)
591            .addGroup(layout.createSequentialGroup()
592                .addGroup(layout.createParallelGroup(TRAILING)
593                    .addComponent(mainPane, LEADING, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
594                    .addComponent(listScrollPane, LEADING, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
595                    .addPreferredGap(RELATED)
596                    .addComponent(buttonPane, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
597        );
598    }
599    
600    /**
601     * Initialize the preference dialog. Leave most of the heavy lifting to
602     * the IDV, except for creating the server manager.
603     */
604    protected void initPreferences() {
605        // General/McIDAS-V
606        addMcVPreferences();
607        
608        // View/Display Window
609        addDisplayWindowPreferences();
610        
611        // Toolbar/Toolbar Options
612        addToolbarPreferences();
613        
614        // Available Choosers/Data Sources
615        addChooserPreferences();
616        
617        // ADDE Servers
618        addServerPreferences();
619        
620        // Available Displays/Display Types
621        addDisplayPreferences();
622        
623        // Navigation/Navigation Controls
624        addNavigationPreferences();
625        
626        // Formats & Data
627        addFormatDataPreferences();
628        
629        // Advanced
630        if (!labelSet.contains(Constants.PREF_LIST_ADVANCED)) {
631            // due to issue with MemoryOption.getTextComponent, we don't
632            // want to do this again if Advanced tab is already built.
633            // (the heap size text field will disappear on second opening
634            //  of McV preferences window!)
635            addAdvancedPreferences();
636        }
637    }
638    
639    /**
640     * Build a {@link AddePreferences} panel {@literal "around"} the
641     * server manager {@link EntryStore}.
642     * 
643     * @see McIDASV#getServerManager()
644     */
645    public void addServerPreferences() {
646        EntryStore remoteAddeStore = ((McIDASV)getIdv()).getServerManager();
647        AddePreferences prefs = new AddePreferences(remoteAddeStore);
648        prefs.addPanel(this);
649    }
650    
651    /**
652     * Create the navigation preference panel
653     */
654    public void addNavigationPreferences() {
655        PreferenceManager navigationManager = new PreferenceManager() {
656            public void applyPreference(XmlObjectStore theStore, Object data) {
657//                System.err.println("applying nav prefs");
658            }
659        };
660        this.add(Constants.PREF_LIST_NAV_CONTROLS, "", navigationManager, makeEventPanel(), new Hashtable());
661    }
662    
663    /**
664     * Create the toolbar preference panel
665     */
666    public void addToolbarPreferences() {
667        if (toolbarEditor == null) {
668            toolbarEditor = 
669                new McvToolbarEditor((UIManager)getIdv().getIdvUIManager());
670        }
671        
672        PreferenceManager toolbarManager = new PreferenceManager() {
673            public void applyPreference(XmlObjectStore s, Object d) {
674                if (toolbarEditor.anyChanges() == true) {
675                    toolbarEditor.doApply();
676                    UIManager mngr = (UIManager)getIdv().getIdvUIManager();
677                    mngr.setCurrentToolbars(toolbarEditor);
678                }
679            }
680        };
681        this.add("Toolbar", "Toolbar icons", toolbarManager,
682                              toolbarEditor.getContents(), toolbarEditor);
683    }
684    
685    /**
686     * Make a checkbox preference panel
687     *
688     * @param objects Holds (Label, preference id, Boolean default value).
689     * If preference id is null then just show the label. If the entry is only length
690     * 2 (i.e., no value) then default to true.
691     * @param widgets The map to store the id to widget
692     * @param store  Where to look up the preference value
693     *
694     * @return The created panel
695     */
696    @SuppressWarnings("unchecked") // idv-style.
697    public static JPanel makePrefPanel(final Object[][] objects, final Hashtable widgets, final XmlObjectStore store) {
698        List<JComponent> comps = CollectionHelpers.arrList();
699        for (int i = 0; i < objects.length; i++) {
700            final String name = (String)objects[i][0];
701            final String id = (String)objects[i][1];
702            final boolean value = ((objects[i].length > 2) ? ((Boolean) objects[i][2]).booleanValue() : true);
703            
704            if (id == null) {
705                if (i > 0) {
706                    comps.add(new JLabel(" "));
707                }
708                comps.add(new JLabel(name));
709                continue;
710            }
711            
712            final JCheckBox cb = new JCheckBox(name, store.get(id, value));
713            cb.addPropertyChangeListener(new PropertyChangeListener() {
714                public void propertyChange(final PropertyChangeEvent e) {
715                    SwingUtilities.invokeLater(new Runnable() {
716                        public void run() {
717                            cb.setSelected(store.get(id, value));
718                        }
719                    });
720                }
721            });
722            if (objects[i].length > 3) {
723                cb.setToolTipText(objects[i][3].toString());
724            }
725            widgets.put(id, cb);
726            comps.add(cb);
727        }
728        return GuiUtils.top(GuiUtils.vbox(comps));
729    }
730    
731    public void addAdvancedPreferences() {
732        Hashtable<String, Component> widgets = new Hashtable<String, Component>();
733        
734        McIDASV mcv = (McIDASV)getIdv();
735        
736        // Build the startup options panel
737        final StartupManager startup = StartupManager.getInstance();
738        Platform platform = startup.getPlatform();
739        platform.setUserDirectory(
740                mcv.getObjectStore().getUserDirectory().toString());
741        platform.setAvailableMemory(GetMem.getMemory());
742        JPanel smPanel = startup.getAdvancedPanel(true);
743        List<JPanel> stuff = Collections.singletonList(smPanel);
744        
745        PreferenceManager advancedManager = new PreferenceManager() {
746            public void applyPreference(XmlObjectStore theStore, Object data) {
747                IdvPreferenceManager.applyWidgets((Hashtable)data, theStore);
748                startup.handleApply();
749            }
750        };
751        
752        JPanel outerPanel = new JPanel();
753        
754        // Outer panel layout
755        GroupLayout layout = new GroupLayout(outerPanel);
756        outerPanel.setLayout(layout);
757        layout.setHorizontalGroup(
758            layout.createParallelGroup(LEADING)
759            .addGroup(layout.createSequentialGroup()
760                .addContainerGap()
761                .addGroup(layout.createParallelGroup(LEADING)
762                    .addComponent(smPanel, TRAILING, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
763                .addContainerGap())
764        );
765        layout.setVerticalGroup(
766            layout.createParallelGroup(LEADING)
767            .addGroup(layout.createSequentialGroup()
768                .addContainerGap()
769                .addComponent(smPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
770                .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
771        );
772        
773        this.add(Constants.PREF_LIST_ADVANCED, "complicated stuff dude", 
774            advancedManager, outerPanel, widgets);
775    }
776    
777    /**
778     * Add in the user preference tab for the controls to show
779     */
780    protected void addDisplayPreferences() {
781        McIDASV mcv = (McIDASV)getIdv();
782        cbxToCdMap = new Hashtable<JCheckBox, ControlDescriptor>();
783        List<JPanel> compList = new ArrayList<JPanel>();
784        List<ControlDescriptor> controlDescriptors = 
785            getIdv().getAllControlDescriptors();
786            
787        final List<CheckboxCategoryPanel> catPanels = 
788            new ArrayList<CheckboxCategoryPanel>();
789            
790        final Hashtable<String, CheckboxCategoryPanel> catMap = 
791            new Hashtable<String, CheckboxCategoryPanel>();
792            
793        for (ControlDescriptor cd : controlDescriptors) {
794            
795            final String displayCategory = cd.getDisplayCategory();
796            
797            CheckboxCategoryPanel catPanel =
798                (CheckboxCategoryPanel) catMap.get(displayCategory);
799                
800            if (catPanel == null) {
801                catPanel = new CheckboxCategoryPanel(displayCategory, false);
802                catPanels.add(catPanel);
803                catMap.put(displayCategory, catPanel);
804                compList.add(catPanel.getTopPanel());
805                compList.add(catPanel);
806            }
807            
808            JCheckBox cbx = 
809                new JCheckBox(cd.getLabel(), shouldShowControl(cd, true));
810            cbx.setToolTipText(cd.getDescription());
811            cbxToCdMap.put(cbx, cd);
812            catPanel.addItem(cbx);
813            catPanel.add(GuiUtils.inset(cbx, new Insets(0, 20, 0, 0)));
814        }
815        
816        for (CheckboxCategoryPanel cbcp : catPanels) {
817            cbcp.checkVisCbx();
818        }
819        
820        final JButton allOn = new JButton("All on");
821        allOn.addActionListener(new ActionListener() {
822            public void actionPerformed(ActionEvent ae) {
823                for (CheckboxCategoryPanel cbcp : catPanels) {
824                    cbcp.toggleAll(true);
825                }
826            }
827        });
828        final JButton allOff = new JButton("All off");
829        allOff.addActionListener(new ActionListener() {
830            public void actionPerformed(ActionEvent ae) {
831                for (CheckboxCategoryPanel cbcp : catPanels) {
832                    cbcp.toggleAll(false);
833                }
834            }
835        });
836        
837        Boolean controlsAll =
838            (Boolean)mcv.getPreference(PROP_CONTROLDESCRIPTORS_ALL, Boolean.TRUE);
839        final JRadioButton useAllBtn = new JRadioButton("Use all displays",
840                                           controlsAll.booleanValue());
841        final JRadioButton useTheseBtn =
842            new JRadioButton("Use selected displays:",
843                             !controlsAll.booleanValue());
844        GuiUtils.buttonGroup(useAllBtn, useTheseBtn);
845        
846        final JPanel cbPanel = GuiUtils.top(GuiUtils.vbox(compList));
847        
848        JScrollPane cbScroller = new JScrollPane(cbPanel);
849        cbScroller.getVerticalScrollBar().setUnitIncrement(10);
850        cbScroller.setPreferredSize(new Dimension(300, 300));
851        
852        JComponent exportComp =
853            GuiUtils.right(GuiUtils.makeButton("Export to Plugin", this,
854                "exportControlsToPlugin"));
855                
856        JComponent cbComp = GuiUtils.centerBottom(cbScroller, exportComp);
857        
858        JPanel bottomPanel =
859            GuiUtils.leftCenter(
860                GuiUtils.inset(
861                    GuiUtils.top(GuiUtils.vbox(allOn, allOff)),
862                    4), new Msg.SkipPanel(
863                        GuiUtils.hgrid(
864                            Misc.newList(cbComp, GuiUtils.filler()), 0)));
865                            
866        JPanel controlsPanel =
867            GuiUtils.inset(GuiUtils.topCenter(GuiUtils.hbox(useAllBtn,
868                useTheseBtn), bottomPanel), 6);
869                
870        GuiUtils.enableTree(cbPanel, !useAllBtn.isSelected());
871        useAllBtn.addActionListener(new ActionListener() {
872            public void actionPerformed(ActionEvent ae) {
873                GuiUtils.enableTree(cbPanel, !useAllBtn.isSelected());
874                allOn.setEnabled(!useAllBtn.isSelected());
875                allOff.setEnabled(!useAllBtn.isSelected());
876            }
877        });
878        
879        useTheseBtn.addActionListener(new ActionListener() {
880            public void actionPerformed(ActionEvent ae) {
881                GuiUtils.enableTree(cbPanel, !useAllBtn.isSelected());
882                allOn.setEnabled(!useAllBtn.isSelected());
883                allOff.setEnabled(!useAllBtn.isSelected());
884            }
885        });
886        
887        GuiUtils.enableTree(cbPanel, !useAllBtn.isSelected());
888        
889        allOn.setEnabled(!useAllBtn.isSelected());
890        
891        allOff.setEnabled(!useAllBtn.isSelected());
892        
893        PreferenceManager controlsManager = new PreferenceManager() {
894            public void applyPreference(XmlObjectStore theStore, Object data) {
895                controlDescriptorsToShow = new Hashtable();
896                
897                Hashtable<JCheckBox, ControlDescriptor> table = (Hashtable)data;
898                
899                List<ControlDescriptor> controlDescriptors = getIdv().getAllControlDescriptors();
900                
901                for (Enumeration keys = table.keys(); keys.hasMoreElements(); ) {
902                    JCheckBox cbx = (JCheckBox) keys.nextElement();
903                    ControlDescriptor cd = (ControlDescriptor)table.get(cbx);
904                    controlDescriptorsToShow.put(cd.getControlId(), Boolean.valueOf(cbx.isSelected()));
905                }
906                
907                showAllControls = useAllBtn.isSelected();
908                
909                theStore.put(PROP_CONTROLDESCRIPTORS, controlDescriptorsToShow);
910                theStore.put(PROP_CONTROLDESCRIPTORS_ALL, Boolean.valueOf(showAllControls));
911            }
912        };
913        
914        this.add(Constants.PREF_LIST_AVAILABLE_DISPLAYS,
915                 "What displays should be available in the user interface?",
916                 controlsManager, controlsPanel, cbxToCdMap);
917    }
918    
919    protected void addDisplayWindowPreferences() {
920        
921        Hashtable<String, JCheckBox> widgets = new Hashtable<>();
922        MapViewManager mappy = new MapViewManager(getIdv());
923        
924        Object[][] legendObjects = {
925            { "Show Side Legend", MapViewManager.PREF_SHOWSIDELEGEND, Boolean.valueOf(mappy.getShowSideLegend()) },
926            { "Show Bottom Legend", MapViewManager.PREF_SHOWBOTTOMLEGEND, Boolean.valueOf(mappy.getShowBottomLegend()) }
927        };
928        JPanel legendPanel = makePrefPanel(legendObjects, widgets, getStore());
929        legendPanel.setBorder(BorderFactory.createTitledBorder("Legends"));
930        
931        Object[][] navigationObjects = {
932            { "Show Earth Navigation Panel", MapViewManager.PREF_SHOWEARTHNAVPANEL, Boolean.valueOf(mappy.getShowEarthNavPanel()) },
933            { "Show Viewpoint Toolbar", MapViewManager.PREF_SHOWTOOLBAR + "perspective" },
934            { "Show Zoom/Pan Toolbar", MapViewManager.PREF_SHOWTOOLBAR + "zoompan" },
935            { "Show Undo/Redo Toolbar", MapViewManager.PREF_SHOWTOOLBAR + "undoredo" }
936        };
937        JPanel navigationPanel = makePrefPanel(navigationObjects, widgets, getStore());
938        navigationPanel.setBorder(BorderFactory.createTitledBorder("Navigation Toolbars"));
939
940        String arLabel = "<html>"+MapViewManager.PR_LABEL+"<p>Changes do not affect current displays/layers.</html>";
941
942        Object[][] panelObjects = {
943            { "Show Globe Background", MapViewManager.PREF_SHOWGLOBEBACKGROUND, Boolean.valueOf(getStore().get(MapViewManager.PREF_SHOWGLOBEBACKGROUND, false)) },
944            { "Show Top Bar", MapViewManager.PREF_TOPBAR_VISIBLE, Boolean.valueOf(mappy.getTopBarVisible()) },
945            { "Show Wireframe Box", MapViewManager.PREF_WIREFRAME, Boolean.valueOf(mappy.getWireframe()) },
946            { "Show Cursor Readout", MapViewManager.PREF_SHOWCURSOR, Boolean.valueOf(mappy.getShowCursor()) },
947            { "Clip View At Box", MapViewManager.PREF_3DCLIP, Boolean.valueOf(mappy.getClipping()) },
948            { "Show Layer List in Panel", MapViewManager.PREF_SHOWDISPLAYLIST, Boolean.valueOf(mappy.getShowDisplayList()) },
949            { "Show Times In Panel", MapViewManager.PREF_ANIREADOUT, Boolean.valueOf(mappy.getAniReadout()) },
950            { "Show Map Display Scales", MapViewManager.PREF_SHOWSCALES, Boolean.valueOf(mappy.getLabelsVisible()) },
951            { "Show Transect Display Scales", MapViewManager.PREF_SHOWTRANSECTSCALES, Boolean.valueOf(mappy.getTransectLabelsVisible()) },
952            { "Show \"Please Wait\" Message", MapViewManager.PREF_WAITMSG, Boolean.valueOf(mappy.getWaitMessageVisible()) },
953            { "Reset Projection With New Data", MapViewManager.PREF_PROJ_USEFROMDATA },
954            { ViewManager.LABEL_AUTO_DEPTH, ViewManager.PREF_AUTO_DEPTH, mappy.getAutoDepth() },
955            { arLabel, MapViewManager.PREF_USE_PROGRESSIVE_RESOLUTION, Boolean.valueOf(getStore().get(MapViewManager.PREF_USE_PROGRESSIVE_RESOLUTION, false)) }
956        };
957        JPanel panelPanel = makePrefPanel(panelObjects, widgets, getStore());
958        JCheckBox adaptiveRezCheckBox = widgets.get(MapViewManager.PREF_USE_PROGRESSIVE_RESOLUTION);
959        adaptiveRezCheckBox.setVerticalTextPosition(SwingConstants.TOP);
960
961        panelPanel.setBorder(BorderFactory.createTitledBorder("Panel Configuration"));
962
963        XmlObjectStore store = getStore();
964        Color globeColor = mappy.getGlobeBackgroundColor();
965        Color bgColor = store.get(ViewManager.PREF_BGCOLOR, mappy.getBackground());
966        Color fgColor = store.get(ViewManager.PREF_FGCOLOR, mappy.getForeground());
967        Color borderColor = store.get(ViewManager.PREF_BORDERCOLOR, Constants.MCV_BLUE_DARK);
968        ColorSwatchComponent globeSwatch = new ColorSwatchComponent(store, globeColor, "Set Globe Background Color");
969        ColorSwatchComponent bgSwatch = new ColorSwatchComponent(store, bgColor, "Set Background Color");
970        ColorSwatchComponent fgSwatch = new ColorSwatchComponent(store, fgColor, "Set Foreground Color");
971        ColorSwatchComponent borderSwatch = new ColorSwatchComponent(store, borderColor, "Set Selected Panel Border Color");
972        final JComponent[] globeBg = { globeSwatch, globeSwatch.getSetButton(), globeSwatch.getClearButton() };
973        final JComponent[] bgComps = { bgSwatch, bgSwatch.getSetButton(), bgSwatch.getClearButton() };
974        final JComponent[] fgComps = { fgSwatch, fgSwatch.getSetButton(), fgSwatch.getClearButton() };
975        final JComponent[] border = { borderSwatch, borderSwatch.getSetButton(), borderSwatch.getClearButton() };
976
977        JPanel colorPanel = GuiUtils.vbox(
978                GuiUtils.hbox(
979                        McVGuiUtils.makeLabelRight("Globe Background:", Width.ONEHALF),
980                        GuiUtils.left(globeBg[0]),
981                        GAP_RELATED
982                ),
983                GuiUtils.hbox(
984                        McVGuiUtils.makeLabelRight("Background:", Width.ONEHALF),
985                        GuiUtils.left(bgComps[0]),
986                        GAP_RELATED
987                ),
988                GuiUtils.hbox(
989                        McVGuiUtils.makeLabelRight("Foreground:", Width.ONEHALF),
990                        GuiUtils.left(fgComps[0]),
991                        GAP_RELATED
992                ),
993                GuiUtils.hbox(
994                        McVGuiUtils.makeLabelRight("Selected Panel:", Width.ONEHALF),
995                        GuiUtils.left(border[0]),
996                        GAP_RELATED
997                )
998        );
999        
1000        colorPanel.setBorder(BorderFactory.createTitledBorder("Color Scheme"));
1001        
1002        final FontSelector fontSelector = new FontSelector(FontSelector.COMBOBOX_UI, false, false);
1003        Font f = getStore().get(MapViewManager.PREF_DISPLAYLISTFONT, mappy.getDisplayListFont());
1004        fontSelector.setFont(f);
1005        Color dlColor = store.get(MapViewManager.PREF_DISPLAYLISTCOLOR, mappy.getDisplayListColor());
1006        final ColorSwatchComponent dlColorWidget = new ColorSwatchComponent(store, dlColor, "Set Display List Color");
1007                
1008        JPanel fontPanel = GuiUtils.vbox(
1009            GuiUtils.hbox(
1010                McVGuiUtils.makeLabelRight("Font:", Width.ONEHALF),
1011                GuiUtils.left(fontSelector.getComponent()),
1012                GAP_RELATED
1013            ),
1014            GuiUtils.hbox(
1015                McVGuiUtils.makeLabelRight("Color:", Width.ONEHALF),
1016                GuiUtils.left(GuiUtils.hbox(dlColorWidget, dlColorWidget.getClearButton(), GAP_RELATED)),
1017                GAP_RELATED
1018            )
1019        );
1020        fontPanel.setBorder(BorderFactory.createTitledBorder("Layer List Properties"));
1021
1022        List<ProjectionImpl> projections = (List<ProjectionImpl>)mappy.getProjectionList();
1023        final JComboBox projBox = new JComboBox();
1024        final Map<String, ProjectionImpl> projectionNamesToObjects = new HashMap<>(projections.size());
1025
1026        Collection<String> projectionNames = new TreeSet<>(Collator.getInstance());
1027
1028        for (ProjectionImpl projection : projections) {
1029            String name = projection.getName();
1030            projectionNamesToObjects.put(name, projection);
1031            projectionNames.add(name);
1032        }
1033
1034        GuiUtils.setListData(projBox, projectionNames.toArray());
1035        Object defaultProj = mappy.getDefaultProjection();
1036        if (defaultProj != null) {
1037            if (defaultProj instanceof ProjectionImpl) {
1038                projBox.setSelectedItem(((ProjectionImpl)defaultProj).getName());
1039            } else {
1040                projBox.setSelectedItem(defaultProj);
1041            }
1042        }
1043
1044        JPanel projPanel = GuiUtils.left(projBox);
1045        projPanel.setBorder(BorderFactory.createTitledBorder("Default Projection"));
1046        
1047        McIDASV mcv = (McIDASV)getIdv();
1048        
1049        // START MCV INQUIRY 2718 changes
1050        // see the comments for the ViewManager.PREF_LOGO_CHANGED field
1051        // as well as Inquiry 2718 for more
1052        boolean logoViz = true;
1053        String logoPath = mcv.getStateManager().getPreferenceOrProperty(ViewManager.PREF_LOGO, Constants.ICON_MCIDASV_DEFAULT);
1054        if (getStore().getKeys().contains(ViewManager.PREF_LOGO_CHANGED)) {
1055            logoViz = mcv.getStateManager().getPreferenceOrProperty(
1056                ViewManager.PREF_LOGO_VISIBILITY, true);
1057//            logoPath = mcv.getStateManager().getPreferenceOrProperty(ViewManager.PREF_LOGO, Constants.ICON_MCIDASV_DEFAULT);
1058        }
1059        // END MCV INQUIRY 2718 changes
1060        
1061        final JCheckBox logoVizBox = new JCheckBox("Show Logo in View", logoViz);
1062        final JTextField logoField = new JTextField(logoPath);
1063        logoField.setToolTipText("Enter a file or URL");
1064
1065        JButton browseButton = new JButton("Browse..");
1066        browseButton.setToolTipText("Choose a logo from disk");
1067        browseButton.addActionListener(new ActionListener() {
1068            public void actionPerformed(ActionEvent ae) {
1069                String filename = FileManager.getReadFile(FileManager.FILTER_IMAGE);
1070                if (filename == null) {
1071                    return;
1072                }
1073                logoField.setText(filename);
1074            }
1075        });
1076
1077        // TJJ Jan 2018 - reworking Logo panel layout
1078        // http://mcidas.ssec.wisc.edu/inquiry-v/?inquiry=1933
1079        
1080        JPanel logoPanel = new JPanel();
1081        logoPanel.setLayout(new BoxLayout(logoPanel, BoxLayout.PAGE_AXIS));
1082        String[] logos = ViewManager.parseLogoPosition(
1083                        mcv.getStateManager().getPreferenceOrProperty(
1084                                        ViewManager.PREF_LOGO_POSITION_OFFSET, ""));
1085        final JComboBox logoPosBox = new JComboBox(ViewManager.logoPoses);
1086        logoPosBox.setToolTipText("Set the logo position on the screen");
1087        logoPosBox.setSelectedItem(ViewManager.findLoc(logos[0]));
1088
1089        final JTextField logoOffsetField = new JTextField(logos[1]);
1090        // provide enough space for 8 characters
1091        logoOffsetField.setColumns(8);
1092        logoOffsetField.setToolTipText("Set an offset from the position (x,y)");
1093
1094        JPanel logoScalePanel = new JPanel(new FlowLayout());
1095        float scaleMin = LOGO_SCALE_MIN;
1096        float scaleMax = LOGO_SCALE_MAX;
1097        float logoScaleFactor =
1098                (float) mcv.getStateManager().getPreferenceOrProperty(ViewManager.PREF_LOGO_SCALE, 1.0);
1099        final JSlider  logoScaleSlider = new JSlider(1, 20, 1);
1100        final JTextField logoSizeTextField = new JTextField("" + logoScaleFactor);
1101        // Legal values will never be more than three characters, e.g. 1.5
1102        logoSizeTextField.setColumns(3);
1103        ActionListener logoListener = new ActionListener() {
1104
1105            @Override
1106            public void actionPerformed(ActionEvent e) {
1107                try {
1108                    float val = Float.parseFloat(logoSizeTextField.getText());
1109                    if ((val >= scaleMin) && (val <= scaleMax)) {
1110                        logoScaleSlider.setValue((int) (val * 10));
1111                    } else {
1112                        JOptionPane.showMessageDialog(null, "Value provided is not a floating point number within valid range (0.1 to 2.0)");
1113                    }
1114                } catch (NumberFormatException nfe) {
1115                    JOptionPane.showMessageDialog(null, "Value provided is not a floating point number within valid range (0.1 to 2.0)");
1116                }
1117            }
1118            
1119        };
1120        logoSizeTextField.addActionListener(logoListener);
1121
1122        logoScaleSlider.setMinorTickSpacing(1);
1123        logoScaleSlider.setPaintTicks(true);
1124        
1125        // TJJ Jan 2018 - create custom labels since we want float values for scale multiplier
1126        Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
1127        labelTable.put(1, new JLabel("0.1"));
1128        labelTable.put(5, new JLabel("0.5"));
1129        labelTable.put(10, new JLabel("1.0"));
1130        labelTable.put(15, new JLabel("1.5"));
1131        labelTable.put(20, new JLabel("2.0"));
1132        logoScaleSlider.setLabelTable(labelTable);
1133        logoScaleSlider.setPaintLabels(true);
1134        logoScaleSlider.setValue((int) (logoScaleFactor * 10));
1135        ChangeListener listener = new ChangeListener() {
1136                public void stateChanged(ChangeEvent e) {
1137                        logoSizeTextField.setText("" + logoScaleSlider.getValue() / 10.f);
1138                }
1139        };
1140        logoScaleSlider.addChangeListener(listener);
1141        logoScaleSlider.setToolTipText("Change Logo Scale Value");
1142        
1143        logoScalePanel.add(logoSizeTextField);
1144        logoScalePanel.add(logoScaleSlider);
1145
1146        JPanel logoTop = new JPanel(new FlowLayout(FlowLayout.LEFT));
1147        logoTop.add(logoVizBox);
1148        
1149        JPanel logoMiddle = new JPanel(new BorderLayout());
1150        logoMiddle.add(logoField, BorderLayout.CENTER);
1151        logoMiddle.add(browseButton, BorderLayout.LINE_END);
1152        
1153        JPanel logoBottomOne = new JPanel(new FlowLayout(FlowLayout.LEFT));
1154        logoBottomOne.add(new JLabel("Screen Position:"));
1155        logoBottomOne.add(logoPosBox);
1156        logoBottomOne.add(new JLabel("Offset:"));
1157        logoBottomOne.add(logoOffsetField);
1158        JPanel logoBottomTwo = new JPanel(new FlowLayout(FlowLayout.LEFT));
1159        logoBottomTwo.add(new JLabel("Scale:"));
1160        logoBottomTwo.add(logoScalePanel);
1161        
1162        logoPanel.add(logoTop);
1163        logoPanel.add(logoMiddle);
1164        logoPanel.add(logoBottomOne);
1165        logoPanel.add(logoBottomTwo);
1166        
1167        logoPanel.setBorder(BorderFactory.createTitledBorder("Logo"));
1168        
1169        JPanel outerPanel = new JPanel();
1170        
1171        // Outer panel layout
1172        GroupLayout layout = new GroupLayout(outerPanel);
1173        outerPanel.setLayout(layout);
1174        layout.setHorizontalGroup(
1175            layout.createParallelGroup(LEADING)
1176            .addGroup(layout.createSequentialGroup()
1177                .addContainerGap()
1178                .addGroup(layout.createParallelGroup(LEADING)
1179                    .addComponent(navigationPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1180                    .addComponent(panelPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1181                .addGap(GAP_RELATED)
1182                .addGroup(layout.createParallelGroup(LEADING)
1183                    .addComponent(colorPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1184                    .addComponent(legendPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1185                    .addComponent(fontPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1186                    .addComponent(logoPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1187                    .addComponent(projPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1188                .addContainerGap())
1189        );
1190        layout.setVerticalGroup(
1191            layout.createParallelGroup(LEADING)
1192            .addGroup(layout.createSequentialGroup()
1193                .addContainerGap()
1194                .addGroup(layout.createParallelGroup(LEADING, false)
1195                    .addComponent(navigationPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1196                    .addComponent(legendPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1197                .addPreferredGap(RELATED)
1198                .addGroup(layout.createParallelGroup(LEADING, false)
1199                    .addGroup(layout.createSequentialGroup()
1200                        .addComponent(colorPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
1201                        .addPreferredGap(RELATED)
1202                        .addComponent(fontPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
1203                        .addPreferredGap(RELATED)
1204                        .addComponent(logoPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
1205                        .addPreferredGap(RELATED)
1206                        .addComponent(projPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
1207                    .addComponent(panelPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1208                .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
1209        );
1210        
1211        PreferenceManager miscManager = new PreferenceManager() {
1212            // applyWidgets called the same way the IDV does it.
1213            public void applyPreference(XmlObjectStore theStore, Object data) {
1214                savePrefsFromWidgets((Hashtable)data, theStore);
1215    
1216                // START MCV INQUIRY 2718 changes
1217                // see the comments for the ViewManager.PREF_LOGO_CHANGED field
1218                // as well as Inquiry 2718 for more
1219                theStore.put(ViewManager.PREF_LOGO_CHANGED, true);
1220                // END MCV INQUIRY 2718 changes
1221                
1222                Object projBoxSelection = projBox.getSelectedItem();
1223                if (projBoxSelection instanceof String) {
1224                    String projName = (String)projBoxSelection;
1225                    ProjectionImpl proj = projectionNamesToObjects.get(projName);
1226                    theStore.put(MapViewManager.PREF_PROJ_DFLT, proj);
1227                } else {
1228                    theStore.put(MapViewManager.PREF_PROJ_DFLT, projBoxSelection);
1229                }
1230
1231                theStore.put(MapViewManager.PREF_BGCOLOR, bgComps[0].getBackground());
1232                theStore.put(MapViewManager.PREF_FGCOLOR, fgComps[0].getBackground());
1233                theStore.put(MapViewManager.PREF_BORDERCOLOR, border[0].getBackground());
1234                theStore.put(MapViewManager.PREF_DISPLAYLISTFONT, fontSelector.getFont());
1235                theStore.put(MapViewManager.PREF_LOGO, logoField.getText());
1236                String lpos =
1237                    ((TwoFacedObject) logoPosBox.getSelectedItem()).getId()
1238                        .toString();
1239                String loff = logoOffsetField.getText().trim();
1240                theStore.put(MapViewManager.PREF_LOGO_POSITION_OFFSET,
1241                             ViewManager.makeLogoPosition(lpos, loff));
1242                theStore.put(MapViewManager.PREF_LOGO_VISIBILITY, logoVizBox.isSelected());
1243                theStore.put(MapViewManager.PREF_LOGO_SCALE,
1244                             logoScaleSlider.getValue() / 10f);
1245                theStore.put(MapViewManager.PREF_DISPLAYLISTCOLOR, dlColorWidget.getSwatchColor());
1246                theStore.put(MapViewManager.PREF_GLOBEBACKGROUND, globeBg[0].getBackground());
1247//                theStore.put(MapViewManager.PREF_USE_PROGRESSIVE_RESOLUTION, )
1248                ViewManager.setHighlightBorder(border[0].getBackground());
1249                EventBus.publish("McvPreference.ProgRez", theStore.get(MapViewManager.PREF_USE_PROGRESSIVE_RESOLUTION, false));
1250            }
1251        };
1252        
1253        this.add(Constants.PREF_LIST_VIEW, "Display Window Preferences", miscManager, outerPanel, widgets);
1254    }
1255
1256    private static void savePrefsFromWidgets(Hashtable widgets, XmlObjectStore store) {
1257        IdvPreferenceManager.applyWidgets(widgets, store);
1258        for (Enumeration keys = widgets.keys(); keys.hasMoreElements(); ) {
1259            String key = (String) keys.nextElement();
1260            Object widget = widgets.get(key);
1261            if (MapViewManager.PREF_USE_PROGRESSIVE_RESOLUTION.equals(key)) {
1262                store.put(key, ((JCheckBox)widget).isSelected());
1263            }
1264        }
1265    }
1266
1267    /**
1268     * Creates and adds the basic preference panel.
1269     */
1270    protected void addMcVPreferences() {
1271        
1272        Hashtable<String, Component> widgets = new Hashtable<String, Component>();
1273        McIDASV mcv = (McIDASV)getIdv();
1274        StateManager sm = (edu.wisc.ssec.mcidasv.StateManager)mcv.getStateManager();
1275        
1276        PreferenceManager basicManager = new PreferenceManager() {
1277            // IDV-style call to applyWidgets.
1278            public void applyPreference(XmlObjectStore theStore, Object data) {
1279                applyWidgets((Hashtable)data, theStore);
1280                getIdv().getIdvUIManager().setDateFormat();
1281                getIdv().getIdvUIManager().favoriteBundlesChanged();
1282                getIdv().initCacheManager();
1283                applyEventPreferences(theStore);
1284            }
1285        };
1286        
1287        boolean isPrerelease = sm.getIsPrerelease();
1288        Object[][] generalObjects = {
1289            { "Show McIDAS-V system bundles", PREF_SHOW_SYSTEM_BUNDLES, Boolean.TRUE },
1290            { "Show Help Tips on start", HelpTipDialog.PREF_HELPTIPSHOW },
1291            { "Show Data Explorer on start", PREF_SHOWDASHBOARD, Boolean.TRUE },
1292            { "Check for new version and notice on start", Constants.PREF_VERSION_CHECK, Boolean.TRUE },
1293            { "Include prereleases in version check", Constants.PREF_PRERELEASE_CHECK, isPrerelease },
1294            { "Confirm before exiting", PREF_SHOWQUITCONFIRM },
1295            { "Automatically save default layout at exit", Constants.PREF_AUTO_SAVE_DEFAULT_LAYOUT, Boolean.FALSE },
1296            { "Save visibility of Data Explorer", Constants.PREF_SAVE_DASHBOARD_VIZ, Boolean.FALSE },
1297            { "Confirm removal of all data sources", PREF_CONFIRM_REMOVE_DATA, Boolean.TRUE },
1298            { "Confirm removal of all layers", PREF_CONFIRM_REMOVE_LAYERS, Boolean.TRUE },
1299            { "Confirm removal of all layers and data sources", PREF_CONFIRM_REMOVE_BOTH, Boolean.TRUE },
1300        };
1301        final IdvObjectStore store = getStore();
1302        JPanel generalPanel = makePrefPanel(generalObjects, widgets, store);
1303        generalPanel.setBorder(BorderFactory.createTitledBorder("General"));
1304        
1305        // Turn what used to be a set of checkboxes into a corresponding menu selection
1306        // The options have to be checkboxes in the widget collection
1307        // That way "applyWidgets" will work as expected
1308        boolean shouldRemove = store.get(PREF_OPEN_REMOVE, false);
1309        boolean shouldMerge  = store.get(PREF_OPEN_MERGE, false);
1310        final JCheckBox shouldRemoveCbx = new JCheckBox("You shouldn't see this", shouldRemove);
1311        final JCheckBox shouldMergeCbx  = new JCheckBox("You shouldn't see this", shouldMerge);
1312        widgets.put(PREF_OPEN_REMOVE, shouldRemoveCbx);
1313        widgets.put(PREF_OPEN_MERGE, shouldMergeCbx);
1314        
1315        final JComboBox loadComboBox = new JComboBox(loadComboOptions);
1316        loadComboBox.addActionListener(new ActionListener() {
1317            public void actionPerformed(ActionEvent e) {
1318                switch (((JComboBox)e.getSource()).getSelectedIndex()) {
1319                case 0:
1320                    shouldRemoveCbx.setSelected(false);
1321                    shouldMergeCbx.setSelected(false);
1322                    break;
1323                case 1:
1324                    shouldRemoveCbx.setSelected(true);
1325                    shouldMergeCbx.setSelected(false);
1326                    break;
1327                case 2:
1328                    shouldRemoveCbx.setSelected(false);
1329                    shouldMergeCbx.setSelected(true);
1330                    break;
1331                case 3:
1332                    shouldRemoveCbx.setSelected(true);
1333                    shouldMergeCbx.setSelected(true);
1334                    break;
1335                }
1336            }
1337        });
1338        
1339        // update the bundle loading options upon visibility changes.
1340        loadComboBox.addPropertyChangeListener(new PropertyChangeListener() {
1341            public void propertyChange(final PropertyChangeEvent e) {
1342                String prop = e.getPropertyName();
1343                if (!"ancestor".equals(prop) && !"Frame.active".equals(prop)) {
1344                    return;
1345                }
1346                
1347                boolean remove = store.get(PREF_OPEN_REMOVE, false);
1348                boolean merge  = store.get(PREF_OPEN_MERGE, false);
1349                
1350                if (!remove) {
1351                    if (!merge) { 
1352                        loadComboBox.setSelectedIndex(0);
1353                    } else {
1354                        loadComboBox.setSelectedIndex(2);
1355                    }
1356                }
1357                else {
1358                    if (!merge) {
1359                        loadComboBox.setSelectedIndex(1);
1360                    } else {
1361                        loadComboBox.setSelectedIndex(3);
1362                    }
1363                }
1364            }
1365        });
1366        
1367        if (!shouldRemove) {
1368            if (!shouldMerge) {
1369                loadComboBox.setSelectedIndex(0);
1370            } else {
1371                loadComboBox.setSelectedIndex(2);
1372            }
1373        }
1374        else {
1375            if (!shouldMerge) {
1376                loadComboBox.setSelectedIndex(1);
1377            } else { 
1378                loadComboBox.setSelectedIndex(3);
1379            }
1380        }
1381        
1382        Object[][] bundleObjects = {
1383            { "Prompt when opening bundles", PREF_OPEN_ASK },
1384            { "Prompt for location for zipped data", PREF_ZIDV_ASK }
1385        };
1386        JPanel bundlePanelInner = makePrefPanel(bundleObjects, widgets, getStore());
1387        JPanel bundlePanel = GuiUtils.topCenter(loadComboBox, bundlePanelInner);
1388        bundlePanel.setBorder(BorderFactory.createTitledBorder("When Opening a Bundle"));
1389        
1390        Object[][] layerObjects = {
1391            { "Show windows when they are created", PREF_SHOWCONTROLWINDOW },
1392            { "Use fast rendering", PREF_FAST_RENDER, Boolean.FALSE, "<html>Turn this on for better performance at the risk of having funky displays</html>" },
1393            { "Auto-select data when loading a template", IdvConstants.PREF_AUTOSELECTDATA, Boolean.FALSE, "<html>When loading a display template should the data be automatically selected</html>" },
1394        };
1395        JPanel layerPanel = makePrefPanel(layerObjects, widgets, getStore());
1396        layerPanel.setBorder(BorderFactory.createTitledBorder("Layer Controls"));
1397        
1398        Object[][] layerclosedObjects = {
1399            { "Remove the display", DisplayControl.PREF_REMOVEONWINDOWCLOSE, Boolean.FALSE },
1400            { "Remove standalone displays", DisplayControl.PREF_STANDALONE_REMOVEONCLOSE, Boolean.FALSE }
1401        };
1402        JPanel layerclosedPanel = makePrefPanel(layerclosedObjects, widgets, getStore());
1403        layerclosedPanel.setBorder(BorderFactory.createTitledBorder("When Layer Control Window is Closed"));
1404        
1405        JPanel outerPanel = new JPanel();
1406        
1407        // Outer panel layout
1408        GroupLayout layout = new GroupLayout(outerPanel);
1409        outerPanel.setLayout(layout);
1410        layout.setHorizontalGroup(
1411            layout.createParallelGroup(LEADING)
1412            .addGroup(layout.createSequentialGroup()
1413                .addContainerGap()
1414                .addGroup(layout.createParallelGroup(TRAILING)
1415                    .addComponent(generalPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1416                    .addComponent(layerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1417                .addGap(GAP_RELATED)
1418                .addGroup(layout.createParallelGroup(LEADING)
1419                    .addComponent(bundlePanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1420                    .addComponent(layerclosedPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1421                .addContainerGap())
1422        );
1423        layout.setVerticalGroup(
1424            layout.createParallelGroup(LEADING)
1425            .addGroup(layout.createSequentialGroup()
1426                .addContainerGap()
1427                .addGroup(layout.createParallelGroup(LEADING, false)
1428                    .addComponent(bundlePanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1429                    .addComponent(generalPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1430                .addPreferredGap(RELATED)
1431                .addGroup(layout.createParallelGroup(LEADING, false)
1432                    .addComponent(layerclosedPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1433                    .addComponent(layerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1434                .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
1435        );
1436        this.add(Constants.PREF_LIST_GENERAL, "General Preferences", basicManager, outerPanel, widgets);
1437    }
1438    
1439    /**
1440     * <p>This determines whether the IDV should do a remove display and data 
1441     * before a bundle is loaded. It returns a 2 element boolean array. The 
1442     * first element is whether the open should take place at all. The second 
1443     * element determines whether displays and data should be removed before 
1444     * the load.</p>
1445     *
1446     * <p>Overridden by McIDAS-V so that we can ask the user whether or not we
1447     * should limit the number of new windows a bundle can create.</p>
1448     *
1449     * @param name Bundle name - may be null.
1450     *
1451     * @return Element 0: did user hit cancel; Element 1: Should remove data 
1452     *         and displays; Element 2: limit new windows.
1453     * 
1454     * @see IdvPreferenceManager#getDoRemoveBeforeOpening(String)
1455     */
1456    @Override public boolean[] getDoRemoveBeforeOpening(String name) {
1457        IdvObjectStore store = getStore();
1458        boolean shouldAsk    = store.get(PREF_OPEN_ASK, true);
1459        boolean shouldRemove = store.get(PREF_OPEN_REMOVE, false);
1460        boolean shouldMerge  = store.get(PREF_OPEN_MERGE, false);
1461        
1462        if (shouldAsk) {
1463            JComboBox loadComboBox = new JComboBox(loadComboOptions);
1464            JCheckBox preferenceCbx = new JCheckBox("Save as default preference", true);
1465            JCheckBox askCbx = new JCheckBox("Don't show this window again", false);
1466            
1467            if (!shouldRemove) {
1468                if (!shouldMerge) {
1469                    loadComboBox.setSelectedIndex(0);
1470                } else { 
1471                    loadComboBox.setSelectedIndex(2);
1472                }
1473            }
1474            else {
1475                if (!shouldMerge) {
1476                    loadComboBox.setSelectedIndex(1);
1477                } else {
1478                    loadComboBox.setSelectedIndex(3);
1479                }
1480            }
1481            
1482            JPanel inner = new JPanel();
1483            GroupLayout layout = new GroupLayout(inner);
1484            inner.setLayout(layout);
1485            layout.setHorizontalGroup(
1486                layout.createParallelGroup(LEADING)
1487                    .addGroup(layout.createSequentialGroup()
1488                        .addContainerGap()
1489                        .addGroup(layout.createParallelGroup(LEADING)
1490                            .addComponent(loadComboBox, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1491                            .addComponent(preferenceCbx)
1492                            .addComponent(askCbx))
1493                        .addContainerGap())
1494            );
1495            layout.setVerticalGroup(
1496                layout.createParallelGroup(LEADING)
1497                    .addGroup(layout.createSequentialGroup()
1498                        .addContainerGap()
1499                        .addComponent(loadComboBox, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
1500                        .addPreferredGap(RELATED)
1501                        .addComponent(preferenceCbx)
1502                        .addPreferredGap(RELATED)
1503                        .addComponent(askCbx)
1504                        .addContainerGap())
1505            );
1506            
1507            if (!GuiUtils.showOkCancelDialog(null, "Open bundle", inner, null)) {
1508                return new boolean[] { false, false, false };
1509            }
1510            
1511            switch (loadComboBox.getSelectedIndex()) {
1512                case 0: // new windows
1513                    shouldRemove = false;
1514                    shouldMerge = false;
1515                    break;
1516                case 1: // merge with existing tabs
1517                    shouldRemove = true;
1518                    shouldMerge = false;
1519                    break;
1520                case 2: // add new tab(s) to current
1521                    shouldRemove = false;
1522                    shouldMerge = true;
1523                    break;
1524                case 3: // replace session
1525                    shouldRemove = true;
1526                    shouldMerge = true;
1527                    break;
1528            }
1529            
1530            // Save these as default preference if the user wants to
1531            if (preferenceCbx.isSelected()) {
1532                store.put(PREF_OPEN_REMOVE, shouldRemove);
1533                store.put(PREF_OPEN_MERGE, shouldMerge);
1534            }
1535            store.put(PREF_OPEN_ASK, !askCbx.isSelected());
1536        }
1537        return new boolean[] { true, shouldRemove, shouldMerge };
1538    }
1539    
1540    /**
1541     * Creates and adds the formats and data preference panel.
1542     */
1543    protected void addFormatDataPreferences() {
1544        Hashtable<String, Component> widgets = new Hashtable<String, Component>();
1545        
1546        JPanel formatPanel = new JPanel();
1547        formatPanel.setBorder(BorderFactory.createTitledBorder("Formats"));
1548        
1549        // Date stuff
1550        JLabel dateLabel = McVGuiUtils.makeLabelRight("Date Format:", Width.ONEHALF);
1551        
1552        String dateFormat = getStore().get(PREF_DATE_FORMAT, DEFAULT_DATE_FORMAT);
1553        
1554        if (!dateFormats.contains(dateFormat)) {
1555            dateFormats.add(dateFormat);
1556        }
1557        
1558        final JComboBox dateComboBox = McVGuiUtils.makeComboBox(dateFormats, dateFormat, Width.TRIPLE);
1559        widgets.put(PREF_DATE_FORMAT, dateComboBox);
1560        
1561        JComponent dateHelpButton = getIdv().makeHelpButton("idv.tools.preferences.dateformat");
1562        
1563        JLabel dateExLabel = new JLabel("");
1564        
1565        // Time stuff
1566        JLabel timeLabel = McVGuiUtils.makeLabelRight("Time Zone:", Width.ONEHALF);
1567        
1568        String timeString = getStore().get(PREF_TIMEZONE, DEFAULT_TIMEZONE);
1569        String[] zoneStrings = TimeZone.getAvailableIDs();
1570        Arrays.sort(zoneStrings);
1571        
1572        final JComboBox timeComboBox = McVGuiUtils.makeComboBox(zoneStrings, timeString, Width.TRIPLE);
1573        widgets.put(PREF_TIMEZONE, timeComboBox);
1574        
1575        try {
1576            dateExLabel.setText("ex:  " + new DateTime().toString());
1577        } catch (Exception ve) {
1578            dateExLabel.setText("Can't format date: " + ve);
1579        }
1580        
1581        ObjectListener timeLabelListener = new ObjectListener(dateExLabel) {
1582            public void actionPerformed(ActionEvent ae) {
1583                JLabel label  = (JLabel) theObject;
1584                String format = dateComboBox.getSelectedItem().toString();
1585                String zone = timeComboBox.getSelectedItem().toString();
1586                try {
1587                    TimeZone tz = TimeZone.getTimeZone(zone);
1588                    // hack to make it the DateTime default
1589                    if (format.equals(DEFAULT_DATE_FORMAT)) {
1590                        if (zone.equals(DEFAULT_TIMEZONE)) {
1591                            format = DateTime.DEFAULT_TIME_FORMAT + "'Z'";
1592                        }
1593                    }
1594                    label.setText("ex:  " + new DateTime().formattedString(format, tz));
1595                } catch (Exception ve) {
1596                    label.setText("Invalid format or time zone");
1597                    LogUtil.userMessage("Invalid format or time zone");
1598                }
1599            }
1600        };
1601        dateComboBox.addActionListener(timeLabelListener);
1602        timeComboBox.addActionListener(timeLabelListener);
1603        
1604        // Lat/Lon stuff
1605        JLabel latlonLabel = McVGuiUtils.makeLabelRight("Lat/Lon Format:", Width.ONEHALF);
1606        
1607        String latlonFormatString = getStore().get(PREF_LATLON_FORMAT, "##0.0");
1608        JComboBox latlonComboBox = McVGuiUtils.makeComboBox(defaultLatLonFormats, latlonFormatString, Width.TRIPLE);
1609        widgets.put(PREF_LATLON_FORMAT, latlonComboBox);
1610        
1611        JComponent latlonHelpButton = getIdv().makeHelpButton("idv.tools.preferences.latlonformat");
1612        
1613        JLabel latlonExLabel = new JLabel("");
1614        
1615        try {
1616            latlonFormat.applyPattern(latlonFormatString);
1617            latlonExLabel.setText("ex: " + latlonFormat.format(latlonValue));
1618        } catch (IllegalArgumentException iae) {
1619            latlonExLabel.setText("Bad format: " + latlonFormatString);
1620        }
1621        latlonComboBox.addActionListener(new ObjectListener(latlonExLabel) {
1622            public void actionPerformed(final ActionEvent ae) {
1623                JLabel label = (JLabel)theObject;
1624                JComboBox box = (JComboBox)ae.getSource();
1625                String pattern = box.getSelectedItem().toString();
1626                try {
1627                    latlonFormat.applyPattern(pattern);
1628                    label.setText("ex: " + latlonFormat.format(latlonValue));
1629                } catch (IllegalArgumentException iae) {
1630                    label.setText("bad pattern: " + pattern);
1631                    LogUtil.userMessage("Bad format:" + pattern);
1632                }
1633            }
1634        });
1635        
1636        // Probe stuff
1637        JLabel probeLabel = McVGuiUtils.makeLabelRight("Probe Format:", Width.ONEHALF);
1638        
1639        String probeFormat = getStore().get(DisplayControl.PREF_PROBEFORMAT, DisplayControl.DEFAULT_PROBEFORMAT);
1640
1641        JComboBox probeComboBox = McVGuiUtils.makeComboBox(probeFormatsList, probeFormat, Width.TRIPLE);
1642        widgets.put(DisplayControl.PREF_PROBEFORMAT, probeComboBox);
1643
1644        JComponent probeHelpButton = getIdv().makeHelpButton("idv.tools.preferences.probeformat");
1645
1646        // Distance stuff
1647        JLabel distanceLabel = McVGuiUtils.makeLabelRight("Distance Unit:", Width.ONEHALF);
1648        
1649        Unit distanceUnit = null;
1650        try {
1651            distanceUnit = ucar.visad.Util.parseUnit(getStore().get(PREF_DISTANCEUNIT, "km"));
1652        } catch (Exception exc) {}
1653        JComboBox distanceComboBox = getIdv().getDisplayConventions().makeUnitBox(distanceUnit, null);
1654        McVGuiUtils.setComponentWidth(distanceComboBox, Width.TRIPLE);
1655        widgets.put(PREF_DISTANCEUNIT, distanceComboBox);
1656
1657        // Locale stuff (largely ripped out of IDV prefs)
1658        JLabel localeLabel = McVGuiUtils.makeLabelRight("Number Style:", Width.ONEHALF);
1659        String defaultLocale = getStore().get(PREF_LOCALE, "SYSTEM_LOCALE");
1660        JRadioButton sysLocale = new JRadioButton("System Default",
1661            defaultLocale.equals("SYSTEM_LOCALE"));
1662
1663        sysLocale.setToolTipText(
1664            "Use the system default locale for number formatting");
1665
1666        JRadioButton usLocale = new JRadioButton("English/US",
1667            !defaultLocale.equals("SYSTEM_LOCALE"));
1668
1669        usLocale.setToolTipText("Use the US number formatting");
1670        GuiUtils.buttonGroup(sysLocale, usLocale);
1671        widgets.put("SYSTEM_LOCALE", sysLocale);
1672        widgets.put("US_LOCALE", usLocale);
1673
1674        // Format panel layout
1675        GroupLayout formatLayout = new GroupLayout(formatPanel);
1676        formatPanel.setLayout(formatLayout);
1677        formatLayout.setHorizontalGroup(
1678            formatLayout.createParallelGroup(LEADING)
1679            .addGroup(formatLayout.createSequentialGroup()
1680                .addContainerGap()
1681                .addGroup(formatLayout.createParallelGroup(LEADING)
1682                    .addGroup(formatLayout.createSequentialGroup()
1683                        .addComponent(dateLabel)
1684                        .addGap(GAP_RELATED)
1685                        .addComponent(dateComboBox)
1686                        .addGap(GAP_RELATED)
1687                        .addComponent(dateHelpButton)
1688                        .addGap(GAP_RELATED)
1689                        .addComponent(dateExLabel))
1690                    .addGroup(formatLayout.createSequentialGroup()
1691                        .addComponent(timeLabel)
1692                        .addGap(GAP_RELATED)
1693                        .addComponent(timeComboBox))
1694                    .addGroup(formatLayout.createSequentialGroup()
1695                        .addComponent(latlonLabel)
1696                        .addGap(GAP_RELATED)
1697                        .addComponent(latlonComboBox)
1698                        .addGap(GAP_RELATED)
1699                        .addComponent(latlonHelpButton)
1700                        .addGap(GAP_RELATED)
1701                        .addComponent(latlonExLabel))
1702                    .addGroup(formatLayout.createSequentialGroup()
1703                        .addComponent(probeLabel)
1704                        .addGap(GAP_RELATED)
1705                        .addComponent(probeComboBox)
1706                        .addGap(GAP_RELATED)
1707                        .addComponent(probeHelpButton))
1708                    .addGroup(formatLayout.createSequentialGroup()
1709                        .addComponent(distanceLabel)
1710                        .addGap(GAP_RELATED)
1711                        .addComponent(distanceComboBox))
1712                    .addGroup(formatLayout.createSequentialGroup()
1713                        .addComponent(localeLabel)
1714                        .addGap(GAP_RELATED)
1715                        .addComponent(sysLocale)
1716                        .addGap(GAP_RELATED)
1717                        .addComponent(usLocale)))
1718                .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
1719        );
1720        formatLayout.setVerticalGroup(
1721            formatLayout.createParallelGroup(LEADING)
1722            .addGroup(formatLayout.createSequentialGroup()
1723                .addGroup(formatLayout.createParallelGroup(BASELINE)
1724                    .addComponent(dateComboBox)
1725                    .addComponent(dateLabel)
1726                    .addComponent(dateHelpButton)
1727                    .addComponent(dateExLabel))
1728                .addPreferredGap(RELATED)
1729                .addGroup(formatLayout.createParallelGroup(BASELINE)
1730                    .addComponent(timeComboBox)
1731                    .addComponent(timeLabel))
1732                .addPreferredGap(RELATED)
1733                .addGroup(formatLayout.createParallelGroup(BASELINE)
1734                    .addComponent(latlonLabel)
1735                    .addComponent(latlonComboBox)
1736                    .addComponent(latlonHelpButton)
1737                    .addComponent(latlonExLabel))
1738                .addPreferredGap(RELATED)
1739                .addGroup(formatLayout.createParallelGroup(BASELINE)
1740                    .addComponent(probeComboBox)
1741                    .addComponent(probeLabel)
1742                    .addComponent(probeHelpButton))
1743                .addPreferredGap(RELATED)
1744                .addGroup(formatLayout.createParallelGroup(BASELINE)
1745                    .addComponent(distanceComboBox)
1746                    .addComponent(distanceLabel))
1747                .addPreferredGap(RELATED)
1748                .addGroup(formatLayout.createParallelGroup(BASELINE)
1749                    .addComponent(localeLabel)
1750                    .addComponent(sysLocale)
1751                    .addComponent(usLocale))
1752                .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
1753        );
1754        
1755        JPanel dataPanel = new JPanel();
1756        dataPanel.setBorder(BorderFactory.createTitledBorder("Data"));
1757
1758        // Sampling stuff
1759        JLabel sampleLabel = McVGuiUtils.makeLabelRight("Sampling Mode:", Width.ONEHALF);
1760        
1761        String sampleValue = getStore().get(PREF_SAMPLINGMODE, DisplayControlImpl.WEIGHTED_AVERAGE);
1762        JRadioButton sampleWA = new JRadioButton(DisplayControlImpl.WEIGHTED_AVERAGE,
1763            sampleValue.equals(DisplayControlImpl.WEIGHTED_AVERAGE));
1764            
1765        sampleWA.setToolTipText("Use a weighted average sampling");
1766        JRadioButton sampleNN = new JRadioButton(DisplayControlImpl.NEAREST_NEIGHBOR,
1767            sampleValue.equals(DisplayControlImpl.NEAREST_NEIGHBOR));
1768            
1769        sampleNN.setToolTipText("Use a nearest neighbor sampling");
1770        GuiUtils.buttonGroup(sampleWA, sampleNN);
1771        widgets.put("WEIGHTED_AVERAGE", sampleWA);
1772        widgets.put("NEAREST_NEIGHBOR", sampleNN);
1773        
1774        // Pressure stuff
1775        JLabel verticalLabel = McVGuiUtils.makeLabelRight("Pressure to Height:", Width.ONEHALF);
1776        
1777        String verticalValue = getStore().get(PREF_VERTICALCS, DataUtil.STD_ATMOSPHERE);
1778        JRadioButton verticalSA = new JRadioButton("Standard Atmosphere", verticalValue.equals(DataUtil.STD_ATMOSPHERE));
1779        verticalSA.setToolTipText("Use a standard atmosphere height approximation");
1780        JRadioButton verticalV5D = new JRadioButton("Vis5D", verticalValue.equals(DataUtil.VIS5D_VERTICALCS));
1781        verticalV5D.setToolTipText("Use the Vis5D vertical transformation");
1782        GuiUtils.buttonGroup(verticalSA, verticalV5D);
1783        widgets.put(DataUtil.STD_ATMOSPHERE, verticalSA);
1784        widgets.put(DataUtil.VIS5D_VERTICALCS, verticalV5D);
1785        
1786        // Caching stuff
1787        JLabel cacheLabel = McVGuiUtils.makeLabelRight("Caching:", Width.ONEHALF);
1788        
1789        JCheckBox cacheCheckBox = new JCheckBox("Cache Data in Memory", getStore().get(PREF_DOCACHE, true));
1790        widgets.put(PREF_DOCACHE, cacheCheckBox);
1791        
1792        JLabel cacheEmptyLabel = McVGuiUtils.makeLabelRight("", Width.ONEHALF);
1793        
1794        JTextField cacheTextField = McVGuiUtils.makeTextField(Misc.format(getStore().get(PREF_CACHESIZE, 20.0)));
1795        JComponent cacheTextFieldComponent = GuiUtils.hbox(new JLabel("Disk Cache Size: "), cacheTextField, new JLabel(" megabytes"));
1796        widgets.put(PREF_CACHESIZE, cacheTextField);
1797        
1798        // Image stuff
1799        JLabel imageLabel = McVGuiUtils.makeLabelRight("Max Image Size:", Width.ONEHALF);
1800        
1801        JTextField imageField = McVGuiUtils.makeTextField(Misc.format(getStore().get(PREF_MAXIMAGESIZE, -1)));
1802        JComponent imageFieldComponent = GuiUtils.hbox(imageField, new JLabel(" pixels (-1 = no limit)"));
1803        widgets.put(PREF_MAXIMAGESIZE, imageField);
1804        
1805        // Grid stuff
1806        JLabel gridLabel = McVGuiUtils.makeLabelRight("Grid Threshold:", Width.ONEHALF);
1807        
1808        JTextField gridField = McVGuiUtils.makeTextField(Misc.format(getStore().get(PREF_FIELD_CACHETHRESHOLD, 1000000.)));
1809        JComponent gridFieldComponent = GuiUtils.hbox(gridField, new JLabel(" bytes (Cache grids larger than this to disk)"));
1810        widgets.put(PREF_FIELD_CACHETHRESHOLD, gridField);
1811        
1812        // Data panel layout
1813        GroupLayout dataLayout = new GroupLayout(dataPanel);
1814        dataPanel.setLayout(dataLayout);
1815        dataLayout.setHorizontalGroup(
1816            dataLayout.createParallelGroup(LEADING)
1817            .addGroup(dataLayout.createSequentialGroup()
1818                .addContainerGap()
1819                .addGroup(dataLayout.createParallelGroup(LEADING)
1820                    .addGroup(dataLayout.createSequentialGroup()
1821                        .addComponent(sampleLabel)
1822                        .addGap(GAP_RELATED)
1823                        .addComponent(sampleWA)
1824                        .addGap(GAP_RELATED)
1825                        .addComponent(sampleNN))
1826                    .addGroup(dataLayout.createSequentialGroup()
1827                        .addComponent(verticalLabel)
1828                        .addGap(GAP_RELATED)
1829                        .addComponent(verticalSA)
1830                        .addGap(GAP_RELATED)
1831                        .addComponent(verticalV5D))
1832                    .addGroup(dataLayout.createSequentialGroup()
1833                        .addComponent(cacheLabel)
1834                        .addGap(GAP_RELATED)
1835                        .addComponent(cacheCheckBox))
1836                    .addGroup(dataLayout.createSequentialGroup()
1837                        .addComponent(cacheEmptyLabel)
1838                        .addGap(GAP_RELATED)
1839                        .addComponent(cacheTextFieldComponent))
1840                    .addGroup(dataLayout.createSequentialGroup()
1841                        .addComponent(imageLabel)
1842                        .addGap(GAP_RELATED)
1843                        .addComponent(imageFieldComponent))
1844                    .addGroup(dataLayout.createSequentialGroup()
1845                        .addComponent(gridLabel)
1846                        .addGap(GAP_RELATED)
1847                        .addComponent(gridFieldComponent)))
1848                .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
1849        );
1850        dataLayout.setVerticalGroup(
1851            dataLayout.createParallelGroup(LEADING)
1852            .addGroup(dataLayout.createSequentialGroup()
1853                .addGroup(dataLayout.createParallelGroup(BASELINE)
1854                    .addComponent(sampleLabel)
1855                    .addComponent(sampleWA)
1856                    .addComponent(sampleNN))
1857                .addPreferredGap(RELATED)
1858                .addGroup(dataLayout.createParallelGroup(BASELINE)
1859                    .addComponent(verticalLabel)
1860                    .addComponent(verticalSA)
1861                    .addComponent(verticalV5D))
1862                .addPreferredGap(RELATED)
1863                .addGroup(dataLayout.createParallelGroup(BASELINE)
1864                    .addComponent(cacheLabel)
1865                    .addComponent(cacheCheckBox))
1866                .addPreferredGap(RELATED)
1867                .addGroup(dataLayout.createParallelGroup(BASELINE)
1868                    .addComponent(cacheEmptyLabel)
1869                    .addComponent(cacheTextFieldComponent))
1870                .addPreferredGap(RELATED)
1871                .addGroup(dataLayout.createParallelGroup(BASELINE)
1872                    .addComponent(imageLabel)
1873                    .addComponent(imageFieldComponent))
1874                .addPreferredGap(RELATED)
1875                .addGroup(dataLayout.createParallelGroup(BASELINE)
1876                    .addComponent(gridLabel)
1877                    .addComponent(gridFieldComponent))
1878                .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
1879        ); 
1880        
1881        JPanel outerPanel = new JPanel();
1882        
1883        // Outer panel layout
1884        GroupLayout layout = new GroupLayout(outerPanel);
1885        outerPanel.setLayout(layout);
1886        layout.setHorizontalGroup(
1887            layout.createParallelGroup(LEADING)
1888            .addGroup(layout.createSequentialGroup()
1889                .addContainerGap()
1890                .addGroup(layout.createParallelGroup(LEADING)
1891                    .addComponent(formatPanel, TRAILING, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1892                    .addComponent(dataPanel, TRAILING, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1893                .addContainerGap())
1894        );
1895        layout.setVerticalGroup(
1896            layout.createParallelGroup(LEADING)
1897            .addGroup(layout.createSequentialGroup()
1898                .addContainerGap()
1899                .addComponent(formatPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
1900                .addGap(GAP_UNRELATED)
1901                .addComponent(dataPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
1902                .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
1903        );
1904        
1905        PreferenceManager formatsManager = new PreferenceManager() {
1906            public void applyPreference(XmlObjectStore theStore, Object data) {
1907                IdvPreferenceManager.applyWidgets((Hashtable)data, theStore);
1908                
1909                // if we ever need to add formats and data prefs, here's where
1910                // they get saved off (unless we override applyWidgets).
1911            }
1912        };
1913        
1914        this.add(Constants.PREF_LIST_FORMATS_DATA, "", formatsManager, outerPanel, widgets);
1915    }
1916    
1917    /**
1918     * Add in the user preference tab for the choosers to show.
1919     */
1920    protected void addChooserPreferences() {
1921        Hashtable<String, JCheckBox> choosersData = new Hashtable<String, JCheckBox>();
1922        List<JPanel> compList = new ArrayList<JPanel>();
1923        
1924        Boolean choosersAll =
1925            (Boolean) getIdv().getPreference(PROP_CHOOSERS_ALL, Boolean.TRUE);
1926            
1927        final List<String[]> choosers = getChooserData();
1928        
1929        final JRadioButton useAllBtn = new JRadioButton("Use all data sources",
1930                                           choosersAll.booleanValue());
1931        final JRadioButton useTheseBtn =
1932            new JRadioButton("Use selected data sources:",
1933                             !choosersAll.booleanValue());
1934                             
1935        GuiUtils.buttonGroup(useAllBtn, useTheseBtn);
1936        
1937        final List<CheckboxCategoryPanel> chooserPanels = 
1938            new ArrayList<CheckboxCategoryPanel>();
1939            
1940        final Hashtable<String, CheckboxCategoryPanel> chooserMap = 
1941            new Hashtable<String, CheckboxCategoryPanel>();
1942            
1943        // create the checkbox + chooser name that'll show up in the preference
1944        // panel.
1945        for (String[] cs : choosers) {
1946            final String chooserCategory = getChooserCategory(cs[1]);
1947            String chooserShortName = getChooserShortName(cs[1]);
1948            
1949            CheckboxCategoryPanel chooserPanel =
1950                (CheckboxCategoryPanel) chooserMap.get(chooserCategory);
1951                
1952            if (chooserPanel == null) {
1953                chooserPanel = new CheckboxCategoryPanel(chooserCategory, false);
1954                chooserPanels.add(chooserPanel);
1955                chooserMap.put(chooserCategory, chooserPanel);
1956                compList.add(chooserPanel.getTopPanel());
1957                compList.add(chooserPanel);
1958            }
1959            
1960            JCheckBox cbx = new JCheckBox(chooserShortName, shouldShowChooser(cs[0], true));
1961            choosersData.put(cs[0], cbx);
1962            chooserPanel.addItem(cbx);
1963            chooserPanel.add(GuiUtils.inset(cbx, new Insets(0, 20, 0, 0)));
1964        }
1965        
1966        for (CheckboxCategoryPanel cbcp : chooserPanels) {
1967            cbcp.checkVisCbx();
1968        }
1969        
1970        // handle the user opting to enable all choosers.
1971        final JButton allOn = new JButton("All on");
1972        allOn.addActionListener(new ActionListener() {
1973            public void actionPerformed(ActionEvent ae) {
1974                for (CheckboxCategoryPanel cbcp : chooserPanels) {
1975                    cbcp.toggleAll(true);
1976                }
1977            }
1978        });
1979        
1980        // handle the user opting to disable all choosers.
1981        final JButton allOff = new JButton("All off");
1982        allOff.addActionListener(new ActionListener() {
1983            public void actionPerformed(ActionEvent ae) {
1984                for (CheckboxCategoryPanel cbcp : chooserPanels) {
1985                    cbcp.toggleAll(false);
1986                }
1987            }
1988        });
1989        
1990        final JPanel cbPanel = GuiUtils.top(GuiUtils.vbox(compList));
1991        
1992        JScrollPane cbScroller = new JScrollPane(cbPanel);
1993        cbScroller.getVerticalScrollBar().setUnitIncrement(10);
1994        cbScroller.setPreferredSize(new Dimension(300, 300));
1995        
1996        GuiUtils.enableTree(cbPanel, !useAllBtn.isSelected());
1997        GuiUtils.enableTree(allOn, !useAllBtn.isSelected());
1998        GuiUtils.enableTree(allOff, !useAllBtn.isSelected());
1999        
2000        JPanel widgetPanel =
2001            GuiUtils.topCenter(
2002                GuiUtils.hbox(useAllBtn, useTheseBtn),
2003                GuiUtils.leftCenter(
2004                    GuiUtils.inset(
2005                        GuiUtils.top(GuiUtils.vbox(allOn, allOff)),
2006                        4), cbScroller));
2007        JPanel choosersPanel =
2008            GuiUtils.topCenter(
2009                GuiUtils.inset(
2010                    new JLabel("Note: This will take effect the next run"),
2011                    4), widgetPanel);
2012        choosersPanel = GuiUtils.inset(GuiUtils.left(choosersPanel), 6);
2013        useAllBtn.addActionListener(new ActionListener() {
2014            public void actionPerformed(ActionEvent ae) {
2015                GuiUtils.enableTree(cbPanel, !useAllBtn.isSelected());
2016                GuiUtils.enableTree(allOn, !useAllBtn.isSelected());
2017                GuiUtils.enableTree(allOff, !useAllBtn.isSelected());
2018                
2019            }
2020        });
2021        useTheseBtn.addActionListener(new ActionListener() {
2022            public void actionPerformed(ActionEvent ae) {
2023                GuiUtils.enableTree(cbPanel, !useAllBtn.isSelected());
2024                GuiUtils.enableTree(allOn, !useAllBtn.isSelected());
2025                GuiUtils.enableTree(allOff, !useAllBtn.isSelected());
2026            }
2027        });
2028        
2029        PreferenceManager choosersManager = new PreferenceManager() {
2030            public void applyPreference(XmlObjectStore theStore, Object data) {
2031                
2032                Hashtable<String, Boolean> newToShow = new Hashtable<String, Boolean>();
2033                
2034                Hashtable table = (Hashtable)data;
2035                for (Enumeration keys = table.keys(); keys.hasMoreElements(); ) {
2036                    String chooserId = (String) keys.nextElement();
2037                    JCheckBox chooserCB = (JCheckBox) table.get(chooserId);
2038                    newToShow.put(chooserId, Boolean.valueOf(chooserCB.isSelected()));
2039                }
2040                
2041                choosersToShow = newToShow;
2042                theStore.put(PROP_CHOOSERS_ALL, Boolean.valueOf(useAllBtn.isSelected()));
2043                theStore.put(PROP_CHOOSERS, choosersToShow);
2044            }
2045        };
2046        this.add(Constants.PREF_LIST_DATA_CHOOSERS,
2047                 "What data sources should be shown in the user interface?",
2048                 choosersManager, choosersPanel, choosersData);
2049    }
2050    
2051    /**
2052     * <p>Return a list that contains a bunch of arrays of two strings.</p>
2053     * 
2054     * <p>The first item in one of the arrays is the chooser id, and the second
2055     * item is the "name" of the chooser. The name is formed by working through
2056     * choosers.xml and concatenating each panel's category and title.</p>
2057     * 
2058     * @return A list of chooser ids and names.
2059     */
2060    private final List<String[]> getChooserData() {
2061        List<String[]> choosers = new ArrayList<String[]>();
2062        String tempString;
2063        
2064        try {
2065            // get the root element so we can iterate through
2066            final String xml = 
2067                IOUtil.readContents(MCV_CHOOSERS, McIdasPreferenceManager.class);
2068                
2069            final Element root = XmlUtil.getRoot(xml);
2070            if (root == null) {
2071                return null;
2072            }
2073            // grab all the children, which should be panels.
2074            final NodeList nodeList = XmlUtil.getElements(root);
2075            for (int i = 0; i < nodeList.getLength(); i++) {
2076                
2077                final Element item = (Element)nodeList.item(i);
2078                
2079                if (item.getTagName().equals(XmlUi.TAG_PANEL) || item.getTagName().equals("chooser")) {
2080                    
2081                    // form the name of the chooser.
2082                    final String title = 
2083                        XmlUtil.getAttribute(item, XmlUi.ATTR_TITLE, "");
2084                    
2085                    final String cat = 
2086                        XmlUtil.getAttribute(item, XmlUi.ATTR_CATEGORY, "");
2087                        
2088                    if (cat.equals("")) {
2089                        tempString = title;
2090                    } else {
2091                        tempString = cat + ">" + title;
2092                    }
2093                    
2094                    final NodeList children = XmlUtil.getElements(item);
2095                    
2096                    if (item.getTagName().equals("chooser")) {
2097                        final String id = 
2098                            XmlUtil.getAttribute(item, XmlUi.ATTR_ID, "");
2099                        String[] tmp = {id, tempString};
2100                        choosers.add(tmp);
2101                    }
2102                    else {
2103                        for (int j = 0; j < children.getLength(); j++) {
2104                            final Element child = (Element)children.item(j);
2105
2106                            // form the id of the chooser and add it to the list.
2107                            if (child.getTagName().equals("chooser")) {
2108                                final String id = 
2109                                    XmlUtil.getAttribute(child, XmlUi.ATTR_ID, "");
2110                                String[] tmp = {id, tempString};
2111                                choosers.add(tmp);
2112                            }
2113                        }
2114                    }
2115                }
2116            }
2117        } catch (Exception e) {
2118            e.printStackTrace();
2119        }
2120        return choosers;
2121    }
2122    
2123    /**
2124     * Parse the full chooser name for a category.
2125     * 
2126     * @param chooserName Name of a chooser. Cannot be {@code null}.
2127     * 
2128     * @return {@literal "Category"} associated with {@code chooserName} or 
2129     * {@literal "Other"} if no category is available.
2130     */
2131    private String getChooserCategory(String chooserName) {
2132        String chooserCategory = "Other";
2133        int indexSep = chooserName.indexOf('>');
2134        if (indexSep >= 0) {
2135            chooserCategory = chooserName.substring(0, indexSep);
2136        }
2137        return chooserCategory;
2138    }
2139    
2140    /**
2141     * Parse the full chooser name for a short name.
2142     * 
2143     * @param chooserName Name of a chooser. Cannot be {@code null}.
2144     * 
2145     * @return The {@literal "short name"} of {@code chooserName}.
2146     */
2147    private String getChooserShortName(String chooserName) {
2148        String chooserShortName = chooserName;
2149        int indexSep = chooserName.indexOf('>');
2150        if (indexSep >= 0 && chooserName.length() > indexSep + 1) {
2151            chooserShortName = 
2152                chooserName.substring(indexSep + 1, chooserName.length());
2153        }
2154        return chooserShortName;
2155    }
2156    
2157    public class IconCellRenderer extends DefaultListCellRenderer {
2158        
2159        /**
2160         * Extends the default list cell renderer to use icons in addition to
2161         * the typical text.
2162         */
2163        public Component getListCellRendererComponent(JList list, Object value, 
2164                int index, boolean isSelected, boolean cellHasFocus) {
2165                
2166            super.getListCellRendererComponent(list, value, index, isSelected, 
2167                    cellHasFocus);
2168                    
2169            if (value instanceof JLabel) {
2170                setText(((JLabel)value).getText());
2171                setIcon(((JLabel)value).getIcon());
2172            }
2173            
2174            return this;
2175        }
2176        
2177        /** 
2178         * I wear some pretty fancy pants, so you'd better believe that I'm
2179         * going to enable fancy-pants text antialiasing.
2180         * 
2181         * @param g The graphics object that we'll use as a base.
2182         */
2183        protected void paintComponent(Graphics g) {
2184            Graphics2D g2d = (Graphics2D)g;
2185            g2d.setRenderingHints(getRenderingHints());
2186            super.paintComponent(g2d);
2187        }
2188    }
2189}
2190