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 }