]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/GpsPrune.java
Version 20.4, May 2021
[GpsPrune.git] / src / tim / prune / GpsPrune.java
1 package tim.prune;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.Image;
6 import java.awt.event.WindowAdapter;
7 import java.awt.event.WindowEvent;
8 import java.io.File;
9 import java.io.FileNotFoundException;
10 import java.util.ArrayList;
11 import java.util.Locale;
12
13 import javax.swing.JFrame;
14 import javax.swing.JSplitPane;
15 import javax.swing.JToolBar;
16 import javax.swing.UIManager;
17 import javax.swing.WindowConstants;
18
19 import tim.prune.config.Config;
20 import tim.prune.config.ConfigException;
21 import tim.prune.gui.DetailsDisplay;
22 import tim.prune.gui.IconManager;
23 import tim.prune.gui.MenuManager;
24 import tim.prune.gui.SelectorDisplay;
25 import tim.prune.gui.SidebarController;
26 import tim.prune.gui.StatusBar;
27 import tim.prune.gui.Viewport;
28 import tim.prune.gui.map.MapCanvas;
29 import tim.prune.gui.profile.ProfileChart;
30
31 /**
32  * GpsPrune is a tool to visualize, edit, convert and prune GPS data
33  * Please see the included readme.txt or https://activityworkshop.net
34  * This software is copyright activityworkshop.net 2006-2020 and made available through the Gnu GPL version 2.
35  * For license details please see the included license.txt.
36  * GpsPrune is the main entry point to the application, including initialisation and launch
37  */
38 public class GpsPrune
39 {
40         /** Version number of application, used in about screen and for version check */
41         public static final String VERSION_NUMBER = "20";
42         /** Build number, just used for about screen */
43         public static final String BUILD_NUMBER = "378";
44         /** Static reference to App object */
45         private static App APP = null;
46
47         /** Program name, used for Frame title and for Macs also on the system bar */
48         private static final String PROGRAM_NAME = "GpsPrune";
49
50
51         /**
52          * Main method
53          * @param args command line arguments
54          */
55         public static void main(String[] args)
56         {
57                 Locale locale = null;
58                 String localeCode = null;
59                 String langFilename = null;
60                 String configFilename = null;
61                 ArrayList<File> dataFiles = new ArrayList<File>();
62                 boolean showUsage = false;
63
64                 // Mac OSX - specific properties (Mac insists that this is done as soon as possible)
65                 if (System.getProperty("mrj.version") != null) {
66                         System.setProperty("apple.laf.useScreenMenuBar", "true"); // menu at top of screen
67                         System.setProperty("com.apple.mrj.application.apple.menu.about.name", PROGRAM_NAME);
68                 }
69                 // Loop over given arguments, if any
70                 for (int i=0; i<args.length; i++)
71                 {
72                         String arg = args[i];
73                         if (arg.startsWith("--lang="))
74                         {
75                                 localeCode = arg.substring(7);
76                                 locale = getLanguage(localeCode);
77                         }
78                         else if (arg.startsWith("--langfile="))
79                         {
80                                 langFilename = arg.substring(11);
81                         }
82                         else if (arg.startsWith("--configfile="))
83                         {
84                                 configFilename = arg.substring(13);
85                         }
86                         else if (arg.startsWith("--help")) {
87                                 showUsage = true;
88                         }
89                         else
90                         {
91                                 // Check if a data file has been given
92                                 File f = new File(arg);
93                                 if (f.exists() && f.isFile() && f.canRead()) {
94                                         dataFiles.add(f);
95                                 }
96                                 else
97                                 {
98                                         // Make a useful String from the unknown parameter - could it be a mistyped filename?
99                                         System.out.println(makeUnknownParameterString(arg));
100                                         showUsage = true;
101                                 }
102                         }
103                 }
104                 if (showUsage)
105                 {
106                         System.out.println("GpsPrune - a tool for editing GPS data.\nPossible parameters:"
107                                 + "\n   --configfile=<file> used to specify a configuration file"
108                                 + "\n   --lang=<code>       used to specify language code such as DE"
109                                 + "\n   --langfile=<file>   used to specify an alternative language file\n");
110                 }
111                 // Initialise configuration if selected
112                 try
113                 {
114                         if (configFilename != null) {
115                                 Config.loadFile(new File(configFilename));
116                         }
117                         else {
118                                 Config.loadDefaultFile();
119                         }
120                 }
121                 catch (ConfigException ce) {
122                         System.err.println("Failed to load config file: " + configFilename);
123                 }
124                 boolean overrideLang = (locale != null);
125                 if (overrideLang) {
126                         // Make sure Config holds chosen language
127                         Config.setConfigString(Config.KEY_LANGUAGE_CODE, localeCode);
128                 }
129                 else {
130                         // Set locale according to Config's language property
131                         String configLang = Config.getConfigString(Config.KEY_LANGUAGE_CODE);
132                         if (configLang != null) {
133                                 Locale configLocale = getLanguage(configLang);
134                                 if (configLocale != null) {locale = configLocale;}
135                         }
136                 }
137                 I18nManager.init(locale);
138                 // Load the external language file, either from config file or from command line params
139                 if (langFilename == null && !overrideLang) {
140                         // If langfilename is blank on command line parameters then don't use setting from config
141                         langFilename = Config.getConfigString(Config.KEY_LANGUAGE_FILE);
142                 }
143                 if (langFilename != null)
144                 {
145                         try {
146                                 I18nManager.addLanguageFile(langFilename);
147                                 Config.setConfigString(Config.KEY_LANGUAGE_FILE, langFilename);
148                         }
149                         catch (FileNotFoundException fnfe) {
150                                 System.err.println("Failed to load language file: " + langFilename);
151                                 Config.setConfigString(Config.KEY_LANGUAGE_FILE, "");
152                         }
153                 }
154
155                 // Set look-and-feel
156                 try {
157                         String windowStyle = Config.getConfigString(Config.KEY_WINDOW_STYLE);
158                         UIManager.setLookAndFeel(windowStyle);
159                 }
160                 catch (Exception e) {}
161
162                 // Set up the window and go
163                 launch(dataFiles);
164         }
165
166
167         /**
168          * Choose a locale based on the given code
169          * @param inString code for locale
170          * @return Locale object if available, otherwise null
171          */
172         private static Locale getLanguage(String inString)
173         {
174                 if (inString.length() == 2)
175                 {
176                         return new Locale(inString);
177                 }
178                 else if (inString.length() == 5 && inString.charAt(2) == '_')
179                 {
180                         return new Locale(inString.substring(0, 2), inString.substring(3));
181                 }
182                 System.out.println("Unrecognised locale '" + inString
183                         + "' - value should be eg 'DE' or 'DE_ch'");
184                 return null;
185         }
186
187
188         /**
189          * Launch the main application
190          * @param inDataFiles list of data files to load on startup
191          */
192         private static void launch(ArrayList<File> inDataFiles)
193         {
194                 // Initialise Frame
195                 JFrame frame = new JFrame(PROGRAM_NAME);
196                 APP = new App(frame);
197
198                 // make menu
199                 MenuManager menuManager = new MenuManager(APP, APP.getTrackInfo());
200                 frame.setJMenuBar(menuManager.createMenuBar());
201                 APP.setMenuManager(menuManager);
202                 UpdateMessageBroker.addSubscriber(menuManager);
203                 // Make toolbar for buttons
204                 JToolBar toolbar = menuManager.createToolBar();
205
206                 // Make main GUI components and add as listeners
207                 SelectorDisplay leftPanel = new SelectorDisplay(APP.getTrackInfo());
208                 UpdateMessageBroker.addSubscriber(leftPanel);
209                 DetailsDisplay rightPanel = new DetailsDisplay(APP.getTrackInfo());
210                 UpdateMessageBroker.addSubscriber(rightPanel);
211                 MapCanvas mapDisp = new MapCanvas(APP, APP.getTrackInfo());
212                 UpdateMessageBroker.addSubscriber(mapDisp);
213                 Viewport viewport = new Viewport(mapDisp);
214                 APP.setViewport(viewport);
215                 ProfileChart profileDisp = new ProfileChart(APP.getTrackInfo());
216                 UpdateMessageBroker.addSubscriber(profileDisp);
217                 StatusBar statusBar = new StatusBar();
218                 UpdateMessageBroker.addSubscriber(statusBar);
219                 UpdateMessageBroker.informSubscribers("GpsPrune v" + VERSION_NUMBER);
220
221                 // Arrange in the frame using split panes
222                 JSplitPane midSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mapDisp, profileDisp);
223                 midSplit.setResizeWeight(1.0); // allocate as much space as poss to map
224                 JSplitPane rightSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, midSplit, rightPanel);
225                 rightSplit.setResizeWeight(1.0); // allocate as much space as poss to map
226
227                 frame.getContentPane().setLayout(new BorderLayout());
228                 frame.getContentPane().add(toolbar, BorderLayout.NORTH);
229                 JSplitPane leftSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightSplit);
230                 frame.getContentPane().add(leftSplit, BorderLayout.CENTER);
231                 frame.getContentPane().add(statusBar, BorderLayout.SOUTH);
232
233                 // add closing listener
234                 frame.addWindowListener(new WindowAdapter() {
235                         public void windowClosing(WindowEvent e) {
236                                 APP.exit();
237                         }
238                 });
239                 // Avoid automatically shutting down if window closed
240                 frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
241
242                 // set window icons of different resolutions (1.6+)
243                 try
244                 {
245                         ArrayList<Image> icons = new ArrayList<Image>();
246                         String[] resolutions = {"_16", "_20", "_22", "_24", "_32", "_36", "_48", "_64", "_72", "_96", "_128"};
247                         for (String r : resolutions) {
248                                 icons.add(IconManager.getImageIcon(IconManager.WINDOW_ICON + r + ".png").getImage());
249                         }
250                         Class<?> d = java.awt.Window.class;
251                         // This is the same as frame.setIconImages(icons) but is compilable also for java1.5 where this isn't available
252                         d.getDeclaredMethod("setIconImages", new Class[]{java.util.List.class}).invoke(frame, icons);
253                 }
254                 catch (Exception e)
255                 {
256                         // setting a list of icon images didn't work, so try with just one image instead
257                         try {
258                                 frame.setIconImage(IconManager.getImageIcon(IconManager.WINDOW_ICON + "_16.png").getImage());
259                         }
260                         catch (Exception e2) {}
261                 }
262
263                 // Set up drag-and-drop handler to accept dropped files
264                 frame.setTransferHandler(new FileDropHandler(APP));
265
266                 // finish off and display frame
267                 frame.pack();
268                 if (!setFrameBoundsFromConfig(frame))
269                 {
270                         frame.setSize(650, 450);
271                 }
272                 frame.setVisible(true);
273                 // Set position of map/profile splitter
274                 midSplit.setDividerLocation(0.75);
275                 // Update menu (only needed for recent file list)
276                 UpdateMessageBroker.informSubscribers();
277
278                 // Make a full screen toggler
279                 SidebarController fsc = new SidebarController(new Component[] {leftPanel, profileDisp, rightPanel},
280                         new JSplitPane[] {leftSplit, midSplit, rightSplit});
281                 APP.setSidebarController(fsc);
282                 // Finally, give the files to load to the App
283                 APP.loadDataFiles(inDataFiles);
284         }
285
286
287         /**
288          * Set the frame bounds using the saved config setting
289          * @param inFrame frame to set the bounds of
290          * @return true on success
291          */
292         private static boolean setFrameBoundsFromConfig(JFrame inFrame)
293         {
294                 // Try to get bounds from config
295                 String bounds = Config.getConfigString(Config.KEY_WINDOW_BOUNDS);
296                 try
297                 {
298                         String[] boundValues = bounds.split("x");
299                         if (boundValues.length == 4)
300                         {
301                                 int[] elems = new int[4];
302                                 for (int i=0; i<4; i++) {
303                                         elems[i] = Integer.parseInt(boundValues[i]);
304                                 }
305                                 // Make sure width and height aren't stupid
306                                 elems[2] = Math.max(elems[2], 400);
307                                 elems[3] = Math.max(elems[3], 300);
308                                 inFrame.setBounds(elems[0], elems[1], elems[2], elems[3]);
309                                 return true;
310                         }
311                 }
312                 catch (NullPointerException npe) {}  // if no entry found in config
313                 catch (NumberFormatException nfe) {} // if string couldn't be parsed
314                 return false;
315         }
316
317
318         /**
319          * Try to guess whether it's a mistyped parameter or a mistyped filename
320          * @param inParam command line argument
321          * @return error message
322          */
323         private static String makeUnknownParameterString(String inParam)
324         {
325                 File file = new File(inParam);
326                 if (file.exists())
327                 {
328                         if (file.isDirectory()) return "'" + inParam + "' is a directory";
329                         if (!file.canRead())    return "Cannot read file '" + inParam + "'";
330                         return "Something wrong with file '" + inParam + "'";
331                 }
332                 do
333                 {
334                         String name = file.getName();
335                         file = file.getParentFile();
336                         if (file != null && file.exists() && file.canRead()) return "Tried to load file '" + inParam + "' but cannot find '" + name + "'";
337                 }
338                 while (file != null);
339
340                 return "Unknown parameter '" + inParam + "'";
341         }
342 }