View Javadoc

1   /*
2    * Trip Tracker, a real-time position tracking system for the Internet.
3    * Copyright (C) 2006  Team Trip Tracker
4    *
5    * This program is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License as published by the
7    * Free Software Foundation; either version 2 of the License, or (at your
8    * option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful, but
11   * WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13   * General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License along
16   * with this program; if not, write to the Free Software Foundation, Inc.,
17   * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18   */
19  
20  package triptracker.client.gps.ui;
21  
22  import java.awt.Dimension;
23  import java.awt.GridLayout;
24  import java.awt.event.ActionListener;
25  import java.awt.event.KeyEvent;
26  import java.io.IOException;
27  
28  import javax.swing.AbstractButton;
29  import javax.swing.BorderFactory;
30  import javax.swing.ImageIcon;
31  import javax.swing.JButton;
32  import javax.swing.JComponent;
33  import javax.swing.JLabel;
34  import javax.swing.JOptionPane;
35  import javax.swing.JPanel;
36  import javax.swing.JSpinner;
37  import javax.swing.JTextField;
38  import javax.swing.SpinnerListModel;
39  import javax.swing.Timer;
40  import javax.swing.event.ChangeListener;
41  
42  import triptracker.client.gps.core.GPSClientModel;
43  import triptracker.client.gps.core.GPSListener;
44  import triptracker.client.gps.core.GPSStatusState;
45  import triptracker.client.net.GPSSocketAdapter;
46  import triptracker.client.ui.Form;
47  import se.datadosen.component.RiverLayout;
48  import triptracker.core.ConnectionState;
49  import triptracker.core.Coordinate;
50  import triptracker.core.Utils;
51  import static triptracker.core.Utils.*;
52  
53  public class MainForm extends GPSSocketAdapter implements Form, GPSListener {
54  	protected final String name;
55  	protected static int nameCount = 1;
56  
57  	private final GPSGui view;
58  	private final GPSClientModel model;
59  	
60  	private int numRecieved = 1;
61  	private Timer timer;
62  	private double distance;
63  
64  	private final JPanel mainPanel, centerPanel, centerBorderPanel,
65  			buttonPanel, routePanel, routeBorderPanel;
66  	protected final JButton gpsButton, serverButton;
67  	private JTextField numCoordsField, coordLatField, coordLonField,
68  			updateField, distField;
69  	protected JSpinner intervalSpinner;
70  	private JLabel activeRouteLabel;
71  	
72  	private ImageIcon imgGPSOff = getImageIcon("gps_notRecieving_icon.gif");
73  	private ImageIcon imgGPSOn = getImageIcon("gps_recieving_icon.gif");
74  	private ImageIcon imgServer = getImageIcon("serverIcon.gif");
75  	private ImageIcon imgBuffer = getImageIcon("bufferIcon.gif");
76  	
77  	public MainForm(GPSGui view, GPSClientModel model) {
78  		this(view, model, MainForm.class.getClass().getName() + "-"
79  				+ nameCount++);
80  	}
81  
82  	public MainForm(GPSGui view, GPSClientModel model, String name) {
83  		this.view = view;
84  		this.model = model;
85  		this.name = name;
86  		
87  		numCoordsField = new JTextField("0 Coordinates", 10);
88  		numCoordsField.setEditable(false);
89  		coordLatField = new JTextField("None", 11);
90  		coordLatField.setEditable(false);
91  		coordLonField = new JTextField("None", 11);
92  		coordLonField.setEditable(false);
93  		updateField = new JTextField("None yet");
94  		updateField.setEditable(false);
95  		distField = new JTextField("Not started");
96  		distField.setEditable(false);
97  
98  		String[] intervalList = { "2", "4", "6", "8", "10", "15", "20", "30",
99  				"45", "60", "120", "180", "240", "300", "600", "900", "1800",
100 				"3600", "7200"}; 
101 		SpinnerListModel intervalModel = new SpinnerListModel(intervalList);
102 		intervalSpinner = new JSpinner(intervalModel);
103 		
104 		gpsButton = new JButton("Not started", imgGPSOff);
105 		gpsButton.setVerticalTextPosition(AbstractButton.CENTER);
106 		gpsButton.setHorizontalTextPosition(AbstractButton.LEFT);
107 		gpsButton.setMnemonic(KeyEvent.VK_M);
108 		gpsButton.setPreferredSize(new Dimension(150, 50));
109 
110 		serverButton = new JButton("Buffering", imgBuffer);
111 		serverButton.setVerticalTextPosition(AbstractButton.CENTER);
112 		serverButton.setHorizontalTextPosition(AbstractButton.RIGHT);
113 		serverButton.setMnemonic(KeyEvent.VK_M);
114 		serverButton.setPreferredSize(new Dimension(150, 50));
115 		
116 		activeRouteLabel = new JLabel("No active route is set");
117 		
118 		centerPanel = new JPanel();
119 		centerPanel.setLayout(new GridLayout(0, 2));
120 		centerPanel.setBorder(BorderFactory.createTitledBorder(null,
121 				"Info from GPS-unit"));
122 		centerPanel.add(new JLabel("Recieved: "));
123 		centerPanel.add(numCoordsField);
124 		centerPanel.add(new JLabel("Latitude: "));
125 		centerPanel.add(coordLatField);
126 		centerPanel.add(new JLabel("Longitude: "));
127 		centerPanel.add(coordLonField);
128 		centerPanel.add(new JLabel("Last update "));
129 		centerPanel.add(updateField);
130 		centerPanel.add(new JLabel("Total dist. "));
131 		centerPanel.add(distField);
132 
133 		centerBorderPanel = new JPanel();
134 		centerBorderPanel.setBorder(
135 				BorderFactory.createEmptyBorder(10, 10, 10, 10));
136 		centerBorderPanel.add(centerPanel);
137 			
138 		routePanel = new JPanel();
139 		routePanel.setBorder(BorderFactory.createTitledBorder(null,
140 				"Active route and GPS-unit recieve interval"));
141 		routePanel.setLayout(new GridLayout(2, 1));
142 		routePanel.add(new JLabel("Active route: "));
143 		routePanel.add(activeRouteLabel);
144 		routePanel.add(new JLabel("Interval: "));
145 		routePanel.add(intervalSpinner);
146 
147 		routeBorderPanel = new JPanel();
148 		routeBorderPanel.setBorder(
149 				BorderFactory.createEmptyBorder(10, 10, 10, 10));
150 		routeBorderPanel.add(routePanel);
151 		
152 		buttonPanel = new JPanel();
153 		buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0));	
154 		buttonPanel.add(gpsButton); 
155 		buttonPanel.add(serverButton); 
156 	
157 		mainPanel = new JPanel();
158 		mainPanel.setLayout(new RiverLayout());		
159 		mainPanel.add("center", centerBorderPanel);
160 		mainPanel.add("p center", routeBorderPanel);
161 		mainPanel.add("p center", buttonPanel);
162 
163 		new MainFormController(model, this);
164 		view.register(this);
165 
166 		model.addSocketListener(this);
167 		model.addGPSListener(this);
168 	}
169     
170 	/***
171 	 * Register a listener on the GPS button.
172 	 * 
173 	 * @param listener
174 	 */
175 	public void addGPSBtnListener(ActionListener listener) {
176 		gpsButton.addActionListener(listener);
177 	}
178 
179 	/***
180 	 * Adds an changelistener for the intervalspinner.
181 	 * 
182 	 * @param listener
183 	 */
184 	public void addSpinnerListener(ChangeListener listener) {
185 		intervalSpinner.addChangeListener(listener);
186 	}
187 
188 	/***
189 	 * Adds an actionlistener for the timer.
190 	 * 
191 	 * @param listener
192 	 */
193 	public void addTimerListener(ActionListener listener) {
194 		timer.addActionListener(listener);
195 	}
196 
197 	/***
198 	 * Register a listener on the Server button.
199 	 * 
200 	 * @param listener
201 	 */
202 	public void addServerBtnListener(ActionListener listener) {
203 		serverButton.addActionListener(listener);
204 	}
205 
206 	/***
207 	 * Gets the form's component
208 	 */
209 	public JComponent getFormComponent() {
210 		return mainPanel;
211 	}
212 
213 	/***
214 	 * Gets the form name
215 	 */
216 	public String getName() {
217 		return name;
218 	}
219 
220 	public void coordReceived(Coordinate coord) { }
221 
222 	/*** 
223 	 * Refreshes the form
224 	 */
225 	public void refreshView() {		
226 		// Formatting the active route
227 		String r = model.getActiveRoute().getDescription();
228 		int rLen = 20;
229 
230 		if (r != null) {
231 			if (r.length() >= rLen) {
232 				r = r.substring(0, rLen) + "..";
233 				setActiveRoute(r);
234 			}
235 			setActiveRoute(r);
236 		} 
237 		
238 		// Enable/disable menu items based on status.
239 		if (model.isLoggedIn()) {
240 			view.enableLogOnItem(false);
241 			view.enableRouteItem(true);
242 			view.enableLogOffItem(true);
243 		} else {
244 			view.enableLogOffItem(false);
245 			view.enableRouteItem(false);	
246 			view.enableLogOnItem(true);
247 		}
248 		
249 		if (model.getPort() == null || model.getPort().contains("NONE")) {
250 			gpsButton.setEnabled(false);
251 			setStatusMsg("No COM-ports found. Please restart.");
252 			showErrorDialog("No COM ports found", "No COM ports was found.\n\n"
253 					+ "Please install COM-port,\nthen restart the application.",
254 					getImageIcon("portIcon.gif"));
255 		} else {
256 			gpsButton.setEnabled(true);
257 			setStatusMsg("Default port is set to " + model.getPort());	
258 		}
259 		
260 		view.enableMenu(true);
261 		if (model.hasActiveRoute()) {
262 			System.out.println("transferring to server");
263 		}
264 			
265 	}
266 
267 	/***
268 	 * Shows an error dialog 
269 	 * 
270 	 * @param heading heading for the error
271 	 * @param error the error itself
272 	 * @param icon the icon for the error
273 	 */
274 	private void showErrorDialog(String heading, String error, ImageIcon icon) {
275 		view.showErrorDialog(heading, error, icon);	
276 	}
277 
278 	/***
279 	 * Gets the interval to get data from the GPS-unit
280 	 * 
281 	 * @return interval to recieve data in secounds
282 	 */
283 	public int getInterval() {
284 		int interval = Integer.parseInt((String)intervalSpinner.getValue());
285 		StringBuilder msg = new StringBuilder();
286 		msg.append("Interval changed to ");
287 		
288 		if ((interval % 60) == 0) { 
289 			msg.append(interval / 60);
290 			if ((interval / 60) != 1 ) {
291 				msg.append(" minutes.");
292 			} else {
293 				msg.append(" minute.");
294 			}
295 		} else {
296 			msg.append(interval + " secounds.");
297 		}
298 		setStatusMsg(msg.toString());
299 	
300 		return interval;	
301 	}
302 
303 	/***
304 	 * Set the status message on the status bar.
305 	 * 
306 	 * @param status status message
307 	 */
308 	public void setStatusMsg(String status) {
309 		view.setStatusMsg(status);
310 	}
311 
312 	/***
313 	 * Increases the number of recieved coordinates
314 	 * 
315 	 */
316 	public void increaseRecievedField() {
317 		numCoordsField.setText(numRecieved++ + " Coordinates");
318 	}
319 
320 	/***
321 	 * Sets the latitude / logitude fields
322 	 * 
323 	 * @param lat the coordinate's latitude
324 	 * @param lon the coordinate's logitude
325 	 */
326 	public void setLatLonFields(String lat, String lon) {
327 		coordLatField.setText(lat);
328 		coordLonField.setText(lon);
329 	}
330 
331 	/***
332 	 * Sets the last updated field
333 	 * 
334 	 * @param update last updated
335 	 */
336 	public void setLastUpdateField(String update) {
337 		updateField.setText(update);
338 	}
339 
340 	public void statusUpdate(GPSStatusState state) {
341 		switch (state) {
342 		case CONNECTED:
343 			timer.start();
344 			System.out.println("Connected to GPS");
345 			break;
346 		case NOFIXEDPOS:
347 			System.out.println("No fixed pos");
348 			setLatLonFields("* No fixed pos *", "* No fixed pos *");
349 			break;
350 		case HASFIXEDPOS:
351 			System.out.println("Has Fixed Pos");
352 			break;
353 		case NOTCONNECTED:
354 			timer.stop();
355 			System.out.println("Not connected");
356 			break;
357 		}
358 	}
359 
360 	public void connected(boolean connected) { }
361 	
362 	/***
363 	 * Creates a new timer 
364 	 * 
365 	 * @param listener the listener
366 	 */
367 	public void createTimer(ActionListener listener) {
368 		timer = new Timer(getInterval() * 1000, listener);
369 	}
370 	
371 	/***
372 	 * Changes the text on the serverButton.
373 	 * 
374 	 * @param text the new text on the button
375 	 */
376 	public void setServerBtnTxt(String text) {
377 		serverButton.setText(text);
378 	}
379 
380 	/***
381 	 * Changes the text on the gpsButton.
382 	 * 
383 	 * @param text the new text on the button
384 	 */
385 	public void setGPSBtnTxt(String text) {
386 		gpsButton.setText(text);
387 	}
388 	
389 	/***
390 	 * Show main form.
391 	 */
392 	public void showMain() {
393 		view.showMainForm();
394 	}
395 	
396 	/***
397 	 * Shows an error dialog.
398 	 * 
399 	 * @param heading heading of the error dialog
400 	 * @param error the error itself
401 	 */
402 	public void showErrorDialog(String heading, String error) {
403 		view.showErrorDialog(heading, error);
404 	}
405 
406 	/***
407 	 * Get the current timer
408 	 * 
409 	 * @return Returns the timer.
410 	 */
411 	public Timer getTimer() {
412 		return timer;
413 	}
414 
415 	/***
416 	 * Shows the loginform.
417 	 */
418 	public void showLogin() {
419 		view.showLoginForm();
420 	}
421 
422 	/***
423 	 * Stops the timer.
424 	 */
425 	public void stopTimer() {
426 		timer.stop();
427 	}
428 
429 	/***
430 	 * Starts the timer.
431 	 */
432 	public void startTimer() {
433 		timer.start();
434 	}
435 	
436 	/***
437 	 * Checks wether the timer is running or not
438 	 * 
439 	 * @return true if running
440 	 */
441 	public boolean timerIsRunning() {
442 		return timer.isRunning();
443 	}
444 
445 	/***
446 	 * Show route form
447 	 */
448 	public void showRoutes() {
449 		view.showRoutesForm();
450 		
451 	}
452 
453 	/***
454 	 * Creates a dialog for transfer buffered coordinates or not
455 	 * 
456 	 * @return true if transfer
457 	 */
458 	public boolean transferBufferedDialog() {
459 		Object[] options = { "Continiue buffering", "Transfer buffered" };
460 		int n = JOptionPane.showOptionDialog(null,
461 				"Buffered coordinates exists, what would you like to do?",
462 				"Buffered coordinates exists", JOptionPane.YES_NO_CANCEL_OPTION,
463 				JOptionPane.QUESTION_MESSAGE, null, options, options[1]);
464 		return (n == 0);
465 	}
466 	
467 	/***
468 	 * Sets the activeroute label
469 	 * 
470 	 * @param route the active route
471 	 */
472 	public void setActiveRoute(String route) {
473 		activeRouteLabel.setText(route);
474 	}
475 	
476 	@Override
477 	public void connectionUpdate(ConnectionState state) {
478 		switch (state) {
479 		case DISCONNECTED:
480 			bufferToFile();
481 			model.changeTransferState();
482 			setStatusMsg("!!: Connection to server is lost.");
483 			break;
484 		case CONNECTED:
485 			if (model.getActiveRoute().getRouteId() != -1
486 					&& model.isLoggedIn()) {
487 				model.setActiveRoute(model.getActiveRoute());
488 				setStatusMsg("Setting active route to "
489 						+ model.getActiveRoute().getRouteId() + " on server");
490 			}
491 			break;
492 		} 
493 	}
494 
495 	/***
496 	 * Transferring to server, changes status message, 
497 	 * text on server button and the server button icon 
498 	 * to transfer mode.
499 	 */
500 	public void transferToServer() {
501 		setStatusMsg("Transferring to server ..");
502 		setServerBtnTxt("Transferring");
503 		serverButton.setIcon(imgServer);
504 	}
505 	
506 	/***
507 	 * Changes the text and icon on the GPS button to recieve mode, also changes
508 	 * the status msg.
509 	 */
510 	public void recieveFromGPS() {
511 		setGPSBtnTxt("Recieving");
512 		gpsButton.setIcon(imgGPSOn);
513 		setStatusMsg("Recieving from GPS-unit.");
514 	}
515 	
516 	/***
517 	 * Changes the text and icon on the GPS button to pause mode, also changes
518 	 * the status msg.
519 	 */
520 	public void pauseFromGPS() {
521 		stopTimer();
522 		setStatusMsg("Recieving from GPS-unit is paused.");
523 		setGPSBtnTxt("Paused");
524 		gpsButton.setIcon(imgGPSOff);
525 	}
526 	
527 	/***
528 	 * Changes the text and icon on the server
529 	 * button to buffer mode, also changes the
530 	 * status msg.
531 	 */
532 	public void bufferToFile() {
533 		setStatusMsg("Buffering to file ..");
534 		setServerBtnTxt("Buffering");
535 		serverButton.setIcon(imgBuffer);
536 	}
537 
538 	/***
539 	 * Handles an read exception when trying to read 
540 	 * data from the GPS-unit
541 	 */
542 	public void readExcepion(IOException e) {
543 		view.setStatusMsg("!!: Problems reading from the GPS");
544 		System.out.println("readException: " + e); // XXX Tmp, remove
545 	}
546 
547 	/***
548 	 * Sets the distance field.
549 	 * 
550 	 * @param dist traveled distance
551 	 */
552 	public void setDistField(double dist) {
553 		distance += dist;
554 		if (distance > 1) {
555 			distField.setText(Utils.round(distance, 3) + " km");
556 		} else {
557 			distField.setText(Utils.round(distance * 1000, 0) + " m");
558 		}
559 	}
560 }