001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2024 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas/ 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see https://www.gnu.org/licenses/. 027 */ 028 029package edu.wisc.ssec.mcidasv.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 useCmsCollector = optMaster.getBooleanOption("USE_CMSGC"); 302 BooleanOption useNpot = optMaster.getBooleanOption("USE_NPOT"); 303 BooleanOption useGeometryByRef = optMaster.getBooleanOption("USE_GEOBYREF"); 304 BooleanOption useImageByRef = optMaster.getBooleanOption("USE_IMAGEBYREF"); 305 FileOption startupBundle = optMaster.getFileOption("STARTUP_BUNDLE"); 306 TextOption jvmArgs = optMaster.getTextOption("JVM_OPTIONS"); 307 LoggerLevelOption logLevel = optMaster.getLoggerLevelOption("LOG_LEVEL"); 308 TextOption textureWidth = optMaster.getTextOption("TEXTURE_WIDTH"); 309 TextOption scaling = optMaster.getTextOption("MCV_SCALING"); 310 BooleanOption darkMode = optMaster.getBooleanOption("USE_DARK_MODE"); 311 312 JPanel startupPanel = new JPanel(); 313 startupPanel.setBorder(BorderFactory.createTitledBorder("Startup Options")); 314 315 // Build the memory panel 316 JPanel heapPanel = McVGuiUtils.makeLabeledComponent(heapSize.getLabel()+':', heapSize.getComponent()); 317 318 // Build the 3D panel 319 JCheckBox use3dCheckBox = use3d.getComponent(); 320 use3dCheckBox.setText(use3d.getLabel()); 321 final JCheckBox joglCheckBox = jogl.getComponent(); 322 joglCheckBox.setText(jogl.getLabel()); 323 JPanel texturePanel = McVGuiUtils.makeLabeledComponent(textureWidth.getLabel()+':', textureWidth.getComponent()); 324// JTextField textureField = textureWidth.getComponent(); 325 326 JPanel internalPanel = McVGuiUtils.topBottom(use3dCheckBox, joglCheckBox, McVGuiUtils.Prefer.TOP); 327 JPanel j3dPanel = McVGuiUtils.makeLabeledComponent("3D:", internalPanel); 328 329 // Build the bundle panel 330 JComponent startupBundlePanel = startupBundle.getComponent(); 331 JCheckBox defaultBundleCheckBox = defaultBundle.getComponent(); 332 defaultBundleCheckBox.setText(defaultBundle.getLabel()); 333 JPanel bundlePanel = McVGuiUtils.makeLabeledComponent(startupBundle.getLabel()+ ':', 334 McVGuiUtils.topBottom(startupBundlePanel, defaultBundleCheckBox, McVGuiUtils.Prefer.TOP)); 335 336 JCheckBox useCmsCollectorCheckBox = useCmsCollector.getComponent(); 337 useCmsCollectorCheckBox.setText(useCmsCollector.getLabel()); 338 339 JCheckBox useGeometryByRefCheckBox = useGeometryByRef.getComponent(); 340 useGeometryByRefCheckBox.setText(useGeometryByRef.getLabel()); 341 342 JCheckBox useImageByRefCheckBox = useImageByRef.getComponent(); 343 useImageByRefCheckBox.setText(useImageByRef.getLabel()); 344 345 JCheckBox useNpotCheckBox = useNpot.getComponent(); 346 useNpotCheckBox.setText(useNpot.getLabel()); 347 348 JCheckBox useDarkModeCheckBox = darkMode.getComponent(); 349 useDarkModeCheckBox.setText(darkMode.getLabel()); 350 if (platform != Platform.MAC) { 351 ToolTipManager.sharedInstance().setInitialDelay(0); 352 useDarkModeCheckBox.setToolTipText("Dark Mode not yet supported on this operating system"); 353 } 354 355 // this is a JComboBox<String>; kinda struggling to represent this 356 // in java's type system. 357 JComboBox logLevelComboBox = logLevel.getComponent(); 358 359 JPanel logLevelPanel = McVGuiUtils.makeLabeledComponent(logLevel.getLabel()+':', logLevelComboBox); 360 361 JPanel scalingPanel = McVGuiUtils.makeLabeledComponent(scaling.getLabel()+':', scaling.getComponent()); 362 363 JPanel miscPanel = McVGuiUtils.makeLabeledComponent("Misc:", McVGuiUtils.vertical(useCmsCollectorCheckBox, useDarkModeCheckBox, scalingPanel)); 364 365 JTextField jvmArgsField = jvmArgs.getComponent(); 366 367 JButton warningBtn = McVGuiUtils.makeImageButton(Constants.ICON_EXCLAMATION_SMALL, "Warning"); 368 warningBtn.addActionListener(e -> { 369 JOptionPane.showMessageDialog(null, "This option refers to official Java VM options. " + 370 "Modifications with invalid arguments could break your install.", "Warning", JOptionPane.OK_OPTION); 371 }); 372 373 JPanel jvmPanel = new JPanel(new BorderLayout()); 374 jvmPanel.add(McVGuiUtils.makeLabeledComponent("Java Flags:", jvmArgsField), BorderLayout.CENTER); 375 jvmPanel.add(warningBtn, BorderLayout.EAST); 376 377 // TJJ Nov 2018 378 // Add note at top of Startup Options alerting user a restart may be needed 379 JLabel restartLabel = new JLabel("Note: Most startup options require a McIDAS-V restart to take effect"); 380 restartLabel.setForeground(Color.red); 381 restartLabel.setBorder(new EmptyBorder(6, 6, 6, 6)); 382 383 Component[] visadComponents = { 384 useGeometryByRefCheckBox, 385 useImageByRefCheckBox, 386 useNpotCheckBox, 387 texturePanel, 388 }; 389 390 JPanel visadPanel = McVGuiUtils.makeLabeledComponent("VisAD:", McVGuiUtils.vertical(visadComponents)); 391 392 GroupLayout panelLayout = new GroupLayout(startupPanel); 393 startupPanel.setLayout(panelLayout); 394 panelLayout.setHorizontalGroup( 395 panelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) 396 .addComponent(restartLabel) 397 .addComponent(heapPanel) 398 .addComponent(j3dPanel) 399 .addComponent(bundlePanel) 400 .addComponent(visadPanel) 401 .addComponent(logLevelPanel) 402 .addComponent(miscPanel) 403 .addComponent(jvmPanel) 404 ); 405 panelLayout.setVerticalGroup( 406 panelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) 407 .addGroup(panelLayout.createSequentialGroup() 408 .addComponent(restartLabel) 409 .addComponent(heapPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 410 .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) 411 .addComponent(bundlePanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 412 .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) 413 .addComponent(j3dPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 414 .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) 415 .addComponent(visadPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 416 .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) 417 .addComponent(logLevelPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 418 .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) 419 .addComponent(miscPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 420 .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) 421 .addComponent(jvmPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 422 ) 423 ); 424 return startupPanel; 425 } 426 427 /** 428 * Builds and returns a {@link JPanel} containing the various buttons that 429 * control the startup manager. These buttons offer identical 430 * functionality to those built by the IDV's preference manager code. 431 * 432 * @return A {@code JPanel} containing the following types of buttons: 433 * {@link ApplyButton}, {@link OkButton}, {@link HelpButton}, 434 * and {@link CancelButton}. 435 * 436 * @see GuiUtils#makeApplyOkHelpCancelButtons(ActionListener) 437 */ 438 private JPanel buildCommandRow() { 439 JPanel panel = new JPanel(new FlowLayout()); 440 // Apply doesn't really mean anything in standalone mode... 441// panel.add(new ApplyButton()); 442 OkButton okBtn = new OkButton(); 443 okBtn.setToolTipText("Accept changed preferences and close this window"); 444 HelpButton hlpBtn = new HelpButton(); 445 hlpBtn.setToolTipText("Open the User Guide Page for User Preferences"); 446 CancelButton cnclBtn = new CancelButton(); 447 cnclBtn.setToolTipText("Exit User Preferences"); 448 panel.add(okBtn); 449 panel.add(hlpBtn); 450 panel.add(cnclBtn); 451 panel = McVGuiUtils.makePrettyButtons(panel); 452 return panel; 453 } 454 455 /** 456 * Returns the advanced preferences panel. Differs from the 457 * {@link #buildAdvancedPanel()} in that a panel isn't created, unless 458 * {@code forceBuild} is {@code true}. 459 * 460 * @param forceBuild Always rebuilds the advanced panel if {@code true}. 461 * 462 * @return Panel containing the startup options. 463 */ 464 public JPanel getAdvancedPanel(final boolean forceBuild) { 465 if (forceBuild || (ADVANCED_PANEL == null)) { 466 OptionMaster.getInstance().readStartup(); 467 ADVANCED_PANEL = buildAdvancedPanel(); 468 } 469 return ADVANCED_PANEL; 470 } 471 472 public JPanel getUnavailablePanel() { 473 if (BAD_CHOICE_PANEL == null) { 474 BAD_CHOICE_PANEL = buildUnavailablePanel(); 475 } 476 return BAD_CHOICE_PANEL; 477 } 478 479 /** 480 * Returns a panel containing the Apply/Ok/Help/Cancel buttons. 481 * 482 * @return Panel containing the command row. 483 */ 484 public JPanel getCommandRow() { 485 if (COMMAND_ROW_PANEL == null) { 486 COMMAND_ROW_PANEL = buildCommandRow(); 487 } 488 return COMMAND_ROW_PANEL; 489 } 490 491 /** 492 * Build and display the startup manager window. 493 */ 494 protected void createDisplay() { 495 DefaultListModel listModel = (DefaultListModel)panelList.getModel(); 496 497 for (String[] PREF_PANEL : PREF_PANELS) { 498 ImageIcon icon = new ImageIcon(getClass().getResource(PREF_PANEL[1])); 499 JLabel label = new JLabel(PREF_PANEL[0], icon, SwingConstants.LEADING); 500 listModel.addElement(label); 501 } 502 503 JScrollPane scroller = new JScrollPane(panelList); 504 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); 505 splitPane.setResizeWeight(0.0); 506 splitPane.setLeftComponent(scroller); 507 scroller.setMinimumSize(new Dimension(166, 319)); 508 509 panelList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 510 panelList.setSelectedIndex(PREF_PANELS.length - 1); 511 panelList.setVisibleRowCount(PREF_PANELS.length); 512 panelList.setCellRenderer(new IconCellRenderer()); 513 514 panelList.addListSelectionListener(e -> { 515 if (!e.getValueIsAdjusting()) { 516 splitPane.setRightComponent(getSelectedPanel()); 517 } 518 }); 519 520 splitPane.setRightComponent(getSelectedPanel()); 521 522 JFrame frame = new JFrame("User Preferences"); 523 frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 524 frame.getContentPane().add(splitPane); 525 frame.getContentPane().add(getCommandRow(), BorderLayout.PAGE_END); 526 527 frame.pack(); 528 frame.setVisible(true); 529 } 530 531 /** 532 * Copies a file. 533 * 534 * @param src The file to copy. 535 * @param dst The path to the copy of {@code src}. 536 * 537 * @throws IOException If there was a problem while attempting to copy. 538 */ 539 public void copy(final File src, final File dst) throws IOException { 540 try ( 541 InputStream in = new FileInputStream(src); 542 OutputStream out = new FileOutputStream(dst) 543 ) { 544 byte[] buf = new byte[1024]; 545 int length; 546 while ((length = in.read(buf)) > 0) { 547 out.write(buf, 0, length); 548 } 549 } 550 } 551 552 public static class TreeCellRenderer extends DefaultTreeCellRenderer { 553 @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { 554 super.getTreeCellRendererComponent(tree, value, sel, expanded, 555 leaf, row, hasFocus); 556 557 DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; 558 559 File f = (File)node.getUserObject(); 560 String path = f.getPath(); 561 562 if (f.isDirectory()) { 563 setToolTipText("Bundle Directory: " + path); 564 } else if (ArgumentManager.isZippedBundle(path)) { 565 setToolTipText("Zipped Bundle: " + path); 566 } else if (ArgumentManager.isXmlBundle(path)) { 567 setToolTipText("XML Bundle: " + path); 568 } else { 569 setToolTipText("Unknown file type: " + path); 570 } 571 setText(f.getName().replace(f.getParent(), "")); 572 return this; 573 } 574 } 575 576 public static class IconCellRenderer extends DefaultListCellRenderer { 577 @Override public Component getListCellRendererComponent(JList list, 578 Object value, int index, boolean isSelected, boolean cellHasFocus) 579 { 580 super.getListCellRendererComponent(list, value, index, isSelected, 581 cellHasFocus); 582 583 if (value instanceof JLabel) { 584 setText(((JLabel)value).getText()); 585 setIcon(((JLabel)value).getIcon()); 586 } 587 588 return this; 589 } 590 591 @Override protected void paintComponent(Graphics g) { 592 Graphics2D g2d = (Graphics2D)g; 593 g2d.setRenderingHints(StartupManager.HINTS); 594 super.paintComponent(g2d); 595 } 596 } 597 598 private static abstract class CommandButton extends JButton 599 implements ActionListener 600 { 601 public CommandButton(final String label) { 602 super(label); 603 McVGuiUtils.setComponentWidth(this); 604 addActionListener(this); 605 } 606 607 @Override public void paintComponent(Graphics g) { 608 Graphics2D g2d = (Graphics2D)g; 609 g2d.setRenderingHints(StartupManager.HINTS); 610 super.paintComponent(g2d); 611 } 612 613 abstract public void actionPerformed(final ActionEvent e); 614 } 615 616 private static class ApplyButton extends CommandButton { 617 public ApplyButton() { 618 super("Apply"); 619 } 620 public void actionPerformed(final ActionEvent e) { 621 StartupManager.getInstance().handleApply(); 622 } 623 } 624 625 private static class OkButton extends CommandButton { 626 public OkButton() { 627 super("OK"); 628 } 629 public void actionPerformed(final ActionEvent e) { 630 StartupManager.getInstance().handleOk(); 631 } 632 } 633 634 private static class HelpButton extends CommandButton { 635 public HelpButton() { 636 super("Help"); 637 } 638 public void actionPerformed(final ActionEvent e) { 639 StartupManager.getInstance().handleHelp(); 640 } 641 } 642 643 private static class CancelButton extends CommandButton { 644 public CancelButton() { 645 super("Cancel"); 646 } 647 public void actionPerformed(final ActionEvent e) { 648 StartupManager.getInstance().handleCancel(); 649 } 650 } 651 652 public static Properties getDefaultProperties() { 653 Properties props = new Properties(); 654 String osName = System.getProperty("os.name"); 655 if (osName.startsWith("Mac OS X")) { 656 props.setProperty("userpath", String.format("%s%s%s%s%s", System.getProperty("user.home"), File.separator, "Documents", File.separator, Constants.USER_DIRECTORY_NAME)); 657 } else { 658 props.setProperty("userpath", String.format("%s%s%s", System.getProperty("user.home"), File.separator, Constants.USER_DIRECTORY_NAME)); 659 } 660 props.setProperty(Constants.PROP_SYSMEM, "0"); 661 return props; 662 } 663 664 /** 665 * Extract any command-line properties and their corresponding values. 666 * 667 * <p>May print out usage information if a badly formatted 668 * {@literal "property=value"} pair is encountered, or when an unknown 669 * argument is found (depending on value of the {@code ignoreUnknown} 670 * parameter). 671 * 672 * <p><b>NOTE:</b> {@code null} is not a permitted value for any parameter. 673 * 674 * @param ignoreUnknown Whether or not to handle unknown arguments. 675 * @param fromStartupManager Whether or not this call originated from 676 * {@code startupmanager.jar}. 677 * @param args Array containing command-line arguments. 678 * @param defaults Default parameter values. 679 * 680 * @return Command-line arguments as a collection of property identifiers 681 * and values. 682 */ 683 public static Properties getArgs(final boolean ignoreUnknown, 684 final boolean fromStartupManager, final String[] args, 685 final Properties defaults) 686 { 687 Properties props = new Properties(defaults); 688 for (int i = 0; i < args.length; i++) { 689 690 // handle property definitions 691 if (args[i].startsWith("-D")) { 692 List<String> l = StringUtil.split(args[i].substring(2), "="); 693 if (l.size() == 2) { 694 props.setProperty(l.get(0), l.get(1)); 695 } else { 696 usage("Invalid property:" + args[i]); 697 } 698 } 699 700 // handle userpath changes 701 else if (ARG_USERPATH.equals(args[i]) && ((i + 1) < args.length)) { 702 props.setProperty("userpath", args[++i]); 703 } 704 705 // handle help requests 706 else if (ARG_HELP.equals(args[i]) && (fromStartupManager)) { 707 System.err.println(USAGE_MESSAGE); 708 System.err.println(getUsageMessage()); 709 System.exit(1); 710 } 711 712 // bail out for unknown args, unless we don't care! 713 else if (!ignoreUnknown){ 714 usage("Unknown argument: " + args[i]); 715 } 716 } 717 return props; 718 } 719 720 public static int getMaximumHeapSize() { 721 int sysmem = 722 StartupManager.getInstance().getPlatform().getAvailableMemory(); 723 if ((sysmem > Constants.MAX_MEMORY_32BIT) && 724 (!System.getProperty("os.arch").contains("64"))) 725 { 726 return Constants.MAX_MEMORY_32BIT; 727 } 728 return sysmem; 729 } 730 731 /** 732 * Print out the command line usage message and exit. Taken entirely from 733 * {@link ucar.unidata.idv.ArgsManager}. 734 * 735 * @param err The usage message 736 */ 737 private static void usage(final String err) { 738 String msg = USAGE_MESSAGE; 739 msg = msg + '\n' + getUsageMessage(); 740 LogUtil.userErrorMessage(err + '\n' + msg); 741 System.exit(1); 742 } 743 744 /** 745 * Return the command line usage message. 746 * 747 * @return The usage message 748 */ 749 protected static String getUsageMessage() { 750 return '\t'+ARG_HELP+" (this message)\n"+ 751 '\t'+ARG_USERPATH+" <user directory to use>\n"+ 752 "\t-Dpropertyname=value (Define the property value)\n"; 753 } 754 755 /** 756 * Applies the command line arguments to the startup preferences. 757 * 758 * This method is mostly useful because it allows us to supply an 759 * arbitrary {@code args} array, link in 760 * {@link edu.wisc.ssec.mcidasv.McIDASV#main(String[])}. 761 * 762 * @param ignoreUnknown If {@code true} ignore any parameters that do not 763 * apply to the startup manager. If {@code false}, 764 * the non-applicable parameters should signify an 765 * error. 766 * @param fromStartupManager Whether or not this call originated from the 767 * startup manager (rather than preferences). 768 * @param args Incoming command line arguments. Cannot be {@code null}. 769 * 770 * @throws NullPointerException if {@code args} is null. 771 * 772 * @see #getArgs(boolean, boolean, String[], Properties) 773 */ 774 public static void applyArgs(final boolean ignoreUnknown, 775 final boolean fromStartupManager, 776 final String[] args) 777 throws IllegalArgumentException 778 { 779 Objects.requireNonNull(args, "Argument list cannot be null"); 780 781 StartupManager sm = StartupManager.getInstance(); 782 Platform platform = sm.getPlatform(); 783 784 Properties props = getArgs(ignoreUnknown, 785 fromStartupManager, 786 args, 787 getDefaultProperties()); 788 platform.setUserDirectory(props.getProperty("userpath")); 789 platform.setAvailableMemory(GetMem.getMemory()); 790 } 791 792 /** 793 * Extracts all startup preferences and returns them in a convenient 794 * {@code Map}. 795 * 796 * @return Either a {@link HashMap} mapping {@literal "preference ID"} to 797 * its corresponding value, or an empty map. 798 */ 799 public static Map<String, String> getStartupPrefs() { 800 StartupManager sm = StartupManager.getInstance(); 801 int size = OptionMaster.getInstance().blahblah.length; 802 File script = new File(sm.getPlatform().getUserPrefs()); 803 Map<String, String> startupPrefs = new HashMap<>(size); 804 try (BufferedReader br = new BufferedReader(new FileReader(script))) { 805 String line; 806 while ((line = br.readLine()) != null) { 807 if (line.startsWith("#")) { 808 continue; 809 } 810 if (line.startsWith(SET_PREFIX)) { 811 line = line.replace(SET_PREFIX, EMPTY_STRING); 812 } 813 int splitAt = line.indexOf('='); 814 if (splitAt >= 0) { 815 String k = line.substring(0, splitAt); 816 String v = line.substring(splitAt + 1); 817 startupPrefs.put(k, v); 818 } 819 } 820 } catch (IOException e) { 821 System.err.println("Problem reading from '"+script.getPath()+"': "+e.getMessage()); 822 } 823 return startupPrefs; 824 } 825 826 public static void main(String[] args) { 827 applyArgs(false, true, args); 828 StartupManager.getInstance().createDisplay(); 829 } 830}