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