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