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