001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2015 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.startupmanager.options; 030 031import java.io.File; 032 033import java.nio.file.Path; 034import java.nio.file.Paths; 035import java.util.regex.Pattern; 036 037import java.awt.event.ActionEvent; 038import java.awt.event.ActionListener; 039 040import javax.swing.JButton; 041import javax.swing.JCheckBox; 042import javax.swing.JComponent; 043import javax.swing.JFileChooser; 044import javax.swing.JPanel; 045import javax.swing.JTextField; 046import javax.swing.SwingUtilities; 047 048import edu.wisc.ssec.mcidasv.startupmanager.StartupManager; 049import edu.wisc.ssec.mcidasv.util.McVGuiUtils; 050 051/** 052 * Represents a file selection. 053 */ 054public final class FileOption extends AbstractOption { 055 056 /** Label for {@link #browseButton}. */ 057 private static final String BUTTON_LABEL = "Browse..."; 058 059 /** Label for {@link #enableCheckBox}. */ 060 private static final String CHECKBOX_LABEL = "Specify default bundle:"; 061 062 /** System property that points to the McIDAS-V user path. */ 063 private static final String USERPATH = "mcv.userpath"; 064 065 /** Name of the {@literal "bundle"} subdirectory of the user path. */ 066 private static final String BUNDLE_DIR = "bundles"; 067 068 /** 069 * Regular expression pattern for ensuring that no quote marks are present 070 * in {@link #value}. 071 */ 072 private static final Pattern CLEAN_VALUE_REGEX = Pattern.compile("\""); 073 074 /** Formatting string used by {@link #toString()}. */ 075 private static final String FORMAT = 076 "[FileOption@%x: optionId=%s, value=%s]"; 077 078 /** Tool tip used by {@link #bundleField}. */ 079 public static final String BUNDLE_FIELD_TIP = 080 "Path to default bundle. An empty path signifies that there is no" 081 + " default bundle in use."; 082 083 /** Default option value. See {@link OptionMaster#blahblah}. */ 084 private final String defaultValue; 085 086 /** Default state of {@link #enableCheckBox}. */ 087 private final boolean defaultCheckBox; 088 089 /** Default path for {@link #bundleField}. */ 090 private final String defaultBundle; 091 092 /** 093 * Shows current default bundle. Empty means there isn't one. May be 094 * {@code null}! 095 */ 096 private JTextField bundleField; 097 098 /** Used to pop up a {@link JFileChooser}. May be {@code null}! */ 099 private JButton browseButton; 100 101 /** 102 * Whether or not the default bundle should be used. May be {@code null}! 103 */ 104 private JCheckBox enableCheckBox; 105 106 /** Current state of {@link #enableCheckBox}. */ 107 private boolean checkbox; 108 109 /** Current contents of {@link #bundleField}. Value may be {@code null}! */ 110 private String path; 111 112 /** 113 * Create a new {@literal "file option"} that allows the user to select 114 * a file. 115 * 116 * @param id Option ID. 117 * @param label Option label (used in GUI). 118 * @param defaultValue Default option value. 119 * @param optionPlatform Platform restrictions for the option. 120 * @param optionVisibility Visibility restrictions for the option. 121 */ 122 public FileOption( 123 final String id, 124 final String label, 125 final String defaultValue, 126 final OptionMaster.OptionPlatform optionPlatform, 127 final OptionMaster.Visibility optionVisibility) 128 { 129 super(id, label, OptionMaster.Type.DIRTREE, optionPlatform, optionVisibility); 130 this.defaultValue = defaultValue; 131 String[] defaults = parseFormat(defaultValue); 132 this.defaultCheckBox = booleanFromFormat(defaults[0]); 133 this.defaultBundle = defaults[1]; 134 setValue(defaultValue); 135 } 136 137 /** 138 * Handles the user clicking on the {@link #browseButton}. 139 * 140 * @param event Currently ignored. 141 */ 142 private void browseButtonActionPerformed(ActionEvent event) { 143 String defaultPath = 144 StartupManager.getInstance().getPlatform().getUserBundles(); 145 String userPath = System.getProperty(USERPATH, defaultPath); 146 String bundlePath = Paths.get(userPath, BUNDLE_DIR).toString(); 147 setValue(selectBundle(bundlePath)); 148 } 149 150 /** 151 * Show a {@code JFileChooser} dialog that allows the user to select a 152 * bundle. 153 * 154 * @param bundleDirectory Initial directory for the {@code JFileChooser}. 155 * 156 * @return Either the path to the user's chosen bundle, or 157 * {@link #defaultValue} if the user cancelled. 158 */ 159 private String selectBundle(final String bundleDirectory) { 160 JFileChooser fileChooser = new JFileChooser(); 161 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 162 if ((path != null) && !path.isEmpty()) { 163 fileChooser.setSelectedFile(new File(path)); 164 } else { 165 fileChooser.setCurrentDirectory(new File(bundleDirectory)); 166 } 167 String result = defaultValue; 168 switch (fileChooser.showOpenDialog(null)) { 169 case JFileChooser.APPROVE_OPTION: 170 result = fileChooser.getSelectedFile().getAbsolutePath(); 171 break; 172 default: 173 result = path; 174 break; 175 } 176 return '"' + getCheckBoxValue() + ';' + result + '"'; 177 } 178 179 /** 180 * Returns the GUI component that represents the option. 181 * 182 * @return GUI representation of this option. 183 */ 184 @Override public JComponent getComponent() { 185 bundleField = new JTextField(path); 186 bundleField.setColumns(30); 187 bundleField.setToolTipText(BUNDLE_FIELD_TIP); 188 bundleField.setEnabled(checkbox); 189 190 browseButton = new JButton(BUTTON_LABEL); 191 browseButton.setEnabled(checkbox); 192 browseButton.addActionListener(new ActionListener() { 193 @Override public void actionPerformed(ActionEvent e) { 194 browseButtonActionPerformed(e); 195 } 196 }); 197 198 enableCheckBox = new JCheckBox(CHECKBOX_LABEL, checkbox); 199 enableCheckBox.addActionListener(new ActionListener() { 200 @Override 201 public void actionPerformed(ActionEvent e) { 202 boolean status = enableCheckBox.isSelected(); 203 bundleField.setEnabled(status); 204 browseButton.setEnabled(status); 205 } 206 }); 207 JPanel bottom = McVGuiUtils.sideBySide(bundleField, browseButton); 208 return McVGuiUtils.topBottom(enableCheckBox, bottom, McVGuiUtils.Prefer.NEITHER); 209 } 210 211 /** 212 * 213 * @return The current value of the option. 214 */ 215 @Override public String getValue() { 216 return '"' + getCheckBoxValue() + ';' + getBundlePath() + '"'; 217 } 218 219 public String getCheckBoxValue() { 220 boolean status = defaultCheckBox; 221 if (enableCheckBox != null) { 222 status = enableCheckBox.isSelected(); 223 } 224 return status ? "1" : "0"; 225 } 226 227 public String getBundlePath() { 228 String result = defaultBundle; 229 if (bundleField != null) { 230 result = bundleField.getText(); 231 } 232 return result; 233 } 234 235 /** 236 * Forces the value of the option to the data specified. 237 * 238 * @param newValue New value to use. 239 */ 240 @Override public void setValue(final String newValue) { 241 String[] results = parseFormat(newValue); 242 checkbox = booleanFromFormat(results[0]); 243 path = results[1]; 244 SwingUtilities.invokeLater(new Runnable() { 245 @Override public void run() { 246 String[] results = parseFormat(newValue); 247 checkbox = booleanFromFormat(results[0]); 248 path = results[1]; 249 if (enableCheckBox != null) { 250 enableCheckBox.setSelected(checkbox); 251 } 252 253 // defaultValue check is to avoid blanking out the field 254 // when the user hits cancel 255 if ((bundleField != null) && !defaultBundle.equals(path)) { 256 bundleField.setEnabled(checkbox); 257 bundleField.setText(path); 258 } 259 260 if (browseButton != null) { 261 browseButton.setEnabled(checkbox); 262 } 263 } 264 }); 265 } 266 267 /** 268 * Friendly string representation of the option. 269 * 270 * @return {@code String} containing relevant info about the option. 271 */ 272 @Override public String toString() { 273 return String.format(FORMAT, hashCode(), getOptionId(), getValue()); 274 } 275 276 public static String[] parseFormat(String format) { 277 format = CLEAN_VALUE_REGEX.matcher(format).replaceAll(""); 278 String checkBox = "1"; 279 String path; 280 int splitAt = format.indexOf(';'); 281 if (splitAt == -1) { 282 // string was something like "/path/goes/here.mcv" 283 path = format; 284 } else if (splitAt == 0) { 285 // string was something like ";/path/goes/here.mcv" 286 path = format.substring(1); 287 } else { 288 // string was something like "1;/path/goes/here.mcv" 289 checkBox = format.substring(0, splitAt); 290 path = format.substring(splitAt + 1); 291 } 292 if (path.isEmpty()) { 293 checkBox = "0"; 294 } 295 return new String[] { checkBox, path }; 296 } 297 298 public static boolean booleanFromFormat(String value) { 299 boolean result = false; 300 if ("1".equals(value)) { 301 result = true; 302 } 303 return result; 304 } 305}