001 package org.LiveGraph.gui.dfs; 002 003 import java.awt.BorderLayout; 004 import java.awt.Color; 005 import java.awt.Dimension; 006 import java.awt.FontMetrics; 007 import java.awt.GridBagConstraints; 008 import java.awt.GridBagLayout; 009 import java.awt.Insets; 010 import java.awt.event.ActionEvent; 011 import java.awt.event.ActionListener; 012 import java.io.File; 013 import java.util.Arrays; 014 015 import javax.swing.BorderFactory; 016 import javax.swing.Box; 017 import javax.swing.ButtonGroup; 018 import javax.swing.JButton; 019 import javax.swing.JCheckBox; 020 import javax.swing.JFileChooser; 021 import javax.swing.JLabel; 022 import javax.swing.JPanel; 023 import javax.swing.JRadioButton; 024 import javax.swing.JScrollBar; 025 import javax.swing.JScrollPane; 026 import javax.swing.JSlider; 027 import javax.swing.JTextArea; 028 import javax.swing.event.ChangeEvent; 029 import javax.swing.event.ChangeListener; 030 import javax.swing.filechooser.FileFilter; 031 032 import org.LiveGraph.LiveGraph; 033 import org.LiveGraph.dataCache.CacheEvent; 034 import org.LiveGraph.dataCache.DataCache; 035 import org.LiveGraph.dataCache.UpdateInvoker; 036 import org.LiveGraph.dataCache.DataUpdateEvent; 037 import org.LiveGraph.events.Event; 038 import org.LiveGraph.events.EventType; 039 import org.LiveGraph.gui.LiveGraphSettingsPanel; 040 import org.LiveGraph.gui.Tools; 041 import org.LiveGraph.settings.DataFileSettings; 042 import org.LiveGraph.settings.SettingsEvent; 043 044 import com.softnetConsult.utils.exceptions.Bug; 045 import com.softnetConsult.utils.exceptions.UnexpectedSwitchCase; 046 import com.softnetConsult.utils.files.FileTools; 047 import com.softnetConsult.utils.swing.SwingTools; 048 049 050 /** 051 * The data file settings panel of the application. This is the only component contained in 052 * the content pane of the application's data file settings window. API users may request 053 * {@link org.LiveGraph.gui.GUIManager} to create additional instances of a 054 * {@code DataFileSettingsPanel} if they wish to integrate the LiveGraph GUI into their application. 055 * 056 * <p> 057 * <strong>LiveGraph</strong> 058 * (<a href="http://www.live-graph.org" target="_blank">http://www.live-graph.org</a>). 059 * </p> 060 * <p>Copyright (c) 2007-2008 by G. Paperin.</p> 061 * <p>File: DataFileSettingsPanel.java</p> 062 * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or 063 * without modification, are permitted provided that the following terms and conditions are met: 064 * </p> 065 * <p style="font-size:smaller;">1. Redistributions of source code must retain the above 066 * acknowledgement of the LiveGraph project and its web-site, the above copyright notice, 067 * this list of conditions and the following disclaimer.<br /> 068 * 2. Redistributions in binary form must reproduce the above acknowledgement of the 069 * LiveGraph project and its web-site, the above copyright notice, this list of conditions 070 * and the following disclaimer in the documentation and/or other materials provided with 071 * the distribution.<br /> 072 * 3. All advertising materials mentioning features or use of this software or any derived 073 * software must display the following acknowledgement:<br /> 074 * <em>This product includes software developed by the LiveGraph project and its 075 * contributors.<br />(http://www.live-graph.org)</em><br /> 076 * 4. All advertising materials distributed in form of HTML pages or any other technology 077 * permitting active hyper-links that mention features or use of this software or any 078 * derived software must display the acknowledgment specified in condition 3 of this 079 * agreement, and in addition, include a visible and working hyper-link to the LiveGraph 080 * homepage (http://www.live-graph.org). 081 * </p> 082 * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 083 * OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 084 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 085 * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 086 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 087 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 088 * </p> 089 * 090 * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>) 091 * @version {@value org.LiveGraph.LiveGraph#version} 092 * 093 */ 094 public class DataFileSettingsPanel extends LiveGraphSettingsPanel { 095 096 private static final String [] updateIntervalLabels = { 097 "100 Hz. (Expert mode). Never use \"Do not cache data\"!", 098 "50 Hz. (Expert mode). Never use \"Do not cache data\"!", 099 "10 Hz (10 times per second). Avoid using \"Do not cache data\".", 100 "2 Hz (twice per second). Avoid using \"Do not cache data\".", 101 "every 1 second (1 Hz). Consider avoiding using \"Do not cache data\".", 102 "every 2 seconds (0.5 Hz). Consider avoiding using \"Do not cache data\".", 103 "every 3 seconds (0.333 Hz). Consider avoiding using \"Do not cache data\".", 104 "every 5 seconds (0.2 Hz).", 105 "every 10 seconds (0.1 Hz).", 106 "every 15 seconds.", "every 20 seconds.", 107 "every 30 seconds.", "every 45 seconds.", 108 "every 1 minute.", "every 90 seconds (1.5 minutes).", 109 "every 2 minutes.", "every 3 minutes.", 110 "every 5 minutes.", "every 10 minutes.", 111 "every 15 minutes.", "every 20 minutes.", 112 "every 30 minutes.", "every 45 minutes.", 113 "every 1 hour.", "only manual update."}; 114 115 private static final long [] updateIntervalValues = { 116 10, 20, 100, 500, 117 1000, 2000, 3000, 5000, 10000, 15000, 118 20000, 30000, 45000, 60000, 90000, 120000, 180000, 119 300000, 600000, 900000, 1200000, 1800000, 2700000, 120 3600000, -1}; 121 static { 122 if (updateIntervalLabels.length != updateIntervalValues.length) 123 throw new Bug("The arrays \"updateIntervalLabels\" and \"updateIntervalValues\" are" 124 + " not of the same size!"); 125 } 126 127 private JLabel intervalLabel = null; 128 private JTextArea fileInfoArea = null; 129 private JLabel fileNameLabel = null; 130 private JSlider updateIntervallSlider = null; 131 private JLabel nextUpdateLabel = null; 132 private Color nextUpdateLabelDefaultColour = null; 133 private JCheckBox dontCacheBox = null; 134 private JRadioButton showTailDataButton = null; 135 private JRadioButton showAllDataButton = null; 136 private JFileChooser openFileDialog = null; 137 private JButton openButton = null; 138 139 /** 140 * This is the default constructor. 141 */ 142 public DataFileSettingsPanel() { 143 super(); 144 initialize(); 145 } 146 147 /** 148 * This method initializes the data file settings panel. 149 */ 150 private void initialize() { 151 152 // General settings: 153 154 final JPanel thisPanel = this; 155 Dimension panelDim = new Dimension(470, 300); 156 this.setPreferredSize(panelDim); 157 this.setSize(panelDim); 158 thisPanel.setLayout(new BorderLayout()); 159 160 DataFileSettings dfSettings = LiveGraph.application().getDataFileSettings(); 161 if (null == dfSettings) 162 dfSettings = new DataFileSettings(); 163 164 // Layout: 165 166 JButton button = null; 167 Dimension dim = null; 168 169 // Settings controls: 170 171 JPanel settingsPanel = new JPanel(new GridBagLayout()); 172 settingsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 173 thisPanel.add(settingsPanel, BorderLayout.NORTH); 174 settingsPanel.add(new Box.Filler((dim = new Dimension(1, 1)), dim, dim), 175 new GridBagConstraints(3, 0, 1, 1, 1, 0, 176 GridBagConstraints.WEST, 177 GridBagConstraints.BOTH, 178 new Insets(0, 0, 0, 0), 179 0, 0)); 180 181 // File name input: 182 183 settingsPanel.add(new JLabel("Data file:"), org.LiveGraph.gui.Tools.createGridBagConstraints(0, 0, 3, 1)); 184 185 openFileDialog = null; 186 try { 187 openFileDialog = new JFileChooser(""); 188 openFileDialog.addChoosableFileFilter(new FileFilter() { 189 @Override public boolean accept(File f) { 190 if (null == f) return false; 191 if (f.isDirectory()) return true; 192 return ".csv".equalsIgnoreCase(FileTools.getExtension(f)); 193 } 194 @Override public String getDescription() { return "Comma separated values (*.csv)"; } 195 }); 196 openFileDialog.addChoosableFileFilter(new FileFilter() { 197 @Override public boolean accept(File f) { 198 if (null == f) return false; 199 if (f.isDirectory()) return true; 200 return ".dat".equalsIgnoreCase(FileTools.getExtension(f)); 201 } 202 @Override public String getDescription() { return "Generic data files (*.dat)"; } 203 }); 204 openFileDialog.addChoosableFileFilter(new FileFilter() { 205 @Override public boolean accept(File f) { 206 if (null == f) return false; 207 if (f.isDirectory()) return true; 208 return ".lgdat".equalsIgnoreCase(FileTools.getExtension(f)); 209 } 210 @Override public String getDescription() { return "LiveGraph data files (*.lgdat)"; } 211 }); 212 try { 213 openFileDialog.setCurrentDirectory(new File(System.getProperty("user.dir"))); 214 } catch(SecurityException e) { 215 openFileDialog.setCurrentDirectory(new File(System.getProperty("user.home"))); 216 } 217 } catch(SecurityException e) { 218 openFileDialog = null; 219 } 220 221 fileNameLabel = new JLabel("- no data file selected -"); 222 setFileNameLabel(dfSettings.getDataFile()); 223 fileNameLabel.setFont(SwingTools.getPlainFont(fileNameLabel)); 224 settingsPanel.add(fileNameLabel, Tools.createGridBagConstraints(0, 1, 4, 1)); 225 settingsPanel.add((openButton = new JButton("Open...")), Tools.createGridBagConstraints(4, 1, 1, 1)); 226 openButton.addActionListener(new ActionListener() { 227 public void actionPerformed(ActionEvent e) { 228 229 if (JFileChooser.APPROVE_OPTION != openFileDialog.showOpenDialog(thisPanel)) 230 return; 231 if (!openFileDialog.getSelectedFile().exists()) 232 return; 233 234 String filePath = openFileDialog.getSelectedFile().getAbsolutePath(); 235 DataFileSettings dfs = LiveGraph.application().getDataFileSettings(); 236 dfs.setDataFile(filePath); 237 LiveGraph.application().guiManager().logInfoLn("Data source file : \"" + dfs.getDataFile() + "\"."); 238 } 239 }); 240 openButton.setEnabled(null != openFileDialog); 241 242 // Cache options: 243 244 ButtonGroup bGroup = new ButtonGroup(); 245 bGroup.add(showAllDataButton = new JRadioButton("Show all data", !dfSettings.getShowOnlyTailData())); 246 bGroup.add(showTailDataButton = new JRadioButton("Show tail data", dfSettings.getShowOnlyTailData())); 247 showAllDataButton.addActionListener(new ActionListener() { 248 public void actionPerformed(ActionEvent e) { 249 DataFileSettings dfs = LiveGraph.application().getDataFileSettings(); 250 dfs.setShowOnlyTailData(!showAllDataButton.isSelected()); 251 showAllDataButton.setSelected(!dfs.getShowOnlyTailData()); 252 showTailDataButton.setSelected(dfs.getShowOnlyTailData()); 253 System.out.println("DFS:" + dfs.getShowOnlyTailData()); 254 } 255 }); 256 showTailDataButton.addActionListener(new ActionListener() { 257 public void actionPerformed(ActionEvent e) { 258 DataFileSettings dfs = LiveGraph.application().getDataFileSettings(); 259 dfs.setShowOnlyTailData(showTailDataButton.isSelected()); 260 showAllDataButton.setSelected(!dfs.getShowOnlyTailData()); 261 showTailDataButton.setSelected(dfs.getShowOnlyTailData()); 262 System.out.println("DFS:" + dfs.getShowOnlyTailData()); 263 } 264 }); 265 settingsPanel.add(showAllDataButton, Tools.createGridBagConstraints(0, 3, 1, 1)); 266 settingsPanel.add(showTailDataButton, Tools.createGridBagConstraints(1, 3, 1, 1)); 267 268 dontCacheBox = new JCheckBox("Do not cache data", dfSettings.getDoNotCacheData()); 269 dontCacheBox.addActionListener(new ActionListener() { 270 public void actionPerformed(ActionEvent e) { 271 DataFileSettings dfs = LiveGraph.application().getDataFileSettings(); 272 dfs.setDoNotCacheData(dontCacheBox.isSelected()); 273 dontCacheBox.setSelected(dfs.getDoNotCacheData()); 274 } 275 }); 276 settingsPanel.add(dontCacheBox, Tools.createGridBagConstraints(2, 3, 3, 1)); 277 278 // Update interval slider: 279 280 settingsPanel.add(new JLabel("Update frequency:"), Tools.createGridBagConstraints(0, 4, 3, 1)); 281 282 intervalLabel = new JLabel(updateIntervalLabels[updateIntervalLabels.length - 1]); 283 intervalLabel.setFont(SwingTools.getPlainFont(intervalLabel)); 284 settingsPanel.add(intervalLabel, Tools.createGridBagConstraints(0, 6, 5, 1)); 285 286 updateIntervallSlider = new JSlider(0, updateIntervalLabels.length - 1, updateIntervalLabels.length - 1); 287 updateIntervallSlider.setMinorTickSpacing(1); 288 updateIntervallSlider.setSnapToTicks(true); 289 updateIntervallSlider.setPaintTicks(true); 290 updateIntervallSlider.setPaintTrack(true); 291 updateIntervallSlider.setPaintLabels(false); 292 updateIntervallSlider.setMajorTickSpacing(1); 293 settingsPanel.add(updateIntervallSlider, Tools.createGridBagConstraints(0, 5, 5, 1)); 294 setUpdateFrequencyLabels(dfSettings.getUpdateFrequency()); 295 updateIntervallSlider.addChangeListener(new ChangeListener() { 296 public void stateChanged(ChangeEvent e) { 297 int v = updateIntervallSlider.getValue(); 298 DataFileSettings dfs = LiveGraph.application().getDataFileSettings(); 299 dfs.setUpdateFrequency(updateIntervalValues[v]); 300 setUpdateFrequencyLabels(dfs.getUpdateFrequency()); 301 } 302 }); 303 304 305 // Update buttons & cache settings: 306 307 nextUpdateLabel = new JLabel(formatNextUpdateLabelString(dfSettings.getUpdateFrequency())); 308 settingsPanel.add(nextUpdateLabel, Tools.createGridBagConstraints(0, 7, 4, 1)); 309 nextUpdateLabelDefaultColour = nextUpdateLabel.getForeground(); 310 311 settingsPanel.add((button = new JButton("Update now")), Tools.createGridBagConstraints(4, 7, 1, 1)); 312 button.addActionListener(new ActionListener() { 313 public void actionPerformed(ActionEvent e) { 314 LiveGraph.application().updateInvoker().requestUpdate(); 315 } 316 }); 317 318 319 // File info text field: 320 321 this.fileInfoArea = new JTextArea(); 322 this.fileInfoArea.setEditable(false); 323 JPanel fileInfoPanel = new JPanel(new BorderLayout(5, 5)); 324 fileInfoPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 325 fileInfoPanel.add(new JLabel("File info:"), BorderLayout.NORTH); 326 fileInfoPanel.add(new JScrollPane(this.fileInfoArea), BorderLayout.CENTER); 327 thisPanel.add(fileInfoPanel, BorderLayout.CENTER); 328 329 } // private void initialize() 330 331 /** 332 * Displayes data file info. 333 * @param text Info. 334 */ 335 private void setDataFileInfoText(String text) { 336 fileInfoArea.setText(text + "\n "); 337 JScrollBar sb = ((JScrollPane) fileInfoArea.getParent().getParent()).getVerticalScrollBar(); 338 if (null != sb) 339 sb.setValue(sb.getMaximum()); 340 } // private void setDataFileInfoText 341 342 343 /** 344 * Sets the file name label in the window. If the label is too long, the baginning it stripped off. 345 * @param fileName Data file name. 346 */ 347 private void setFileNameLabel(String fileName) { 348 final String noDataFileLabel = "- no data file selected -"; 349 if (null == fileName || 0 == fileName.trim().length()) { 350 fileNameLabel.setText(noDataFileLabel); 351 return; 352 } 353 fileName = fileName.trim(); 354 FontMetrics fm = fileNameLabel.getFontMetrics(fileNameLabel.getFont()); 355 if (fm.stringWidth(fileName) > fileNameLabel.getWidth() - 10) { 356 while (fileName.length() > noDataFileLabel.length() 357 && fm.stringWidth("..." + fileName) > fileNameLabel.getWidth() - 10) { 358 fileName = fileName.substring(1); 359 } 360 fileName = "..." + fileName; 361 } 362 fileNameLabel.setText(fileName); 363 } // private void setFileNameLabel 364 365 366 /** 367 * Updates the view of the update inverval slider and label according to the specified update frequency. 368 * @param f Update frequency. 369 */ 370 private void setUpdateFrequencyLabels(long f) { 371 int p = (0 >= f ? updateIntervalValues.length - 1 : Arrays.binarySearch(updateIntervalValues, f)); 372 String lab; 373 if (0 > p) 374 lab = "every " + f + " milliseconds."; 375 else 376 lab = updateIntervalLabels[p]; 377 updateIntervallSlider.setValue(p); 378 intervalLabel.setText(lab); 379 } 380 381 /** 382 * Formats the text for the {@code nextUpdateLabel}. 383 * 384 * @param remaining Milliseconds. 385 * @return A formatted string. 386 */ 387 private String formatNextUpdateLabelString(long remaining) { 388 389 if (remaining < 0) { 390 return "Next update: on button click."; 391 } 392 393 long h = remaining / 3600000; 394 long m = (remaining % 3600000) / 60000; 395 long s = ((remaining % 3600000) % 60000) / 1000; 396 long mill = ((remaining % 3600000) % 60000) % 1000; 397 398 StringBuffer t = new StringBuffer("Next update: "); 399 if (h > 0) { 400 t.append(h); 401 t.append(h == 1 ? " hour " : " hours "); 402 } 403 404 if (h > 0 || m > 0) { 405 t.append(m); 406 t.append(m == 1 ? " minute " : " minutes "); 407 } 408 409 t.append(s); 410 t.append("."); 411 412 if (mill < 10) 413 t.append("00"); 414 else if (mill < 100) { 415 t.append("0"); 416 } 417 418 t.append(mill); 419 t.append(" seconds."); 420 421 return t.toString(); 422 } 423 424 /** 425 * Processes events. 426 * 427 * @param event Event to process. 428 */ 429 @Override 430 public void eventRaised(Event<? extends EventType> event) { 431 432 super.eventRaised(event); 433 434 if (event.getDomain() == CacheEvent.class) { 435 processCacheEvent(event.cast(CacheEvent.class)); 436 return; 437 } 438 439 if (event.getDomain() == DataUpdateEvent.class) { 440 processDataUpdateEvent(event.cast(DataUpdateEvent.class)); 441 return; 442 } 443 } 444 445 /** 446 * Updates the view when data file settings change. 447 * 448 * @param event Describes the change event. 449 */ 450 @Override 451 protected void processSettingsEvent(Event<SettingsEvent> event) { 452 453 final DataFileSettings settings = LiveGraph.application().getDataFileSettings(); 454 final boolean loadEvent = (SettingsEvent.DFS_Load == event.getType()); 455 456 if (SettingsEvent.DFS_DataFile == event.getType() || loadEvent) { 457 setFileNameLabel(settings.getDataFile()); 458 } 459 460 if (SettingsEvent.DFS_ShowOnlyTailData == event.getType() || loadEvent) { 461 showAllDataButton.setSelected(!settings.getShowOnlyTailData()); 462 showTailDataButton.setSelected(settings.getShowOnlyTailData()); 463 } 464 465 if (SettingsEvent.DFS_DoNotCacheData == event.getType() || loadEvent) { 466 dontCacheBox.setSelected(settings.getDoNotCacheData()); 467 } 468 469 if (SettingsEvent.DFS_UpdateFrequency == event.getType() || loadEvent) { 470 setUpdateFrequencyLabels(settings.getUpdateFrequency()); 471 } 472 } 473 474 475 /** 476 * Updates data file info when the cache changes. 477 * 478 * @param event The cache event. 479 */ 480 private void processCacheEvent(Event<CacheEvent> event) { 481 482 switch(event.getType()) { 483 case CACHE_UpdatedLabels: 484 case CACHE_ChangedMode: 485 case CACHE_UpdatedData: 486 break; 487 case CACHE_UpdatedDataFileInfo: 488 setDataFileInfoText(((DataCache) event.getProducer()).getDataFileInfo()); 489 break; 490 default: 491 throw new UnexpectedSwitchCase(event.getType()); 492 493 } 494 } 495 496 /** 497 * Updates the panel when an {@code UpdateInvoker} event occured. 498 * 499 * @param event The event. 500 */ 501 private void processDataUpdateEvent(Event<DataUpdateEvent> event) { 502 503 switch(event.getType()) { 504 505 case UPDIN_TimerTick: 506 nextUpdateLabel.setForeground(nextUpdateLabelDefaultColour); 507 long remaining = ((UpdateInvoker) event.getProducer()).getRemainingMillis(); 508 nextUpdateLabel.setText(formatNextUpdateLabelString(remaining)); 509 break; 510 511 case UPDIN_UpdateStart: 512 nextUpdateLabel.setForeground(nextUpdateLabelDefaultColour); 513 nextUpdateLabel.setText("Update in progress."); 514 break; 515 516 case UPDIN_UpdateFinishSuccess: 517 nextUpdateLabel.setForeground(nextUpdateLabelDefaultColour); 518 nextUpdateLabel.setText("Update finished successfully."); 519 break; 520 521 case UPDIN_CannotInitiateUpdate: 522 case UPDIN_UpdateFinishError: 523 nextUpdateLabel.setForeground(Color.RED); 524 String out = ((String) event.getInfoObject()).trim(); 525 nextUpdateLabel.setText(out); 526 break; 527 528 case UPDIN_StartMemoryStreamMode: 529 openButton.setEnabled(false); 530 dontCacheBox.setSelected(LiveGraph.application().getDataFileSettings().getDoNotCacheData()); 531 showAllDataButton.setEnabled(false); 532 showTailDataButton.setEnabled(false); 533 dontCacheBox.setEnabled(false); 534 setFileNameLabel("Data loaded directly from main memory."); 535 break; 536 537 case UPDIN_EndMemoryStreamMode: 538 openButton.setEnabled(true); 539 showAllDataButton.setEnabled(true); 540 showTailDataButton.setEnabled(true); 541 dontCacheBox.setEnabled(true); 542 DataFileSettings dfs = LiveGraph.application().getDataFileSettings(); 543 dontCacheBox.setSelected(dfs.getDoNotCacheData()); 544 setFileNameLabel(dfs.getDataFile()); 545 break; 546 547 default: 548 break; 549 } 550 } 551 552 } // public class DataFileSettingsPanel