View Javadoc

1   /* OpenLogViewer
2    *
3    * Copyright 2011
4    *
5    * This file is part of the OpenLogViewer project.
6    *
7    * OpenLogViewer software is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation, either version 3 of the License, or
10   * (at your option) any later version.
11   *
12   * OpenLogViewer software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with any OpenLogViewer software.  If not, see http://www.gnu.org/licenses/
19   *
20   * I ask that if you make any changes to this file you fork the code on github.com!
21   *
22   */
23  
24  /*
25   * OpenLogViewerApp.java
26   *
27   * Created on Jan 26, 2011, 2:55:31 PM
28   */
29  package org.diyefi.openlogviewer;
30  
31  import java.awt.BorderLayout;
32  import java.awt.Dimension;
33  import java.awt.GraphicsDevice;
34  import java.awt.GraphicsEnvironment;
35  import java.awt.Toolkit;
36  import java.awt.event.ActionEvent;
37  import java.awt.event.ActionListener;
38  import java.awt.event.WindowEvent;
39  import java.io.File;
40  import java.io.IOException;
41  import java.io.FileInputStream;
42  import java.io.FileOutputStream;
43  import java.util.List;
44  import java.util.ArrayList;
45  import java.util.Locale;
46  import java.util.Properties;
47  import java.util.ResourceBundle;
48  
49  import javax.swing.JFileChooser;
50  import javax.swing.JFrame;
51  import javax.swing.JMenu;
52  import javax.swing.JMenuBar;
53  import javax.swing.JMenuItem;
54  import javax.swing.JPanel;
55  import javax.swing.UIManager;
56  import javax.swing.UnsupportedLookAndFeelException;
57  import javax.swing.WindowConstants;
58  import javax.swing.filechooser.FileFilter;
59  
60  import org.diyefi.openlogviewer.decoder.AbstractDecoder;
61  import org.diyefi.openlogviewer.decoder.CSVTypeLog;
62  import org.diyefi.openlogviewer.decoder.FreeEMSBin;
63  import org.diyefi.openlogviewer.filefilters.CSVFileFilter;
64  import org.diyefi.openlogviewer.filefilters.FreeEMSBinFileFilter;
65  import org.diyefi.openlogviewer.filefilters.FreeEMSLAFileFilter;
66  import org.diyefi.openlogviewer.filefilters.LogFileFilter;
67  import org.diyefi.openlogviewer.filefilters.MSTypeFileFilter;
68  import org.diyefi.openlogviewer.filefilters.FreeEMSFileFilter;
69  import org.diyefi.openlogviewer.genericlog.GenericLog;
70  import org.diyefi.openlogviewer.graphing.EntireGraphingPanel;
71  import org.diyefi.openlogviewer.graphing.MultiGraphLayeredPane;
72  import org.diyefi.openlogviewer.optionpanel.OptionFrameV2;
73  import org.diyefi.openlogviewer.propertypanel.PropertiesPane;
74  import org.diyefi.openlogviewer.propertypanel.SingleProperty;
75  import org.diyefi.openlogviewer.utils.Utilities;
76  
77  public final class OpenLogViewer extends JFrame {
78  	public static final String NEWLINE = System.getProperty("line.separator");
79  
80  	private static final long serialVersionUID = 1L;
81  
82  	private static final String APPLICATION_NAME = OpenLogViewer.class.getSimpleName();
83  	private static final String SETTINGS_DIRECTORY = "." + APPLICATION_NAME;
84  
85  	private static final String FILE_MENU_KEY = "FileMenuName";
86  	private static final String FILE_MENU_ITEM_OPEN_KEY = "FileMenuItemOpenName";
87  	private static final String FILE_MENU_ITEM_QUIT_KEY = "FileMenuItemQuitName";
88  
89  	private static final String VIEW_MENU_KEY = "ViewMenuName";
90  	private static final String VIEW_MENU_ITEM_FULL_SCREEN_KEY = "ViewMenuItemFullScreenName";
91  	private static final String VIEW_MENU_ITEM_SCALE_AND_COLOR_KEY = "ViewMenuItemScaleAndColorName";
92  	private static final String VIEW_MENU_ITEM_FIELDS_AND_DIVISIONS_KEY = "ViewMenuItemFieldsAndDivisionsName";
93  
94  	private static final String FAILED_TO_GO_FULLSCREEN_MESSAGE_KEY = "FailedToGoFullScreenMessage";
95  	private static final String CANT_GO_FULLSCREEN_MESSAGE_KEY = "CantGoFullScreenMessage";
96  
97  	// TODO localise and refactor these:
98  	private static final String PROPERTIES_FILENAME = "OLVAllProperties.olv";
99  
100 	private static final String NAME_OF_LAST_FILE_KEY = "lastFingFile";
101 	private static final String NAME_OF_LAST_DIR_KEY = "lastFingDir";
102 	private static final String NAME_OF_LAST_CHOOSER_CLASS = "chooserClass";
103 
104 	// Real vars start here, many will probably get ripped out later
105 	private static OpenLogViewer mainAppRef;
106 	private static ResourceBundle labels;
107 
108 	private JPanel mainPanel;
109 	private EntireGraphingPanel graphingPanel;
110 	private PlayBarPanel playBar;
111 	private OptionFrameV2 optionFrame;
112 	private PropertiesPane prefFrame;
113 
114 	private List<SingleProperty> properties;
115 	private AbstractDecoder decoderInUse;
116 	private JMenuBar menuBar;
117 	private boolean fullscreen;
118 
119 	public OpenLogViewer() {
120 		prefFrame = new PropertiesPane(labels.getString(VIEW_MENU_ITEM_SCALE_AND_COLOR_KEY));
121 		properties = new ArrayList<SingleProperty>();
122 		prefFrame.setProperties(properties);
123 
124 		playBar = new PlayBarPanel();
125 		optionFrame = new OptionFrameV2();
126 		graphingPanel = new EntireGraphingPanel();
127 		graphingPanel.setPreferredSize(new Dimension(600, 420));
128 
129 		this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
130 		this.setTitle(APPLICATION_NAME);
131 		this.setLayout(new BorderLayout());
132 		this.setFocusable(true);
133 
134 		mainPanel = new JPanel();
135 		mainPanel.setName("mainPanel");
136 		mainPanel.setLayout(new BorderLayout());
137 		mainPanel.add(graphingPanel, BorderLayout.CENTER);
138 		mainPanel.add(playBar, BorderLayout.SOUTH);
139 		this.add(mainPanel, BorderLayout.CENTER);
140 
141 		final JMenuItem openFileMenuItem = new JMenuItem(labels.getString(FILE_MENU_ITEM_OPEN_KEY));
142 		openFileMenuItem.setName(FILE_MENU_ITEM_OPEN_KEY);
143 		openFileMenuItem.addActionListener(new ActionListener() {
144 			@Override
145 			public void actionPerformed(final ActionEvent e) {
146 				openFile();
147 			}
148 		});
149 
150 		final JMenuItem quitFileMenuItem = new JMenuItem(labels.getString(FILE_MENU_ITEM_QUIT_KEY));
151 		quitFileMenuItem.setName(FILE_MENU_ITEM_QUIT_KEY);
152 		quitFileMenuItem.addActionListener(new ActionListener() {
153 			@Override
154 			public void actionPerformed(final ActionEvent e) {
155 				WindowEvent wev = new WindowEvent(OpenLogViewer.getInstance(), WindowEvent.WINDOW_CLOSING);
156 				Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev);
157 			}
158 		});
159 
160 		final JMenuItem fullScreenViewMenuItem = new JMenuItem(labels.getString(VIEW_MENU_ITEM_FULL_SCREEN_KEY));
161 		fullScreenViewMenuItem.setName(VIEW_MENU_ITEM_FULL_SCREEN_KEY);
162 		fullScreenViewMenuItem.addActionListener(new ActionListener() {
163 			@Override
164 			public void actionPerformed(final ActionEvent e) {
165 				enterFullScreen();
166 			}
167 		});
168 
169 		final JMenuItem scaleAndColorViewMenuItem = new JMenuItem(labels.getString(VIEW_MENU_ITEM_SCALE_AND_COLOR_KEY));
170 		scaleAndColorViewMenuItem.setName(VIEW_MENU_ITEM_SCALE_AND_COLOR_KEY);
171 		scaleAndColorViewMenuItem.addActionListener(new ActionListener() {
172 			@Override
173 			public void actionPerformed(final ActionEvent e) {
174 				prefFrame.setVisible(true);
175 				prefFrame.setAlwaysOnTop(true);
176 				prefFrame.setAlwaysOnTop(false);
177 			}
178 		});
179 
180 		final JMenuItem fieldsAndDivisionsViewMenuItem = new JMenuItem(labels.getString(VIEW_MENU_ITEM_FIELDS_AND_DIVISIONS_KEY));
181 		fieldsAndDivisionsViewMenuItem.setName(VIEW_MENU_ITEM_FIELDS_AND_DIVISIONS_KEY);
182 		fieldsAndDivisionsViewMenuItem.addActionListener(new ActionListener() {
183 			@Override
184 			public void actionPerformed(final ActionEvent e) {
185 				optionFrame.setVisible(true);
186 				optionFrame.setAlwaysOnTop(true);
187 				optionFrame.setAlwaysOnTop(false);
188 			}
189 		});
190 
191 		/* 1 November 2011 Migrated Gufi's menu from the pointless class it's in to here.
192 		 * 22 October 2011 Left Gufi's menu in place for future dev's enjoyment.
193 		 *
194 		 * 5 February 2011 meal for the night DO NOT EDIT MENU!
195 		 * Sesame chicken alacarte
196 		 * chicken lo mein alacarte
197 		 * orange chicken x2
198 		 */
199 
200 		final JMenu fileMenu = new JMenu(labels.getString(FILE_MENU_KEY));
201 		fileMenu.setName(FILE_MENU_KEY);
202 		fileMenu.add(openFileMenuItem);
203 		fileMenu.add(quitFileMenuItem);
204 
205 		final JMenu viewMenu = new JMenu(labels.getString(VIEW_MENU_KEY));
206 		viewMenu.setName(VIEW_MENU_KEY);
207 		viewMenu.add(fullScreenViewMenuItem);
208 		viewMenu.add(scaleAndColorViewMenuItem);
209 		viewMenu.add(fieldsAndDivisionsViewMenuItem);
210 
211 		menuBar = new JMenuBar();
212 		menuBar.setName("menuBar");
213 		menuBar.add(fileMenu);
214 		menuBar.add(viewMenu);
215 		setJMenuBar(menuBar);
216 
217 		this.addKeyListener(graphingPanel);
218 
219 		pack();
220 
221 		this.setVisible(true);
222 	}
223 
224 	/**
225 	 * The entry point of OLV!
226 	 *
227 	 * @param args the command line arguments
228 	 */
229 	public static void main(final String[] args) {
230 		java.awt.EventQueue.invokeLater(new Runnable() {
231 
232 			@Override
233 			public void run() {
234 				Locale currentLocale = Locale.getDefault();
235 				labels = ResourceBundle.getBundle(this.getClass().getPackage().getName() + ".Labels", currentLocale);
236 
237 				final String lookAndFeel;
238 				final String systemLookAndFeel = UIManager.getSystemLookAndFeelClassName();
239 				if ("com.apple.laf.AquaLookAndFeel".equals(systemLookAndFeel)) { // If Mac!
240 					System.setProperty("apple.laf.useScreenMenuBar", "true");
241 					lookAndFeel = systemLookAndFeel;
242 				} else {
243 					lookAndFeel = "javax.swing.plaf.metal.MetalLookAndFeel";
244 				}
245 
246 				try {
247 					UIManager.setLookAndFeel(lookAndFeel);
248 				} catch (UnsupportedLookAndFeelException e) {
249 					e.printStackTrace();
250 					System.out.println(labels.getString("LookAndFeelExceptionMessageOne"));
251 				} catch (ClassNotFoundException e) {
252 					e.printStackTrace();
253 					System.out.println(labels.getString("LookAndFeelExceptionMessageTwo"));
254 				} catch (InstantiationException e) {
255 					e.printStackTrace();
256 					System.out.println(labels.getString("LookAndFeelExceptionMessageThree"));
257 				} catch (IllegalAccessException e) {
258 					e.printStackTrace();
259 					System.out.println(labels.getString("LookAndFeelExceptionMessageFour"));
260 				}
261 
262 				mainAppRef = new OpenLogViewer();
263 			}
264 		});
265 	}
266 
267 	public void openFile() {
268 		final JFileChooser fileChooser = new JFileChooser();
269 		final String lastFingFile = getApplicationWideProperty(NAME_OF_LAST_FILE_KEY);
270 
271 		if (lastFingFile != null) {
272 			fileChooser.setSelectedFile(new File(lastFingFile));
273 		} else {
274 			final String lastFingDir = getApplicationWideProperty(NAME_OF_LAST_DIR_KEY);
275 			if (lastFingDir != null) {
276 				fileChooser.setCurrentDirectory(new File(lastFingDir));
277 			}
278 		}
279 
280 		fileChooser.addChoosableFileFilter(new FreeEMSFileFilter());
281 		fileChooser.addChoosableFileFilter(new FreeEMSBinFileFilter());
282 		fileChooser.addChoosableFileFilter(new FreeEMSLAFileFilter());
283 		fileChooser.addChoosableFileFilter(new CSVFileFilter());
284 		fileChooser.addChoosableFileFilter(new LogFileFilter());
285 		fileChooser.addChoosableFileFilter(new MSTypeFileFilter());
286 
287 		final String chooserClass = getApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS);
288 
289 		if (chooserClass != null) {
290 			try {
291 				final FileFilter[] existingFilters = fileChooser.getChoosableFileFilters();
292 				boolean alreadyHasSavedFilter = false;
293 				for (int i = 0; i < existingFilters.length; i++) {
294 					final String thisFilter = existingFilters[i].getClass().getCanonicalName();
295 					if (thisFilter.equals(chooserClass)) {
296 						alreadyHasSavedFilter = true;
297 						fileChooser.setFileFilter(existingFilters[i]); // If set to a new instance the list will contain two!
298 					}
299 				}
300 
301 				// If it's not one of ours, create a new one and set it, though that almost certainly means we'll throw an exception and clean up the prefs...
302 				if (!alreadyHasSavedFilter) {
303 					final FileFilter savedFilter = (FileFilter) Class.forName(chooserClass).newInstance();
304 					fileChooser.setFileFilter(savedFilter);
305 				}
306 			} catch (ClassNotFoundException c) {
307 				removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS);
308 				System.out.println("Class not found! chooserClass removed from props!");
309 			} catch (InstantiationException i) {
310 				removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS);
311 				System.out.println("Could not instantiate class! chooserClass removed from props!");
312 			} catch (IllegalAccessException l) {
313 				removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS);
314 				System.out.println("Could not access class! chooserClass removed from props!");
315 			}
316 		}
317 
318 		final int acceptValue = fileChooser.showOpenDialog(OpenLogViewer.getInstance());
319 		if (acceptValue == JFileChooser.APPROVE_OPTION) {
320 			if (decoderInUse != null) {
321 				// Clear out all references to data that we don't need and thereby ensure that we have lots of memory free for data we're about to gather!
322 				final GenericLog logInUse = decoderInUse.getDecodedLog();
323 				if (logInUse != null) {
324 					logInUse.clearOut(); // This is the wrong approach. The correct approach is to reuse the object, try that next...
325 				}
326 				decoderInUse = null;
327 				setLog(null);
328 			} // else haven't read in a log yet.
329 
330 			final File openFile = fileChooser.getSelectedFile();
331 			if ("bin".equals(Utilities.getExtension(openFile)) || "la".equals(Utilities.getExtension(openFile)) || (fileChooser.getFileFilter() instanceof FreeEMSFileFilter)) {
332 				decoderInUse = new FreeEMSBin(openFile);
333 			} else {
334 				decoderInUse = new CSVTypeLog(openFile);
335 			}
336 
337 			if (openFile != null) {
338 				OpenLogViewer.getInstance().setTitle(APPLICATION_NAME + " - " + openFile.getName());
339 				saveApplicationWideProperty(NAME_OF_LAST_DIR_KEY, openFile.getParent());
340 				saveApplicationWideProperty(NAME_OF_LAST_FILE_KEY, openFile.getPath());
341 				saveApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS, fileChooser.getFileFilter().getClass().getCanonicalName());
342 			}
343 		}
344 	}
345 
346 	private String getApplicationWideProperty(final String key) {
347 		try {
348 			final Properties AppWide = new Properties();
349 			final File AppWideFile = openAppWideProps(AppWide);
350 			if (AppWideFile != null) {
351 				return AppWide.getProperty(key);
352 			} else {
353 				throw new IllegalArgumentException("Problem getting property, got null instead of file!");
354 			}
355 		} catch (IOException e) {
356 			e.printStackTrace();
357 			throw new RuntimeException("IO issue: " + e.getMessage(), e);
358 		}
359 	}
360 
361 	private void saveApplicationWideProperty(final String key, final String value) {
362 		try {
363 			final Properties AppWide = new Properties();
364 			final File AppWideFile = openAppWideProps(AppWide);
365 			if (AppWideFile != null) {
366 				AppWide.setProperty(key, value);
367 				AppWide.store(new FileOutputStream(AppWideFile), "saved");
368 			} else {
369 				throw new IllegalArgumentException("Problem saving property, got null instead of file!");
370 			}
371 		} catch (IOException e) {
372 			e.printStackTrace();
373 			throw new RuntimeException("Another IO issue: " + e.getMessage(), e);
374 		}
375 	}
376 
377 	private void removeApplicationWideProperty(final String key) {
378 		try {
379 			final Properties AppWide = new Properties();
380 			final File AppWideFile = openAppWideProps(AppWide);
381 			if (AppWideFile != null) {
382 				AppWide.remove(key);
383 				AppWide.store(new FileOutputStream(AppWideFile), "removed");
384 			} else {
385 				throw new IllegalArgumentException("Problem removing property, got null instead of file!");
386 			}
387 		} catch (IOException e) {
388 			e.printStackTrace();
389 			throw new RuntimeException("YAIO issue: " + e.getMessage(), e);
390 		}
391 	}
392 
393 	private File openAppWideProps(final Properties AppWide) throws IOException {
394 		File AppWideFile;
395 		AppWideFile = new File(System.getProperty("user.home"));
396 
397 		if (!AppWideFile.exists() || !AppWideFile.canRead() || !AppWideFile.canWrite()) {
398 			System.out.println("Either you dont have a home director, or it isnt read/writeable... fix it!");
399 		} else {
400 			AppWideFile = new File(AppWideFile, SETTINGS_DIRECTORY);
401 		}
402 
403 		if (!AppWideFile.exists()) {
404 			try {
405 				if (AppWideFile.mkdir()) {
406 					AppWideFile = new File(AppWideFile, PROPERTIES_FILENAME);
407 					if (AppWideFile.createNewFile()) {
408 						AppWide.load(new FileInputStream(AppWideFile));
409 					}
410 				} else {
411 					throw new RuntimeException("Failed to create directory, no code to handle this at this time.");
412 					// This should be passed up to the GUI as a dialog that tells you it can't do what it has to be able to...
413 				}
414 			} catch (IOException IOE) {
415 				System.out.print(IOE.getMessage());
416 			}
417 		} else {
418 			AppWideFile = new File(AppWideFile, PROPERTIES_FILENAME);
419 			if (!AppWideFile.createNewFile()) {
420 				AppWide.load(new FileInputStream(AppWideFile));
421 			}
422 		}
423 		return AppWideFile;
424 	}
425 
426 	public void enterFullScreen() {
427 		if (!fullscreen ) {
428 			final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
429 			final GraphicsDevice[] gs = ge.getScreenDevices();
430 			final GraphicsDevice gd = gs[0];
431 
432 			if (gd.isFullScreenSupported()) {
433 				try {
434 				setJMenuBar(null);    // remove the menu bar for maximum space, load files in non fullscreen mode! :-p
435 				removeNotify();       // without this we can't do the next thing
436 				setUndecorated(true); // remove the window frame!
437 				addNotify();          // turn things back on again!
438 				setResizable(false);  // doesn't make sense and could be dangerous, according to oracle.
439 				gd.setFullScreenWindow(this);
440 				validate();           // required after rearranging component hierarchy
441 				fullscreen = true;    // remember so that we don't do random things when escape is pushed at other times...
442 				} catch(Exception e) {
443 					e.printStackTrace();
444 					System.out.println(labels.getObject(FAILED_TO_GO_FULLSCREEN_MESSAGE_KEY));
445 				}
446 			} else {
447 				System.out.println(labels.getObject(CANT_GO_FULLSCREEN_MESSAGE_KEY));
448 			}
449 		}
450 	}
451 
452 	public void exitFullScreen() {
453 		if (fullscreen) {
454 			GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
455 			GraphicsDevice[] gs = ge.getScreenDevices();
456 			// Do the reverse of what we did to put it into full screen!
457 			gs[0].setFullScreenWindow(null);
458 			removeNotify();
459 			setUndecorated(false);
460 			addNotify();
461 			setJMenuBar(menuBar);
462 			setResizable(true);
463 			pack();
464 			fullscreen = false;
465 		}
466 	}
467 
468 	public void toggleFullScreen(){
469 		if(fullscreen){
470 			exitFullScreen();
471 		} else {
472 			enterFullScreen();
473 		}
474 	}
475 
476 	public void setLog(final GenericLog genericLog) {
477 		graphingPanel.setLog(genericLog);
478 	}
479 
480 	/**
481 	 * Returns the reference to this instance, it is meant to be a method to make getting the main frame simpler
482 	 * @return <code>this</code> instance
483 	 */
484 	public static OpenLogViewer getInstance() {
485 		return mainAppRef;
486 	}
487 
488 	public EntireGraphingPanel getEntireGraphingPanel() {
489 		return graphingPanel;
490 	}
491 
492 	public MultiGraphLayeredPane getMultiGraphLayeredPane() {
493 		return graphingPanel.getMultiGraphLayeredPane();
494 	}
495 
496 	public OptionFrameV2 getOptionFrame() {
497 		return optionFrame;
498 	}
499 
500 	public PropertiesPane getPropertyPane() {
501 		return prefFrame;
502 	}
503 
504 	public List<SingleProperty> getProperties() {
505 		return properties;
506 	}
507 }