001 /*
002 * $Id: StartupManager.java,v 1.65 2012/02/19 17:35:49 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2012
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 *
012 * All Rights Reserved
013 *
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.
016 *
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 *
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
025 * GNU Lesser Public License for more details.
026 *
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program. If not, see http://www.gnu.org/licenses.
029 */
030
031 package edu.wisc.ssec.mcidasv.startupmanager;
032
033 import java.awt.BorderLayout;
034 import java.awt.Component;
035 import java.awt.Container;
036 import java.awt.Dimension;
037 import java.awt.FlowLayout;
038 import java.awt.Graphics;
039 import java.awt.Graphics2D;
040 import java.awt.RenderingHints;
041 import java.awt.event.ActionEvent;
042 import java.awt.event.ActionListener;
043 import java.io.File;
044 import java.io.FileInputStream;
045 import java.io.FileOutputStream;
046 import java.io.IOException;
047 import java.io.InputStream;
048 import java.io.OutputStream;
049 import java.util.List;
050 import java.util.Properties;
051
052 import javax.swing.BorderFactory;
053 import javax.swing.DefaultListCellRenderer;
054 import javax.swing.DefaultListModel;
055 import javax.swing.ImageIcon;
056 import javax.swing.JButton;
057 import javax.swing.JCheckBox;
058 import javax.swing.JFrame;
059 import javax.swing.JLabel;
060 import javax.swing.JList;
061 import javax.swing.JPanel;
062 import javax.swing.JScrollPane;
063 import javax.swing.JSplitPane;
064 import javax.swing.JTree;
065 import javax.swing.ListModel;
066 import javax.swing.ListSelectionModel;
067 import javax.swing.SwingConstants;
068 import javax.swing.event.ListSelectionEvent;
069 import javax.swing.event.ListSelectionListener;
070 import javax.swing.tree.DefaultMutableTreeNode;
071 import javax.swing.tree.DefaultTreeCellRenderer;
072
073 import ucar.unidata.ui.Help;
074 import ucar.unidata.util.GuiUtils;
075 import ucar.unidata.util.LogUtil;
076 import ucar.unidata.util.StringUtil;
077 import edu.wisc.ssec.mcidasv.ArgumentManager;
078 import edu.wisc.ssec.mcidasv.Constants;
079 import edu.wisc.ssec.mcidasv.startupmanager.options.BooleanOption;
080 import edu.wisc.ssec.mcidasv.startupmanager.options.DirectoryOption;
081 import edu.wisc.ssec.mcidasv.startupmanager.options.MemoryOption;
082 import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster;
083 import edu.wisc.ssec.mcidasv.startupmanager.options.TextOption;
084 import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
085
086 // using an enum to enforce singleton-ness is a hack, but it's been pretty
087 // effective. OptionMaster is used in a similar way. The remaining enums are
088 // used in the more traditional fashion.
089 public enum StartupManager implements edu.wisc.ssec.mcidasv.Constants {
090 /** Lone instance of the startup manager. */
091 INSTANCE;
092
093 // TODO(jon): replace
094 public static final String[][] PREF_PANELS = {
095 { Constants.PREF_LIST_GENERAL, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/mcidasv-round32.png" },
096 { Constants.PREF_LIST_VIEW, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/tab-new32.png" },
097 { Constants.PREF_LIST_TOOLBAR, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/application-x-executable32.png" },
098 { Constants.PREF_LIST_DATA_CHOOSERS, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/preferences-desktop-remote-desktop32.png" },
099 { Constants.PREF_LIST_ADDE_SERVERS, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/applications-internet32.png" },
100 { Constants.PREF_LIST_AVAILABLE_DISPLAYS, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/video-display32.png" },
101 { Constants.PREF_LIST_NAV_CONTROLS, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/input-mouse32.png" },
102 { Constants.PREF_LIST_FORMATS_DATA,"/edu/wisc/ssec/mcidasv/resources/icons/prefs/preferences-desktop-theme32.png" },
103 { Constants.PREF_LIST_ADVANCED, "/edu/wisc/ssec/mcidasv/resources/icons/prefs/applications-internet32.png" },
104 };
105
106 // TODO(jon): replace
107 public static final Object[][] RENDER_HINTS = {
108 { RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON },
109 { RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY },
110 { RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON },
111 };
112
113 public enum Platform {
114 /** Instance of unix-specific platform information. */
115 UNIXLIKE("/", "runMcV.prefs", "\n"),
116
117 /** Instance of windows-specific platform information. */
118 WINDOWS("\\", "runMcV-Prefs.bat", "\r\n");
119
120 /** Path to the user's {@literal "userpath"} directory. */
121 private String userDirectory;
122
123 /** The path to the user's copy of the startup preferences. */
124 private String userPrefs;
125
126 /** Path to the preference file that ships with McIDAS-V. */
127 private final String defaultPrefs;
128
129 /** Holds the platform's representation of a new line. */
130 private final String newLine;
131
132 /** Directory delimiter for the current platform. */
133 private final String pathSeparator;
134
135 /** Total amount of memory avilable in megabytes */
136 private int availableMemory = 0;
137
138 /**
139 * Initializes the platform-specific paths to the different files
140 * required by the startup manager.
141 *
142 * @param pathSeparator Character that delimits directories. On Windows
143 * this will be {@literal \\}, while on Unix-style systems, it will be
144 * {@literal /}.
145 * @param defaultPrefs The path to the preferences file that ships with
146 * McIDAS-V.
147 * @param newLine Character(s!) that represent a new line for this
148 * platform.
149 *
150 * @throws NullPointerException if either {@code pathSeparator} or
151 * {@code defaultPrefs} are null.
152 *
153 * @throws IllegalArgumentException if either {@code pathSeparator} or
154 * {@code defaultPrefs} are an empty string.
155 */
156 Platform(final String pathSeparator, final String defaultPrefs,
157 final String newLine)
158 {
159 if (pathSeparator == null || defaultPrefs == null)
160 throw new NullPointerException("");
161 if (pathSeparator.length() == 0 || defaultPrefs.length() == 0)
162 throw new IllegalArgumentException("");
163
164 String osName = System.getProperty("os.name");
165 if (osName.startsWith("Mac OS X")) {
166 this.userDirectory = System.getProperty("user.home") + pathSeparator + "Documents" + pathSeparator + Constants.USER_DIRECTORY_NAME;
167 }
168 else {
169 this.userDirectory = System.getProperty("user.home") + pathSeparator + Constants.USER_DIRECTORY_NAME;
170 }
171 this.userPrefs = userDirectory + pathSeparator + defaultPrefs;
172 this.defaultPrefs = defaultPrefs;
173 this.newLine = newLine;
174 this.pathSeparator = pathSeparator;
175 }
176
177 /**
178 * Sets the path to the user's userpath directory explicitly.
179 *
180 * @param path New path.
181 */
182 public void setUserDirectory(final String path) {
183 userDirectory = path;
184 userPrefs = userDirectory + pathSeparator + defaultPrefs;
185 }
186
187 /**
188 * Sets the amount of available memory. {@code megabytes} must be
189 * greater than or equal to zero.
190 *
191 * @param megabytes Memory in megabytes
192 *
193 * @throws NullPointerException if {@code megabytes} is {@code null}.
194 * @throws IllegalArgumentException if {@code megabytes} is less than
195 * zero or does not represent an integer.
196 *
197 * @see StartupManager#getArgs(String[], Properties)
198 */
199 public void setAvailableMemory(String megabytes) {
200 if (megabytes == null)
201 throw new NullPointerException("Available memory cannot be null");
202 if (megabytes.equals("")) megabytes="0";
203
204 try {
205 int test = Integer.parseInt(megabytes);
206 if (test < 0)
207 throw new IllegalArgumentException("Available memory must be a non-negative integer, not \""+megabytes+"\"");
208
209 availableMemory = test;
210 } catch (NumberFormatException e) {
211 throw new IllegalArgumentException("Could not convert \""+megabytes+"\" to a non-negative integer");
212 }
213 }
214
215 /**
216 * Returns the path to the user's {@literal "userpath"} directory.
217 *
218 * @return Path to the user's directory.
219 */
220 public String getUserDirectory() {
221 return userDirectory;
222 }
223
224 /**
225 * Returns the path to a file in the user's {@literal "userpath"} directory
226 *
227 * @param filename
228 * @return Path to a file in the user's directory.
229 */
230 public String getUserFile(String filename) {
231 return getUserDirectory()+pathSeparator+filename;
232 }
233
234 public String getUserBundles() {
235 return getUserDirectory()+pathSeparator+"bundles";
236 }
237
238 /**
239 * Returns the amount of available memory in megabytes
240 *
241 * @return Available memory in megabytes
242 */
243 public int getAvailableMemory() {
244 return availableMemory;
245 }
246
247 /**
248 * Returns the path of user's copy of the startup preferences.
249 *
250 * @return Path to the user's startup preferences file.
251 */
252 public String getUserPrefs() {
253 return userPrefs;
254 }
255
256 /**
257 * Returns the path of the startup preferences included in the McIDAS-V
258 * distribution. Mostly useful for normalizing the user
259 * directory.
260 *
261 * @return Path to the default startup preferences.
262 *
263 * @see OptionMaster#normalizeUserDirectory()
264 */
265 public String getDefaultPrefs() {
266 return defaultPrefs;
267 }
268
269 /**
270 * Returns the platform's notion of a new line.
271 *
272 * @return Unix-like: {@literal \n}; Windows: {@literal \r\n}.
273 */
274 public String getNewLine() {
275 return newLine;
276 }
277
278 /**
279 * Returns a brief summary of the platform specific file locations.
280 * Please note that the format and contents are subject to change.
281 *
282 * @return String that looks like
283 * {@code [Platform@HASHCODE: defaultPrefs=..., userDirectory=...,
284 * userPrefs=...]}
285 */
286 @Override public String toString() {
287 return String.format(
288 "[Platform@%x: defaultPrefs=%s, userDirectory=%s, userPrefs=%s]",
289 hashCode(), defaultPrefs, userDirectory, userPrefs);
290 }
291 }
292
293 /** usage message */
294 public static final String USAGE_MESSAGE =
295 "Usage: runMcV-Prefs <args>";
296
297 /** Path to the McIDAS-V help set within {@literal mcv_userguide.jar}. */
298 private static final String HELP_PATH = "/docs/userguide";
299
300 /** ID of the startup prefs help page. */
301 private static final String HELP_TARGET = "idv.tools.preferences.advancedpreferences";
302
303 /** The type of platform as reported by {@link #determinePlatform()}. */
304 private final Platform platform = determinePlatform();
305
306 /** Cached copy of the application rendering hints. */
307 public static final RenderingHints HINTS = getRenderingHints();
308
309 /** Contains the list of the different preference panels. */
310 private final JList panelList = new JList(new DefaultListModel());
311
312 /** Panel containing the startup options. */
313 private JPanel ADVANCED_PANEL;
314
315 /**
316 * Panel to use for all other preference panels while running startup
317 * manager.
318 */
319 private JPanel BAD_CHOICE_PANEL;
320
321 /** Contains the various buttons (Apply, Ok, Help, Cancel). */
322 private JPanel COMMAND_ROW_PANEL;
323
324 /**
325 * Creates and returns the rendering hints for the GUI.
326 * Built from {@link #RENDER_HINTS}
327 *
328 * @return Hints to use when displaying the GUI.
329 */
330 public static RenderingHints getRenderingHints() {
331 RenderingHints hints = new RenderingHints(null);
332 for (int i = 0; i < RENDER_HINTS.length; i++)
333 hints.put(RENDER_HINTS[i][0], RENDER_HINTS[i][1]);
334 return hints;
335 }
336
337 /**
338 * Figures out the type of platform. Queries the "os.name"
339 * system property to determine the platform type.
340 *
341 * @return {@link Platform#UNIXLIKE} or {@link Platform#WINDOWS}.
342 */
343 private Platform determinePlatform() {
344 String os = System.getProperty("os.name");
345 if (os == null)
346 throw new RuntimeException();
347
348 if (os.startsWith("Windows"))
349 return Platform.WINDOWS;
350 else
351 return Platform.UNIXLIKE;
352 }
353
354 /**
355 * Returns either {@link Platform#UNIXLIKE} or
356 * {@link Platform#WINDOWS}.
357 *
358 * @return The platform as determined by {@link #determinePlatform()}.
359 */
360 public Platform getPlatform() {
361 return platform;
362 }
363
364 /**
365 * Saves the changes to the preferences and quits. Unlike the other button
366 * handling methods, this one is public. This was done so that the advanced
367 * preferences (within McIDAS-V) can force an update to the startup prefs.
368 */
369 public void handleApply() {
370 OptionMaster.INSTANCE.writeStartup();
371 }
372
373 /**
374 * Saves the preference changes.
375 */
376 protected void handleOk() {
377 OptionMaster.INSTANCE.writeStartup();
378 System.exit(0);
379 }
380
381 /**
382 * Shows the startup preferences help page.
383 */
384 protected void handleHelp() {
385 Help.setTopDir(HELP_PATH);
386 Help.getDefaultHelp().gotoTarget(HELP_TARGET);
387 }
388
389 /**
390 * Simply quits the program.
391 */
392 protected void handleCancel() {
393 System.exit(0);
394 }
395
396 /**
397 *
398 * @return
399 */
400 private Container getSelectedPanel() {
401 ListModel listModel = panelList.getModel();
402 int index = panelList.getSelectedIndex();
403 if (index == -1)
404 return getAdvancedPanel(true);
405
406 String key = ((JLabel)listModel.getElementAt(index)).getText();
407 if (!key.equals(Constants.PREF_LIST_ADVANCED))
408 return getUnavailablePanel();
409
410 return getAdvancedPanel(true);
411 }
412
413 /**
414 * Creates and returns a dummy panel.
415 *
416 * @return Panel containing only a note about
417 * "options unavailable."
418 */
419 private JPanel buildUnavailablePanel() {
420 JPanel panel = new JPanel();
421 panel.add(new JLabel("These options are unavailable in this context"));
422 return panel;
423 }
424
425 /**
426 * Creates and returns the advanced preferences panel.
427 *
428 * @return Panel with all of the various startup options.
429 */
430 private JPanel buildAdvancedPanel() {
431 OptionMaster optMaster = OptionMaster.INSTANCE;
432 MemoryOption heapSize = (MemoryOption)optMaster.getOption("HEAP_SIZE");
433 BooleanOption jogl = (BooleanOption)optMaster.getOption("JOGL_TOGL");
434 BooleanOption use3d = (BooleanOption)optMaster.getOption("USE_3DSTUFF");
435 BooleanOption defaultBundle = (BooleanOption)optMaster.getOption("DEFAULT_LAYOUT");
436 BooleanOption useDirect3d = (BooleanOption)optMaster.getOption("D3DREND");
437 BooleanOption useCmsCollector = (BooleanOption)optMaster.getOption("USE_CMSGC");
438 BooleanOption useNpot = (BooleanOption)optMaster.getOption("USE_NPOT");
439 BooleanOption useGeometryByRef = (BooleanOption)optMaster.getOption("USE_GEOBYREF");
440 BooleanOption useImageByRef = (BooleanOption)optMaster.getOption("USE_IMAGEBYREF");
441 DirectoryOption startupBundle = (DirectoryOption)optMaster.getOption("STARTUP_BUNDLE");
442 TextOption jvmArgs = (TextOption)optMaster.getOption("JVM_ARGS");
443
444 JPanel startupPanel = new JPanel();
445 startupPanel.setBorder(BorderFactory.createTitledBorder("Startup Options"));
446
447 // Build the memory panel
448 JPanel heapPanel = McVGuiUtils.makeLabeledComponent(heapSize.getLabel()+":", heapSize.getComponent());
449
450 // Build the 3D panel
451 JCheckBox use3dCheckBox = (JCheckBox)use3d.getComponent();
452 use3dCheckBox.setText(use3d.getLabel());
453 final JCheckBox joglCheckBox = (JCheckBox)jogl.getComponent();
454 joglCheckBox.setText(jogl.getLabel());
455 final JCheckBox direct3dBox = (JCheckBox)useDirect3d.getComponent();
456 direct3dBox.setText(useDirect3d.getLabel());
457
458 JPanel internalPanel = McVGuiUtils.topCenterBottom(use3dCheckBox, joglCheckBox, direct3dBox);
459 JPanel j3dPanel = McVGuiUtils.makeLabeledComponent("3D:", internalPanel);
460
461 // Build the bundle panel
462 JPanel startupBundlePanel = (JPanel)startupBundle.getComponent();
463 JCheckBox defaultBundleCheckBox = (JCheckBox)defaultBundle.getComponent();
464 defaultBundleCheckBox.setText(defaultBundle.getLabel());
465 JPanel bundlePanel = McVGuiUtils.makeLabeledComponent(startupBundle.getLabel()+":",
466 McVGuiUtils.topBottom(startupBundlePanel, defaultBundleCheckBox, McVGuiUtils.Prefer.TOP));
467
468 JCheckBox useCmsCollectorCheckBox = (JCheckBox)useCmsCollector.getComponent();
469 useCmsCollectorCheckBox.setText(useCmsCollector.getLabel());
470
471 JCheckBox useGeometryByRefCheckBox = (JCheckBox)useGeometryByRef.getComponent();
472 useGeometryByRefCheckBox.setText(useGeometryByRef.getLabel());
473
474 JCheckBox useImageByRefCheckBox = (JCheckBox)useImageByRef.getComponent();
475 useImageByRefCheckBox.setText(useImageByRef.getLabel());
476
477 JCheckBox useNpotCheckBox = (JCheckBox)useNpot.getComponent();
478 useNpotCheckBox.setText(useNpot.getLabel());
479
480 JPanel miscPanel = McVGuiUtils.makeLabeledComponent("Misc:", useCmsCollectorCheckBox);
481
482 Component[] visadComponents = new Component[] {
483 useGeometryByRefCheckBox,
484 useImageByRefCheckBox,
485 useNpotCheckBox
486 };
487
488 JPanel visadPanel = McVGuiUtils.makeLabeledComponent("VisAD:", McVGuiUtils.vertical(visadComponents));
489
490 javax.swing.GroupLayout panelLayout = new javax.swing.GroupLayout(startupPanel);
491 startupPanel.setLayout(panelLayout);
492 panelLayout.setHorizontalGroup(
493 panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
494 .addComponent(heapPanel)
495 .addComponent(j3dPanel)
496 .addComponent(bundlePanel)
497 .addComponent(miscPanel)
498 .addComponent(visadPanel)
499 );
500 panelLayout.setVerticalGroup(
501 panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
502 .addGroup(panelLayout.createSequentialGroup()
503 .addComponent(heapPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
504 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
505 .addComponent(bundlePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
506 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
507 .addComponent(j3dPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
508 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
509 .addComponent(visadPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
510 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
511 .addComponent(miscPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
512 )
513 );
514
515 return startupPanel;
516 }
517
518 /**
519 * Builds and returns a {@link JPanel} containing the various buttons that
520 * control the startup manager. These buttons offer identical
521 * functionality to those built by the IDV's preference manager code.
522 *
523 * @return A {@code JPanel} containing the following types of buttons:
524 * {@link ApplyButton}, {@link OkButton}, {@link HelpButton},
525 * and {@link CancelButton}.
526 *
527 * @see GuiUtils#makeApplyOkHelpCancelButtons(ActionListener)
528 */
529 private JPanel buildCommandRow() {
530 JPanel panel = new JPanel(new FlowLayout());
531 // Apply doesn't really mean anything in standalone mode...
532 // panel.add(new ApplyButton());
533 panel.add(new OkButton());
534 panel.add(new HelpButton());
535 panel.add(new CancelButton());
536 panel = McVGuiUtils.makePrettyButtons(panel);
537 return panel;
538 }
539
540 /**
541 * Returns the advanced preferences panel. Differs from the
542 * {@link #buildAdvancedPanel()} in that a panel isn't created, unless
543 * {@code forceBuild} is {@code true}.
544 *
545 * @param forceBuild Always rebuilds the advanced panel if {@code true}.
546 *
547 * @return Panel containing the startup options.
548 */
549 public JPanel getAdvancedPanel(final boolean forceBuild) {
550 if (forceBuild || ADVANCED_PANEL == null) {
551 OptionMaster.INSTANCE.readStartup();
552 ADVANCED_PANEL = buildAdvancedPanel();
553 }
554 return ADVANCED_PANEL;
555 }
556
557 public JPanel getUnavailablePanel() {
558 if (BAD_CHOICE_PANEL == null)
559 BAD_CHOICE_PANEL = buildUnavailablePanel();
560 return BAD_CHOICE_PANEL;
561 }
562
563 /**
564 * Returns a panel containing the Apply/Ok/Help/Cancel buttons.
565 *
566 * @return Panel containing the the command row.
567 */
568 public JPanel getCommandRow() {
569 if (COMMAND_ROW_PANEL == null)
570 COMMAND_ROW_PANEL = buildCommandRow();
571 return COMMAND_ROW_PANEL;
572 }
573
574 /**
575 * Build and display the startup manager window.
576 */
577 protected void createDisplay() {
578 DefaultListModel listModel = (DefaultListModel)panelList.getModel();
579
580 for (int i = 0; i < PREF_PANELS.length; i++) {
581 ImageIcon icon = new ImageIcon(getClass().getResource(PREF_PANELS[i][1]));
582 JLabel label = new JLabel(PREF_PANELS[i][0], icon, SwingConstants.LEADING);
583 listModel.addElement(label);
584 }
585
586 JScrollPane scroller = new JScrollPane(panelList);
587 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
588 splitPane.setResizeWeight(0.0);
589 splitPane.setLeftComponent(scroller);
590 scroller.setMinimumSize(new Dimension(166, 319));
591
592 panelList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
593 panelList.setSelectedIndex(PREF_PANELS.length - 1);
594 panelList.setVisibleRowCount(PREF_PANELS.length);
595 panelList.setCellRenderer(new IconCellRenderer());
596
597 panelList.addListSelectionListener(new ListSelectionListener() {
598 public void valueChanged(final ListSelectionEvent e) {
599 if (!e.getValueIsAdjusting())
600 splitPane.setRightComponent(getSelectedPanel());
601 }
602 });
603
604 splitPane.setRightComponent(getSelectedPanel());
605
606 JFrame frame = new JFrame("User Preferences");
607 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
608 frame.getContentPane().add(splitPane);
609 frame.getContentPane().add(getCommandRow(), BorderLayout.PAGE_END);
610
611 frame.pack();
612 frame.setVisible(true);
613 }
614
615 /**
616 * Copies a file.
617 *
618 * @param src The file to copy.
619 * @param dst The path to the copy of {@code src}.
620 *
621 * @throws IOException If there was a problem while attempting to copy.
622 */
623 public void copy(final File src, final File dst) throws IOException {
624 InputStream in = new FileInputStream(src);
625 OutputStream out = new FileOutputStream(dst);
626
627 byte[] buf = new byte[1024];
628 int length;
629
630 while ((length = in.read(buf)) > 0)
631 out.write(buf, 0, length);
632
633 in.close();
634 out.close();
635 }
636
637 public static class TreeCellRenderer extends DefaultTreeCellRenderer {
638 @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
639 super.getTreeCellRendererComponent(tree, value, sel, expanded,
640 leaf, row, hasFocus);
641
642 DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
643
644 File f = (File)node.getUserObject();
645 String path = f.getPath();
646
647 if (f.isDirectory())
648 setToolTipText("Bundle Directory: " + path);
649 else if (ArgumentManager.isZippedBundle(path))
650 setToolTipText("Zipped Bundle: " + path);
651 else if (ArgumentManager.isXmlBundle(path))
652 setToolTipText("XML Bundle: " + path);
653 else
654 setToolTipText("Unknown file type: " + path);
655
656 setText(f.getName().replace(f.getParent(), ""));
657 return this;
658 }
659 }
660
661 public static class IconCellRenderer extends DefaultListCellRenderer {
662 @Override public Component getListCellRendererComponent(JList list,
663 Object value, int index, boolean isSelected, boolean cellHasFocus)
664 {
665 super.getListCellRendererComponent(list, value, index, isSelected,
666 cellHasFocus);
667
668 if (value instanceof JLabel) {
669 setText(((JLabel)value).getText());
670 setIcon(((JLabel)value).getIcon());
671 }
672
673 return this;
674 }
675
676 @Override protected void paintComponent(Graphics g) {
677 Graphics2D g2d = (Graphics2D)g;
678 g2d.setRenderingHints(StartupManager.HINTS);
679 super.paintComponent(g2d);
680 }
681 }
682
683 private static abstract class CommandButton extends JButton
684 implements ActionListener
685 {
686 public CommandButton(final String label) {
687 super(label);
688 McVGuiUtils.setComponentWidth(this);
689 addActionListener(this);
690 }
691
692 @Override public void paintComponent(Graphics g) {
693 Graphics2D g2d = (Graphics2D)g;
694 g2d.setRenderingHints(StartupManager.HINTS);
695 super.paintComponent(g2d);
696 }
697
698 abstract public void actionPerformed(final ActionEvent e);
699 }
700
701 private static class ApplyButton extends CommandButton {
702 public ApplyButton() {
703 super("Apply");
704 }
705 public void actionPerformed(final ActionEvent e) {
706 StartupManager.INSTANCE.handleApply();
707 }
708 }
709
710 private static class OkButton extends CommandButton {
711 public OkButton() {
712 super("OK");
713 }
714 public void actionPerformed(final ActionEvent e) {
715 StartupManager.INSTANCE.handleOk();
716 }
717 }
718
719 private static class HelpButton extends CommandButton {
720 public HelpButton() {
721 super("Help");
722 }
723 public void actionPerformed(final ActionEvent e) {
724 StartupManager.INSTANCE.handleHelp();
725 }
726 }
727
728 private static class CancelButton extends CommandButton {
729 public CancelButton() {
730 super("Cancel");
731 }
732 public void actionPerformed(final ActionEvent e) {
733 StartupManager.INSTANCE.handleCancel();
734 }
735 }
736
737 public static Properties getDefaultProperties() {
738 Properties props = new Properties();
739 String osName = System.getProperty("os.name");
740 if (osName.startsWith("Mac OS X")) {
741 props.setProperty("userpath", String.format("%s%s%s%s%s", System.getProperty("user.home"), File.separator, "Documents", File.separator, Constants.USER_DIRECTORY_NAME));
742 }
743 else {
744 props.setProperty("userpath", String.format("%s%s%s", System.getProperty("user.home"), File.separator, Constants.USER_DIRECTORY_NAME));
745 }
746 props.setProperty(Constants.PROP_SYSMEM, "0");
747 return props;
748 }
749
750 public static Properties getArgs(final boolean ignoreUnknown,
751 final boolean fromStartupManager, final String[] args,
752 final Properties defaults)
753 {
754 Properties props = new Properties(defaults);
755 for (int i = 0; i < args.length; i++) {
756
757 // handle property definitions
758 if (args[i].startsWith("-D")) {
759 List<String> l = StringUtil.split(args[i].substring(2), "=");
760 if (l.size() == 2)
761 props.setProperty(l.get(0), l.get(1));
762 else
763 usage("Invalid property:" + args[i]);
764 }
765
766 // handle userpath changes
767 else if (ARG_USERPATH.equals(args[i]) && (i+1) < args.length) {
768 props.setProperty("userpath", args[++i]);
769 }
770
771 // handle help requests
772 else if (args[i].equals(ARG_HELP) && (fromStartupManager)) {
773 System.err.println(USAGE_MESSAGE);
774 System.err.println(getUsageMessage());
775 System.exit(1);
776 }
777
778 // bail out for unknown args, unless we don't care!
779 else if (!ignoreUnknown){
780 usage("Unknown argument: " + args[i]);
781 }
782 }
783 return props;
784 }
785
786 public static int getMaximumHeapSize() {
787 int sysmem = StartupManager.INSTANCE.getPlatform().getAvailableMemory();
788 if (sysmem > Constants.MAX_MEMORY_32BIT &&
789 System.getProperty("os.arch").indexOf("64") < 0)
790 return Constants.MAX_MEMORY_32BIT;
791 return sysmem;
792 }
793
794 /**
795 * Print out the command line usage message and exit. Taken entirely from
796 * {@link ucar.unidata.idv.ArgsManager}.
797 *
798 * @param err The usage message
799 */
800 private static void usage(final String err) {
801 String msg = USAGE_MESSAGE;
802 msg = msg + "\n" + getUsageMessage();
803 LogUtil.userErrorMessage(err + "\n" + msg);
804 System.exit(1);
805 }
806
807 /**
808 * Return the command line usage message.
809 *
810 * @return The usage message
811 */
812 protected static String getUsageMessage() {
813 return "\t"+ARG_HELP+" (this message)\n"+
814 "\t"+ARG_USERPATH+" <user directory to use>\n"+
815 "\t-Dpropertyname=value (Define the property value)\n";
816 }
817
818 /**
819 * Applies the command line arguments to the startup preferences. This function
820 * is mostly useful because it allows us to supply an arbitrary {@code args} array,
821 * link in {@link edu.wisc.ssec.mcidasv.McIDASV#main(String[])}.
822 *
823 * @param ignoreUnknown If {@code true} ignore any parameters that do not
824 * apply to the startup manager. If {@code false} the non-applicable
825 * parameters should signify an error.
826 * @param fromStartupManager Whether or not this call originated from the
827 * startup manager (rather than preferences).
828 * @param args Incoming command line arguments. Cannot be {@code null}.
829 *
830 * @throws NullPointerException if {@code args} is null.
831 *
832 * @see #getArgs(boolean, String[], Properties)
833 */
834 public static void applyArgs(final boolean ignoreUnknown, final boolean fromStartupManager, final String[] args) {
835 if (args == null)
836 throw new NullPointerException("Arguments list cannot be null");
837 StartupManager sm = StartupManager.INSTANCE;
838 Platform platform = sm.getPlatform();
839
840 Properties props = getArgs(ignoreUnknown, fromStartupManager, args, getDefaultProperties());
841 platform.setUserDirectory(props.getProperty("userpath"));
842 platform.setAvailableMemory(props.getProperty(Constants.PROP_SYSMEM));
843 }
844
845 public static void main(String[] args) {
846 applyArgs(false, true, args);
847 StartupManager.INSTANCE.createDisplay();
848 }
849 }