001 /*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2013
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
029 package edu.wisc.ssec.mcidasv.chooser;
030
031 import static javax.swing.GroupLayout.DEFAULT_SIZE;
032 import static javax.swing.GroupLayout.Alignment.LEADING;
033 import static javax.swing.GroupLayout.Alignment.TRAILING;
034
035 import java.awt.Component;
036 import java.awt.Dimension;
037 import java.io.File;
038 import java.util.ArrayList;
039 import java.util.Hashtable;
040 import java.util.List;
041
042 import javax.swing.GroupLayout;
043 import javax.swing.JCheckBox;
044 import javax.swing.JComponent;
045 import javax.swing.JFileChooser;
046 import javax.swing.JLabel;
047 import javax.swing.JPanel;
048 import javax.swing.JRadioButton;
049 import javax.swing.JTextField;
050
051 import org.w3c.dom.Element;
052
053 import ucar.unidata.data.DataSource;
054 import ucar.unidata.idv.chooser.IdvChooserManager;
055 import ucar.unidata.util.FileManager;
056 import ucar.unidata.util.GuiUtils;
057 import ucar.unidata.util.Misc;
058 import ucar.unidata.util.PollingInfo;
059 import ucar.unidata.xml.XmlUtil;
060
061 import edu.wisc.ssec.mcidasv.ArgumentManager;
062 import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
063
064 /**
065 * A class for choosing files that can be polled.
066 *
067 * @author IDV development team
068 */
069 public class PollingFileChooser extends FileChooser {
070
071 /** Any initial file system path to start with */
072 public static final String ATTR_DIRECTORY = "directory";
073
074 /** Polling interval */
075 public static final String ATTR_INTERVAL = "interval";
076
077 /** The title attribute */
078 public static final String ATTR_TITLE = "title";
079
080 /** polling info */
081 private PollingInfo pollingInfo;
082
083 /** file path widget accessible to everyone */
084 private JTextField filePathWidget;
085
086 /** file pattern widget accessible to everyone */
087 private JTextField filePatternWidget;
088
089 /** Keep track of what was selected and update status accordingly */
090 boolean isDirectory = false;
091 int directoryCount = 0;
092 int fileCount = 0;
093
094 /**
095 * Create the PollingFileChooser, passing in the manager and the xml element
096 * from choosers.xml
097 *
098 * @param mgr The manager
099 * @param root The xml root
100 *
101 */
102 public PollingFileChooser(IdvChooserManager mgr, Element root) {
103 super(mgr, root);
104
105 Element chooserNode = getXmlNode();
106
107 pollingInfo = (PollingInfo) idv.getPreference(PREF_POLLINGINFO + "." + getId());
108 if (pollingInfo == null) {
109 pollingInfo = new PollingInfo();
110 pollingInfo.setMode(PollingInfo.MODE_COUNT);
111 pollingInfo.setName("Directory");
112 pollingInfo.setFilePattern(getAttribute(ATTR_FILEPATTERN, ""));
113 pollingInfo.setFilePaths(Misc.newList(getAttribute(ATTR_DIRECTORY, "")));
114 pollingInfo.setIsActive(XmlUtil.getAttribute(chooserNode, ATTR_POLLON, true));
115
116 pollingInfo.setInterval((long) (XmlUtil.getAttribute(chooserNode, ATTR_INTERVAL, 5.0) * 60 * 1000));
117 int fileCount = 1;
118 String s = XmlUtil.getAttribute(chooserNode, ATTR_FILECOUNT, "1");
119 s = s.trim();
120 if (s.equals("all")) {
121 fileCount = Integer.MAX_VALUE;
122 } else {
123 fileCount = new Integer(s).intValue();
124 }
125 pollingInfo.setFileCount(fileCount);
126 }
127 filePathWidget = pollingInfo.getFilePathWidget();
128 filePatternWidget = pollingInfo.getPatternWidget();
129
130 }
131
132 /**
133 * An extension of JFileChooser
134 *
135 * @author IDV Development Team
136 * @version $Revision$
137 */
138 public class MyDirectoryChooser extends MyFileChooser {
139
140 /**
141 * Create the file chooser
142 *
143 * @param path the initial path
144 */
145 public MyDirectoryChooser(String path) {
146 super(path);
147 setMultiSelectionEnabled(getAllowMultiple());
148 setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
149 }
150
151 /**
152 * Set the selected directory
153 *
154 * @param selectedDirectory the selected directory
155 */
156 public void setCurrentDirectory(File selectedDirectory) {
157 super.setCurrentDirectory(selectedDirectory);
158 setSelectedFiles(null);
159 }
160
161 /**
162 * Set the selected files
163 *
164 * @param selectedFiles the selected files
165 */
166 public void setSelectedFiles(File[] selectedFiles) {
167 fileCount=0;
168 directoryCount=0;
169 if (selectedFiles == null || selectedFiles.length == 0) {
170 isDirectory = true;
171 if (filePathWidget!=null) {
172 filePathWidget.setText(this.getCurrentDirectory().getAbsolutePath());
173 }
174 }
175 else {
176 isDirectory = false;
177 for (File selectedFile : selectedFiles) {
178 if (selectedFile.isFile()) fileCount++;
179 if (selectedFile.isDirectory()) {
180 directoryCount++;
181 if (directoryCount==1 && filePathWidget!=null) {
182 filePathWidget.setText(selectedFile.getAbsolutePath());
183 }
184 }
185 }
186 }
187 super.setSelectedFiles(selectedFiles);
188
189 // Disable load button if we arrived here by typing a directory or file name
190 if (directoryCount > 0 ||
191 directoryCount == 0 && fileCount == 0 && !isDirectory) {
192 setHaveData(false);
193 }
194 else {
195 setHaveData(true);
196 }
197
198 updateStatus();
199 }
200
201 }
202
203 /**
204 * Make the file chooser
205 *
206 * @param path the initial path
207 *
208 * @return the file chooser
209 */
210 protected JFileChooser doMakeDirectoryChooser(String path) {
211 return new MyDirectoryChooser(path);
212 }
213
214 /**
215 * Override the base class method to catch the do load
216 * This directly handles loading directories and passes off files to selectFiles() and selectFilesInner()
217 */
218 public void doLoadInThread() {
219 Element chooserNode = getXmlNode();
220
221 idv.getStateManager().writePreference(PREF_POLLINGINFO + "." + getId(), pollingInfo);
222 idv.getStateManager().writePreference(PREF_DEFAULTDIR + getId(), pollingInfo.getFile());
223
224 // userMessage("doLoadInThread: fileCount: " + fileCount + ", directoryCount: " + directoryCount + ", isDirectory: " + isDirectory + ", getHaveData: " + getHaveData() + ", buttonPressed: " + buttonPressed);
225
226 // If a user types in a directory (on Windows) do not try to load that directory.
227 // If the load button was pressed, go for it!
228 if (fileCount == 0 && !buttonPressed) return;
229
230 // If this is file(s) only, use FileChooser.doLoadInThread()
231 if (fileCount > 0) {
232 super.doLoadInThread();
233 return;
234 }
235
236 Hashtable properties = new Hashtable();
237 if ( !pollingInfo.applyProperties()) {
238 return;
239 }
240
241 String title = basename(pollingInfo.getFile());
242 title += "/" + ((JTextField)pollingInfo.getPatternWidget()).getText();
243 pollingInfo.setName(title);
244 properties.put(DataSource.PROP_TITLE, title);
245 properties.put(DataSource.PROP_POLLINFO, pollingInfo.cloneMe());
246
247 String dataSourceId;
248 if (XmlUtil.hasAttribute(chooserNode, ATTR_DATASOURCEID)) {
249 dataSourceId = XmlUtil.getAttribute(chooserNode, ATTR_DATASOURCEID);
250 } else {
251 dataSourceId = getDataSourceId();
252 }
253
254 makeDataSource(pollingInfo.getFiles(), dataSourceId, properties);
255 }
256
257 /**
258 * Handle the selection of the set of files
259 * Copy from IDV FileChooser, add ability to name and poll
260 */
261 protected boolean selectFilesInner(File[] files, File directory)
262 throws Exception {
263 Element chooserNode = getXmlNode();
264
265 if ((files == null) || (files.length == 0)) {
266 userMessage("Please select a file");
267 return false;
268 }
269 FileManager.addToHistory(files[0]);
270 List selectedFiles = new ArrayList();
271 String fileNotExistsError = "";
272 boolean didXidv = false;
273
274 for (int i = 0; i < files.length; i++) {
275 if ( !files[i].exists()) {
276 fileNotExistsError += "File does not exist: " + files[i] + "\n";
277 } else {
278 String filename = files[i].toString();
279 //Check for the bundle or jnlp file
280 if (((ArgumentManager)idv.getArgsManager()).isBundle(filename)
281 || idv.getArgsManager().isJnlpFile(filename)) {
282 didXidv = idv.handleAction(filename, null);
283 } else {
284 selectedFiles.add(filename);
285 }
286 }
287 }
288
289 if (didXidv) {
290 closeChooser();
291 return true;
292 }
293
294 if (selectedFiles.size() == 0) {
295 return false;
296 }
297
298 if (fileNotExistsError.length() > 0) {
299 userMessage(fileNotExistsError);
300 return false;
301 }
302
303 Object definingObject = selectedFiles;
304 String title = selectedFiles.size() + " files";
305 if (selectedFiles.size() == 1) {
306 definingObject = selectedFiles.get(0);
307 title = basename(definingObject.toString());
308 }
309
310 String dataSourceId;
311 if (XmlUtil.hasAttribute(chooserNode, ATTR_DATASOURCEID)) {
312 dataSourceId = XmlUtil.getAttribute(chooserNode, ATTR_DATASOURCEID);
313 } else {
314 dataSourceId = getDataSourceId();
315 }
316
317 Hashtable properties = new Hashtable();
318
319 // TODO: I disabled file polling on purpose:
320 // The control for this is in the Directory options and is grayed out
321 // when selecting single files. Is this something people want?
322 PollingInfo newPollingInfo = new PollingInfo(false);
323 String pattern = getFilePattern();
324 if ((pattern != null) && (pattern.length() > 0)) {
325 newPollingInfo.setFilePattern(pattern);
326 }
327 newPollingInfo.setName(title);
328 properties.put(DataSource.PROP_TITLE, title);
329 properties.put(DataSource.PROP_POLLINFO, newPollingInfo);
330
331 // explicitly denote whether or not this was a "bulk load". these data
332 // sources require a little extra attention when being unpersisted.
333 properties.put("bulk.load", (selectedFiles.size() > 1));
334 return makeDataSource(definingObject, dataSourceId, properties);
335 }
336
337 /**
338 * Emulate basename()
339 */
340 private String basename(String path) {
341 if (path.lastIndexOf('/') > 0)
342 path = path.substring(path.lastIndexOf('/'));
343 else if (path.lastIndexOf('\\') > 0)
344 path = path.substring(path.lastIndexOf('\\'));
345 if (path.length() > 1)
346 path = path.substring(1);
347 return path;
348 }
349
350 /**
351 * Get the tooltip for the load button
352 *
353 * @return The tooltip for the load button
354 */
355 protected String getLoadToolTip() {
356 return "Load the file(s) or directory";
357 }
358
359 /**
360 * Override the base class method to catch the do update.
361 */
362 @Override public void doUpdate() {
363 fileChooser.rescanCurrentDirectory();
364 }
365
366 /**
367 * Process PollingInfo GUI components based on their label and properties
368 * Turn it into a nicely-formatted labeled panel
369 */
370 private JPanel processPollingOption(JLabel label, JPanel panel) {
371 String string = label.getText().trim();
372
373 // File Pattern
374 if (string.equals("File Pattern:")) {
375 Component panel1 = panel.getComponent(0);
376 if (panel1 instanceof JPanel) {
377 Component[] comps = ((JPanel)panel1).getComponents();
378 if (comps.length == 2) {
379 List newComps1 = new ArrayList();
380 List newComps2 = new ArrayList();
381 if (comps[0] instanceof JPanel) {
382 Component[] comps2 = ((JPanel)comps[0]).getComponents();
383 if (comps2.length==1 &&
384 comps2[0] instanceof JPanel)
385 comps2=((JPanel)comps2[0]).getComponents();
386 if (comps2.length == 2) {
387 if (comps2[0] instanceof JTextField) {
388 McVGuiUtils.setComponentWidth((JTextField) comps2[0], McVGuiUtils.Width.SINGLE);
389 }
390 newComps1.add(comps2[0]);
391 newComps2.add(comps2[1]);
392 }
393 }
394 newComps1.add(comps[1]);
395 panel = GuiUtils.vbox(
396 GuiUtils.left(GuiUtils.hbox(newComps1)),
397 GuiUtils.left(GuiUtils.hbox(newComps2))
398 );
399 }
400 }
401 }
402
403 // Files
404 if (string.equals("Files:")) {
405 Component panel1 = panel.getComponent(0);
406 if (panel1 instanceof JPanel) {
407 Component[] comps = ((JPanel)panel1).getComponents();
408 if (comps.length == 6) {
409 List newComps1 = new ArrayList();
410 List newComps2 = new ArrayList();
411 if (comps[3] instanceof JRadioButton) {
412 String text = ((JRadioButton) comps[3]).getText().trim();
413 if (text.equals("All files in last:")) text="All files in last";
414 ((JRadioButton) comps[3]).setText(text);
415 }
416 if (comps[4] instanceof JTextField) {
417 McVGuiUtils.setComponentWidth((JTextField) comps[4], McVGuiUtils.Width.HALF);
418 }
419 if (comps[5] instanceof JLabel) {
420 String text = ((JLabel) comps[5]).getText().trim();
421 ((JLabel) comps[5]).setText(text);
422 }
423 newComps1.add(comps[0]);
424 newComps1.add(comps[1]);
425 newComps2.add(comps[3]);
426 newComps2.add(comps[4]);
427 newComps2.add(comps[5]);
428 panel = GuiUtils.vbox(
429 GuiUtils.left(GuiUtils.hbox(newComps1)),
430 GuiUtils.left(GuiUtils.hbox(newComps2))
431 );
432 }
433 }
434 }
435
436 // Polling
437 if (string.equals("Polling:")) {
438 Component panel1 = panel.getComponent(0);
439 if (panel1 instanceof JPanel) {
440 Component[] comps = ((JPanel)panel1).getComponents();
441 if (comps.length == 4) {
442 List newComps = new ArrayList();
443 if (comps[0] instanceof JCheckBox) {
444 ((JCheckBox) comps[0]).setText("");
445 }
446 if (comps[1] instanceof JLabel) {
447 String text = ((JLabel) comps[1]).getText().trim();
448 if (text.equals("Check every:")) text="Refresh every";
449 ((JLabel) comps[1]).setText(text);
450 }
451 if (comps[2] instanceof JTextField) {
452 McVGuiUtils.setComponentWidth((JTextField) comps[2], McVGuiUtils.Width.HALF);
453 }
454 if (comps[3] instanceof JLabel) {
455 String text = ((JLabel) comps[3]).getText().trim();
456 ((JLabel) comps[3]).setText(text);
457 }
458 newComps.add(comps[0]);
459 newComps.add(comps[1]);
460 newComps.add(comps[2]);
461 newComps.add(comps[3]);
462 string="";
463 panel = GuiUtils.left(GuiUtils.hbox(newComps));
464 }
465 }
466 }
467
468 return McVGuiUtils.makeLabeledComponent(string, panel);
469 }
470
471 /**
472 * Turn PollingInfo options into a nicely-formatted panel
473 */
474 private JPanel processPollingOptions(List comps) {
475 List newComps = new ArrayList();
476 newComps = new ArrayList();
477 if (comps.size() == 4) {
478 // newComps.add(comps.get(0));
479
480 // Put Recent and Pattern panels next to each other and make them bordered
481 Component[] labelPanel1 = ((JPanel)comps.get(2)).getComponents();
482 Component[] labelPanel2 = ((JPanel)comps.get(1)).getComponents();
483 if (labelPanel1[1] instanceof JPanel && labelPanel2[1] instanceof JPanel) {
484 JPanel recentPanel = (JPanel)labelPanel1[1];
485 JPanel patternPanel = (JPanel)labelPanel2[1];
486 recentPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Recent Files"));
487 patternPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("File Pattern"));
488
489 // Make the container panel
490 JPanel filePanel = new JPanel();
491
492 GroupLayout layout = new GroupLayout(filePanel);
493 filePanel.setLayout(layout);
494 layout.setHorizontalGroup(
495 layout.createParallelGroup(LEADING)
496 .addGroup(layout.createSequentialGroup()
497 .addComponent(recentPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
498 .addGap(GAP_RELATED)
499 .addComponent(patternPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
500 )
501 );
502 layout.setVerticalGroup(
503 layout.createParallelGroup(LEADING)
504 .addGroup(layout.createSequentialGroup()
505 .addGroup(layout.createParallelGroup(TRAILING)
506 .addComponent(recentPanel, LEADING, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
507 .addComponent(patternPanel, LEADING, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
508 )
509 );
510
511 newComps.add(McVGuiUtils.makeLabeledComponent("Directory:", filePanel));
512 }
513 else {
514 newComps.add(comps.get(1));
515 newComps.add(comps.get(2));
516 }
517 newComps.add(comps.get(3));
518 }
519 else {
520 newComps = comps;
521 }
522 return GuiUtils.top(GuiUtils.vbox(newComps));
523 }
524
525 /**
526 * Set the status message appropriately
527 */
528 protected void updateStatus() {
529 super.updateStatus();
530 String selectedReference = "the selected data";
531
532 if(!getHaveData()) {
533 setStatus("Select zero, one, or multiple files");
534 GuiUtils.enableTree(bottomPanel, false);
535 return;
536 }
537
538 if (isDirectory) {
539 selectedReference = "all files in this directory";
540 }
541 else {
542 if (fileCount > 0) {
543 if (fileCount > 1) selectedReference = "the selected files";
544 else selectedReference = "the selected file";
545 }
546 if (directoryCount > 0) {
547 selectedReference = "the selected directory";
548 }
549 }
550 GuiUtils.enableTree(bottomPanel, isDirectory || directoryCount > 0);
551 setStatus("Press \"" + CMD_LOAD + "\" to load " + selectedReference, "buttons");
552 }
553
554 /**
555 * Get the top panel for the chooser
556 * @return the top panel
557 */
558 // protected JPanel getTopPanel() {
559 // return McVGuiUtils.makeLabeledComponent("Source Name:", pollingInfo.getNameWidget());
560 // }
561
562 /**
563 * Get the center panel for the chooser
564 * @return the center panel
565 */
566 protected JPanel getCenterPanel() {
567 fileChooser = doMakeDirectoryChooser(path);
568 fileChooser.setPreferredSize(new Dimension(300, 300));
569 fileChooser.setMultiSelectionEnabled(getAllowMultiple());
570
571 JPanel centerPanel;
572 JComponent accessory = getAccessory();
573 if (accessory == null) {
574 centerPanel = GuiUtils.center(fileChooser);
575 } else {
576 centerPanel = GuiUtils.centerRight(fileChooser, GuiUtils.top(accessory));
577 }
578 centerPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
579 return McVGuiUtils.makeLabeledComponent("Files:", centerPanel);
580 }
581
582 /**
583 * Get the bottom panel for the chooser
584 * @return the bottom panel
585 */
586 protected JPanel getBottomPanel() {
587
588 // Pull apart the PollingInfo components and rearrange them
589 // Don't want to override PollingInfo because it isn't something the user sees
590 // Arranged like: Label, Panel; Label, Panel; Label, Panel; etc...
591 List comps = new ArrayList();
592 List newComps = new ArrayList();
593 pollingInfo.getPropertyComponents(comps, false, pollingInfo.getFileCount()>0);
594 for (int i=0; i<comps.size()-1; i++) {
595 JComponent compLabel = (JComponent)comps.get(i);
596 if (compLabel instanceof JLabel) {
597 i++;
598 JComponent compPanel = (JComponent)comps.get(i);
599 if (compPanel instanceof JPanel) {
600 newComps.add(processPollingOption((JLabel)compLabel, (JPanel)compPanel));
601 }
602 }
603 }
604
605 JPanel pollingPanel = processPollingOptions(newComps);
606 return pollingPanel;
607 }
608
609 }
610