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