source: src/mlx/gui/flight.py@ 754:8547ad78cc29

Last change on this file since 754:8547ad78cc29 was 754:8547ad78cc29, checked in by István Váradi <ivaradi@…>, 9 years ago

The pages now have a textual identifier and can be referenced with it (re #286)

File size: 192.9 KB
Line 
1
2from mlx.gui.common import *
3import mlx.gui.cef as cef
4
5import mlx.const as const
6import mlx.fs as fs
7import mlx.acft as acft
8from mlx.flight import Flight
9from mlx.checks import PayloadChecker
10from mlx.gates import lhbpGates
11import mlx.util as util
12from mlx.pirep import PIREP
13from mlx.i18n import xstr
14from mlx.sound import startSound
15import mlx.web as web
16
17import datetime
18import time
19import os
20import tempfile
21import threading
22import re
23
24#-----------------------------------------------------------------------------
25
26## @package mlx.gui.flight
27#
28# The flight "wizard".
29#
30# This module implements the main tab of the application, the flight
31# wizard. The wizard consists of \ref Page "pages", that come one after the
32# other. As some pages might be skipped, the pages dynamically store the index
33# of the previous page so that going back to it is simpler. The \ref
34# Page.activate "activate" function is called before a page is first shown
35# during a flight. This function should initialize the page's controls and fill
36# it with initial data. When a page is left for the first time, its \ref
37# Page.finalize "finalize" function is called. It should set those controls
38# insensitive, that will not be available if the user comes back to this page.
39#
40# Each page has a title at the top displayed in inverted colors and a big
41# font. There is a help text below it centered, that shortly describes what is
42# expected on the page. There can be two help texts: one shown when the page is
43# first displayed during a flight, another shown when the user goes back to the
44# page. The main content area is below this, also centered. Finally, there are
45# some buttons at the bottom on the right. As some buttons occur very
46# frequently, there are functions to add them (\ref Page.addCancelFlightButton
47# "addCancelFlightButton", \ref Page.addPreviousButton "addPreviousButton" and
48# \ref Page.addNextButton "addNextButton".
49#
50# The \ref Wizard class is the main class to collect the pages. It also stores
51# some data passed from one page to another and provides properties to access
52# data items set via the wizard pages.
53
54#-----------------------------------------------------------------------------
55
56comboModel = gtk.ListStore(gobject.TYPE_STRING)
57comboModel.append(("N/A",))
58comboModel.append(("VECTORS",))
59
60#-----------------------------------------------------------------------------
61
62class Page(gtk.Alignment):
63 """A page in the flight wizard."""
64 def __init__(self, wizard, id, title, help, completedHelp = None):
65 """Construct the page."""
66 super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
67 xscale = 1.0, yscale = 1.0)
68 self.set_padding(padding_top = 4, padding_bottom = 4,
69 padding_left = 12, padding_right = 12)
70
71 frame = gtk.Frame()
72 self.add(frame)
73
74 self._vbox = gtk.VBox()
75 self._vbox.set_homogeneous(False)
76 frame.add(self._vbox)
77
78 eventBox = gtk.EventBox()
79
80 alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
81
82 titleLabel = gtk.Label(title)
83 titleLabel.modify_font(pango.FontDescription("bold 24"))
84 alignment.set_padding(padding_top = 4, padding_bottom = 4,
85 padding_left = 6, padding_right = 0)
86
87 alignment.add(titleLabel)
88 eventBox.add(alignment)
89
90 self._vbox.pack_start(eventBox, False, False, 0)
91
92 self._titleEventBox = eventBox
93 self._titleLabel = titleLabel
94
95 mainBox = gtk.VBox()
96
97 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
98 xscale = 1.0, yscale = 1.0)
99 alignment.set_padding(padding_top = 16, padding_bottom = 16,
100 padding_left = 16, padding_right = 16)
101 alignment.add(mainBox)
102 self._vbox.pack_start(alignment, True, True, 0)
103
104 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
105 xscale = 0.0, yscale = 0.0)
106 alignment.set_padding(padding_top = 0, padding_bottom = 16,
107 padding_left = 0, padding_right = 0)
108
109 self._help = help
110 self._completedHelp = completedHelp
111
112 if self._completedHelp is None or \
113 len(help.splitlines())>=len(completedHelp.splitlines()):
114 longerHelp = help
115 else:
116 longerHelp = completedHelp
117
118 self._helpLabel = gtk.Label(longerHelp)
119 # FIXME: should be a constant in common
120 self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
121 else gtk.JUSTIFY_CENTER)
122 self._helpLabel.set_use_markup(True)
123 alignment.add(self._helpLabel)
124 mainBox.pack_start(alignment, False, False, 0)
125
126 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
127 xscale = 1.0, yscale = 1.0)
128 mainBox.pack_start(self._mainAlignment, True, True, 0)
129
130 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
131 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
132 padding_left = 16, padding_right = 16)
133
134 self._buttonBox = gtk.HBox()
135 self._buttonBox.set_homogeneous(False)
136 self._defaultButton = None
137 buttonAlignment.add(self._buttonBox)
138
139 self._vbox.pack_start(buttonAlignment, False, False, 0)
140
141 self._wizard = wizard
142 self._id = id
143 self._nextPageID = None
144
145 self._cancelFlightButton = None
146
147 self._completed = False
148 self._fromPage = None
149
150 @property
151 def id(self):
152 """Get the identifier of the page."""
153 return self._id
154
155 @property
156 def nextPageID(self):
157 """Get the identifier of the next page, if set."""
158 return self._nextPageID
159
160 @nextPageID.setter
161 def nextPageID(self, nextPageID):
162 """Set the identifier of the next page."""
163 self._nextPageID = nextPageID
164
165 def setMainWidget(self, widget):
166 """Set the given widget as the main one."""
167 self._mainAlignment.add(widget)
168
169 def addButton(self, label, default = False, sensitive = True,
170 tooltip = None, clicked = None, padding = 4):
171 """Add a button with the given label.
172
173 Return the button object created."""
174 button = gtk.Button(label)
175 self._buttonBox.pack_start(button, False, False, padding)
176 button.set_use_underline(True)
177 if default:
178 button.set_can_default(True)
179 self._defaultButton = button
180 button.set_sensitive(sensitive)
181 if tooltip is not None:
182 button.set_tooltip_text(tooltip)
183 if clicked is not None:
184 button.connect("clicked", clicked)
185 return button
186
187 def addCancelFlightButton(self):
188 """Add the 'Cancel flight' button to the page."""
189 self._cancelFlightButton = \
190 self.addButton(xstr("button_cancelFlight"),
191 sensitive = True,
192 tooltip = xstr("button_cancelFlight_tooltip"),
193 clicked = self._cancelFlight,
194 padding = 16)
195 return self._cancelFlightButton
196
197 def addPreviousButton(self, sensitive = True, clicked = None):
198 """Add the 'Next' button to the page."""
199 return self.addButton(xstr("button_previous"),
200 sensitive = sensitive,
201 tooltip = xstr("button_previous_tooltip"),
202 clicked = clicked)
203
204 def addNextButton(self, default = True, sensitive = True,
205 clicked = None):
206 """Add the 'Next' button to the page."""
207 return self.addButton(xstr("button_next"),
208 default = default,
209 sensitive = sensitive,
210 tooltip = xstr("button_next_tooltip"),
211 clicked = clicked)
212
213 def setStyle(self):
214 """Set the styles of some of the items on the page."""
215 if pygobject:
216 context = self.get_style_context()
217 color = context.get_background_color(gtk.StateFlags.SELECTED)
218 self._titleEventBox.modify_bg(0, color.to_color())
219 color = context.get_color(gtk.StateFlags.SELECTED)
220 self._titleLabel.modify_fg(0, color.to_color())
221 else:
222 style = self.rc_get_style()
223 self._titleEventBox.modify_bg(0, style.bg[3])
224 self._titleLabel.modify_fg(0, style.fg[3])
225
226 def initialize(self):
227 """Initialize the page.
228
229 It sets up the primary help, and calls the activate() function."""
230 self._helpLabel.set_markup(self._help)
231 self._helpLabel.set_sensitive(True)
232 self.activate()
233
234 def activate(self):
235 """Called when this page becomes active.
236
237 This default implementation does nothing."""
238 pass
239
240 def setHelp(self, help):
241 """Set the help string."""
242 self._help = help
243 if not self._completed:
244 self._helpLabel.set_markup(self._help)
245 self._helpLabel.set_sensitive(True)
246
247 def complete(self):
248 """Called when the page is completed.
249
250 It greys out/changes the help text and then calls finalize()."""
251 self.finalize()
252 if self._completedHelp is None:
253 self._helpLabel.set_sensitive(False)
254 else:
255 self._helpLabel.set_markup(self._completedHelp)
256 self._completed = True
257
258 def finalize(self):
259 """Called when the page is finalized."""
260 pass
261
262 def grabDefault(self):
263 """If the page has a default button, make it the default one."""
264 if self._defaultButton is not None:
265 self._defaultButton.grab_default()
266
267 def reset(self):
268 """Reset the page if the wizard is reset."""
269 self._completed = False
270 self._fromPage = None
271 if self._cancelFlightButton is not None:
272 self._cancelFlightButton.set_sensitive(True)
273
274 def goBack(self):
275 """Go to the page we were invoked from."""
276 assert self._fromPage is not None
277
278 self._wizard.setCurrentPage(self._fromPage, finalize = False)
279
280 def flightEnded(self):
281 """Called when the flight has ended.
282
283 This default implementation disables the cancel flight button."""
284 if self._cancelFlightButton is not None:
285 self._cancelFlightButton.set_sensitive(False)
286
287 def _cancelFlight(self, button):
288 """Called when the Cancel flight button is clicked."""
289 self._wizard.gui.cancelFlight()
290
291#-----------------------------------------------------------------------------
292
293class LoginPage(Page):
294 """The login page."""
295 def __init__(self, wizard):
296 """Construct the login page."""
297 super(LoginPage, self).__init__(wizard, "login",
298 xstr("login"), xstr("loginHelp"))
299
300 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
301 xscale = 0.0, yscale = 0.0)
302
303 table = gtk.Table(4, 2)
304 table.set_row_spacings(4)
305 table.set_col_spacings(32)
306 alignment.add(table)
307 self.setMainWidget(alignment)
308
309 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
310 xscale = 0.0, yscale = 0.0)
311 label = gtk.Label(xstr("label_pilotID"))
312 label.set_use_underline(True)
313 labelAlignment.add(label)
314 table.attach(labelAlignment, 0, 1, 0, 1)
315
316 self._pilotID = gtk.Entry()
317 self._pilotID.connect("changed", self._pilotIDChanged)
318 self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
319 table.attach(self._pilotID, 1, 2, 0, 1)
320 label.set_mnemonic_widget(self._pilotID)
321
322 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
323 xscale = 0.0, yscale = 0.0)
324 label = gtk.Label(xstr("label_password"))
325 label.set_use_underline(True)
326 labelAlignment.add(label)
327 table.attach(labelAlignment, 0, 1, 1, 2)
328
329 self._password = gtk.Entry()
330 self._password.set_visibility(False)
331 self._password.connect("changed", self._setControls)
332 self._password.set_tooltip_text(xstr("login_password_tooltip"))
333 table.attach(self._password, 1, 2, 1, 2)
334 label.set_mnemonic_widget(self._password)
335
336 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
337 self._rememberButton.set_use_underline(True)
338 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
339 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
340
341 self._entranceExam = gtk.CheckButton(xstr("login_entranceExam"))
342 self._entranceExam.set_use_underline(True)
343 self._entranceExam.set_tooltip_text(xstr("login_entranceExam_tooltip"))
344 self._entranceExam.connect("toggled", self._setControls)
345 table.attach(self._entranceExam, 1, 2, 3, 4, ypadding = 12)
346
347 self.addButton(xstr("button_login_register"),
348 clicked = self._registerClicked,
349 tooltip = xstr("button_login_register_tooltip"))
350
351 self.addButton(xstr("button_offline"),
352 clicked = self._offlineClicked,
353 tooltip = xstr("button_offline_tooltip"))
354
355 self._loginButton = self.addButton(xstr("button_login"), default = True)
356 self._loginButton.connect("clicked", self._loginClicked)
357 self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
358
359
360 @property
361 def entranceExam(self):
362 """Get whether an entrance exam is being performed."""
363 return self._entranceExam.get_active() and \
364 self._pilotID.get_text()!=""
365
366 @property
367 def pilotID(self):
368 """Get the pilot ID, if given."""
369 return self._pilotID.get_text()
370
371 def activate(self):
372 """Activate the page."""
373 config = self._wizard.gui.config
374 self._pilotID.set_text(config.pilotID)
375 self._password.set_text(config.password)
376 self._rememberButton.set_active(config.rememberPassword)
377 self._setControls(None)
378
379 def _pilotIDChanged(self, entry):
380 """Called when the pilot ID has changed.
381
382 It sets the text to upper-case and calls _setControls to update other
383 stuff."""
384 entry.set_text(entry.get_text().upper())
385 self._setControls(entry)
386
387 def _setControls(self, entry = None):
388 """Set the sensitivity of the various controls.
389
390 The login button is sensitive only if both the pilot ID and the
391 password fields contain values.
392
393 The password field is sensitive only, if the entrance exam checkbox is
394 not selected.
395
396 The remember password checkbox is sensitive only, if the password field
397 contains text.
398
399 The entrance exam checkbox is sensitive only, if the pilot ID is not
400 empty."""
401 pilotID = self._pilotID.get_text()
402 password = self._password.get_text()
403 entranceExam = self._entranceExam.get_active()
404 self._password.set_sensitive(not entranceExam)
405 self._rememberButton.set_sensitive(password!="" and not entranceExam)
406 self._entranceExam.set_sensitive(pilotID!="")
407 self._loginButton.set_sensitive(pilotID!="" and
408 (password!="" or entranceExam))
409
410 def _registerClicked(self, button):
411 """Called when the Register button was clicked."""
412 self._wizard.jumpPage("register")
413
414 def _offlineClicked(self, button):
415 """Called when the offline button was clicked."""
416 print "mlx.flight.LoginPage: offline flight selected"
417 self._wizard.nextPage()
418
419 def _loginClicked(self, button):
420 """Called when the login button was clicked."""
421 print "mlx.flight.LoginPage: logging in"
422 self._wizard.login(self._handleLoginResult,
423 self._pilotID.get_text(),
424 self._password.get_text(),
425 self.entranceExam)
426
427 def _handleLoginResult(self, returned, result):
428 """Handle the login result."""
429 self._loginButton.set_sensitive(True)
430 if returned and result.loggedIn:
431 config = self._wizard.gui.config
432
433 config.pilotID = self._pilotID.get_text()
434
435 rememberPassword = self._rememberButton.get_active()
436 config.password = result.password if rememberPassword else ""
437
438 config.rememberPassword = rememberPassword
439
440 config.save()
441 self._wizard.nextPage()
442
443#-----------------------------------------------------------------------------
444
445class FlightSelectionPage(Page):
446 """The page to select the flight."""
447 def __init__(self, wizard):
448 """Construct the flight selection page."""
449 help = xstr("flightsel_help")
450 completedHelp = xstr("flightsel_chelp")
451 super(FlightSelectionPage, self).__init__(wizard, "flightsel",
452 xstr("flightsel_title"),
453 help, completedHelp = completedHelp)
454
455
456 self._listStore = gtk.ListStore(str, str, str, str)
457 self._flightList = gtk.TreeView(self._listStore)
458 column = gtk.TreeViewColumn(xstr("flightsel_no"), gtk.CellRendererText(),
459 text = 1)
460 column.set_expand(True)
461 self._flightList.append_column(column)
462 column = gtk.TreeViewColumn(xstr("flightsel_deptime"), gtk.CellRendererText(),
463 text = 0)
464 column.set_expand(True)
465 self._flightList.append_column(column)
466 column = gtk.TreeViewColumn(xstr("flightsel_from"), gtk.CellRendererText(),
467 text = 2)
468 column.set_expand(True)
469 self._flightList.append_column(column)
470 column = gtk.TreeViewColumn(xstr("flightsel_to"), gtk.CellRendererText(),
471 text = 3)
472 column.set_expand(True)
473 self._flightList.append_column(column)
474 self._flightList.connect("row-activated", self._rowActivated)
475 self._flightList.connect("button-press-event", self._listButtonPressed)
476
477 self._flightListPopupMenu = None
478
479 flightSelection = self._flightList.get_selection()
480 flightSelection.connect("changed", self._selectionChanged)
481
482 scrolledWindow = gtk.ScrolledWindow()
483 scrolledWindow.add(self._flightList)
484 scrolledWindow.set_size_request(400, -1)
485 # FIXME: these should be constants in common.py
486 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
487 else gtk.POLICY_AUTOMATIC,
488 gtk.PolicyType.AUTOMATIC if pygobject
489 else gtk.POLICY_AUTOMATIC)
490 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
491 else gtk.SHADOW_IN)
492
493 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
494 alignment.add(scrolledWindow)
495
496 self.setMainWidget(alignment)
497
498 self._saveButton = self.addButton(xstr("flightsel_save"),
499 sensitive = False,
500 clicked = self._saveClicked,
501 tooltip = xstr("flightsel_save_tooltip"))
502 self._saveDialog = None
503
504 self._refreshButton = self.addButton(xstr("flightsel_refresh"),
505 sensitive = True,
506 clicked = self._refreshClicked,
507 tooltip = xstr("flightsel_refresh_tooltip"))
508
509 self._loadButton = self.addButton(xstr("flightsel_load"),
510 sensitive = True,
511 tooltip = xstr("flightsel_load_tooltip"))
512 self._loadButton.connect("clicked", self._loadButtonClicked)
513 self._loadDialog = None
514
515 self._button = self.addNextButton(sensitive = False,
516 clicked = self._forwardClicked)
517
518 self._flights = []
519
520 def activate(self):
521 """Fill the flight list."""
522 self._flightList.set_sensitive(True)
523 self._loadButton.set_sensitive(True)
524 self._refreshButton.set_sensitive(self._wizard.loggedIn)
525 self._buildFlights()
526
527 def finalize(self):
528 """Finalize the page."""
529 self._flightList.set_sensitive(False)
530 self._loadButton.set_sensitive(False)
531 self._refreshButton.set_sensitive(False)
532
533 def _buildFlights(self):
534 """Rebuild the flights from the login result."""
535 self._flights = []
536 self._listStore.clear()
537 if self._wizard.loggedIn:
538 for flight in self._wizard.loginResult.flights:
539 self._addFlight(flight)
540
541 def _addFlight(self, flight):
542 """Add the given file to the list of flights."""
543 self._flights.append(flight)
544 self._listStore.append([str(flight.departureTime),
545 flight.callsign,
546 flight.departureICAO,
547 flight.arrivalICAO])
548
549 def _saveClicked(self, button):
550 """Called when the Save flight button is clicked."""
551 self._saveSelected()
552
553 def _saveSelected(self):
554 """Save the selected flight."""
555 flight = self._getSelectedFlight()
556 date = flight.departureTime.date()
557 name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
558 (date.year, date.month, date.day, flight.callsign,
559 flight.departureICAO, flight.arrivalICAO)
560
561 dialog = self._getSaveDialog()
562 dialog.set_current_name(name)
563 dialog.show_all()
564 response = dialog.run()
565 dialog.hide()
566
567 if response==RESPONSETYPE_OK:
568 fileName = text2unicode(dialog.get_filename())
569 print "Saving", fileName
570 try:
571 with open(fileName, "wt") as f:
572 flight.writeIntoFile(f)
573 except Exception, e:
574 print "Failed to save flight:", util.utf2unicode(str(e))
575 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
576 type = MESSAGETYPE_ERROR,
577 message_format =
578 xstr("flightsel_save_failed"))
579 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
580 dialog.set_title(WINDOW_TITLE_BASE)
581 secondary = xstr("flightsel_save_failed_sec")
582 dialog.format_secondary_markup(secondary)
583 dialog.run()
584 dialog.hide()
585
586 def _refreshClicked(self, button):
587 """Called when the refresh button is clicked."""
588 self._wizard.reloadFlights(self._refreshCallback)
589
590 def _refreshCallback(self, returned, result):
591 """Callback for the refresh."""
592 if returned and result.loggedIn:
593 self._buildFlights()
594
595 def _selectionChanged(self, selection):
596 """Called when the selection is changed."""
597 selected = selection.count_selected_rows()==1
598 self._saveButton.set_sensitive(selected)
599 self._button.set_sensitive(selected)
600
601 def _loadButtonClicked(self, loadButton):
602 """Called when the load a flight button is clicked."""
603 dialog = self._getLoadDialog()
604 dialog.show_all()
605 response = dialog.run()
606 dialog.hide()
607
608 if response==RESPONSETYPE_OK:
609 fileName = text2unicode(dialog.get_filename())
610 print "Loading", fileName
611 bookedFlight = web.BookedFlight()
612 try:
613 with open(fileName, "rt") as f:
614 bookedFlight.readFromFile(f)
615 self._addFlight(bookedFlight)
616 except Exception, e:
617 print "Failed to load flight:", util.utf2unicode(str(e))
618 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
619 type = MESSAGETYPE_ERROR,
620 message_format =
621 xstr("flightsel_load_failed"))
622 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
623 dialog.set_title(WINDOW_TITLE_BASE)
624 secondary = xstr("flightsel_load_failed_sec")
625 dialog.format_secondary_markup(secondary)
626 dialog.run()
627 dialog.hide()
628
629 def _forwardClicked(self, button):
630 """Called when the forward button was clicked."""
631 if self._completed:
632 self._wizard.jumpPage(self._nextID, finalize = False)
633 else:
634 self._flightSelected()
635
636 def _rowActivated(self, flightList, path, column):
637 """Called when a row is activated."""
638 if not self._completed:
639 self._flightSelected()
640
641 def _flightSelected(self):
642 """Called when a flight has been selected."""
643 flight = self._getSelectedFlight()
644 self._wizard._bookedFlight = flight
645 self._wizard.gui.enableFlightInfo(flight.aircraftType)
646
647 self._updateDepartureGate()
648
649 def _getSelectedFlight(self):
650 """Get the currently selected flight."""
651 selection = self._flightList.get_selection()
652 (listStore, iter) = selection.get_selected()
653 path = listStore.get_path(iter)
654 [index] = path.get_indices() if pygobject else path
655
656 return self._flights[index]
657
658 def _listButtonPressed(self, widget, event):
659 """Called when a mouse button is pressed on the flight list."""
660 if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
661 return
662
663 (path, _, _, _) = self._flightList.get_path_at_pos(int(event.x),
664 int(event.y))
665 selection = self._flightList.get_selection()
666 selection.unselect_all()
667 selection.select_path(path)
668
669 menu = self._getListPopupMenu()
670 if pygobject:
671 menu.popup(None, None, None, None, event.button, event.time)
672 else:
673 menu.popup(None, None, None, event.button, event.time)
674
675 def _updateDepartureGate(self):
676 """Update the departure gate for the booked flight."""
677 flight = self._wizard._bookedFlight
678 if self._wizard.gui.config.onlineGateSystem and \
679 self._wizard.loggedIn and not self._wizard.entranceExam:
680 if flight.departureICAO=="LHBP":
681 self._wizard.getFleet(self._fleetRetrieved)
682 else:
683 self._wizard.updatePlane(self._planeUpdated,
684 flight.tailNumber,
685 const.PLANE_AWAY)
686 else:
687 self._nextID = "connect"
688 self._wizard.jumpPage("connect")
689
690 def _fleetRetrieved(self, fleet):
691 """Called when the fleet has been retrieved."""
692 if fleet is None:
693 self._nextID = "connect"
694 self._wizard.jumpPage("connect")
695 else:
696 plane = fleet[self._wizard._bookedFlight.tailNumber]
697 if plane is None:
698 self._nextID = "connect"
699 self._wizard.jumpPage("connect")
700 elif plane.gateNumber is not None and \
701 not fleet.isGateConflicting(plane):
702 self._wizard._departureGate = plane.gateNumber
703 self._nextID = "connect"
704 self._wizard.jumpPage("connect")
705 else:
706 self._nextID = "gatesel"
707 self._wizard.jumpPage("gatesel")
708
709 def _planeUpdated(self, success):
710 """Callback for the plane updating."""
711 self._nextID = "connect"
712 self._wizard.jumpPage("connect")
713
714 def _getSaveDialog(self):
715 """Get the dialog to load a flight file."""
716 if self._saveDialog is not None:
717 return self._saveDialog
718
719 gui = self._wizard.gui
720 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
721 xstr("flightsel_save_title"),
722 action = FILE_CHOOSER_ACTION_SAVE,
723 buttons = (gtk.STOCK_CANCEL,
724 RESPONSETYPE_CANCEL,
725 gtk.STOCK_OK, RESPONSETYPE_OK),
726 parent = gui.mainWindow)
727 dialog.set_modal(True)
728 dialog.set_do_overwrite_confirmation(True)
729
730 filter = gtk.FileFilter()
731 filter.set_name(xstr("flightsel_filter_flights"))
732 filter.add_pattern("*.vaflight")
733 dialog.add_filter(filter)
734
735 filter = gtk.FileFilter()
736 filter.set_name(xstr("file_filter_all"))
737 filter.add_pattern("*.*")
738 dialog.add_filter(filter)
739
740 self._saveDialog = dialog
741
742 return dialog
743
744 def _getLoadDialog(self):
745 """Get the dialog to load a flight file."""
746 if self._loadDialog is not None:
747 return self._loadDialog
748
749 gui = self._wizard.gui
750 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
751 xstr("flightsel_load_title"),
752 action = FILE_CHOOSER_ACTION_OPEN,
753 buttons = (gtk.STOCK_CANCEL,
754 RESPONSETYPE_CANCEL,
755 gtk.STOCK_OK, RESPONSETYPE_OK),
756 parent = gui.mainWindow)
757 dialog.set_modal(True)
758
759 filter = gtk.FileFilter()
760 filter.set_name(xstr("flightsel_filter_flights"))
761 filter.add_pattern("*.vaflight")
762 dialog.add_filter(filter)
763
764 filter = gtk.FileFilter()
765 filter.set_name(xstr("file_filter_all"))
766 filter.add_pattern("*.*")
767 dialog.add_filter(filter)
768
769 self._loadDialog = dialog
770
771 return dialog
772
773 def _getListPopupMenu(self):
774 """Get the flight list popup menu."""
775 if self._flightListPopupMenu is None:
776 menu = gtk.Menu()
777
778 menuItem = gtk.MenuItem()
779 menuItem.set_label(xstr("flightsel_popup_select"))
780 menuItem.set_use_underline(True)
781 menuItem.connect("activate", self._popupSelect)
782 menuItem.show()
783
784 menu.append(menuItem)
785
786 menuItem = gtk.MenuItem()
787 menuItem.set_label(xstr("flightsel_popup_save"))
788 menuItem.set_use_underline(True)
789 menuItem.connect("activate", self._popupSave)
790 menuItem.show()
791
792 menu.append(menuItem)
793
794 self._flightListPopupMenu = menu
795
796 return self._flightListPopupMenu
797
798 def _popupSelect(self, menuItem):
799 """Called when the Select menu item is activated in the popup menu."""
800 if not self._completed:
801 self._flightSelected()
802
803 def _popupSave(self, menuItem):
804 """Called when the Save menu item is activated in the popup menu."""
805 if not self._completed:
806 self._saveSelected()
807
808#-----------------------------------------------------------------------------
809
810class GateSelectionPage(Page):
811 """Page to select a free gate at LHBP.
812 This page should be displayed only if we have fleet information!."""
813 def __init__(self, wizard):
814 """Construct the gate selection page."""
815 super(GateSelectionPage, self).__init__(wizard, "gatesel",
816 xstr("gatesel_title"),
817 xstr("gatesel_help"))
818
819 self._listStore = gtk.ListStore(str)
820 self._gateList = gtk.TreeView(self._listStore)
821 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
822 text = 0)
823 column.set_expand(True)
824 self._gateList.append_column(column)
825 self._gateList.set_headers_visible(False)
826 self._gateList.connect("row-activated", self._rowActivated)
827
828 gateSelection = self._gateList.get_selection()
829 gateSelection.connect("changed", self._selectionChanged)
830
831 scrolledWindow = gtk.ScrolledWindow()
832 scrolledWindow.add(self._gateList)
833 scrolledWindow.set_size_request(50, -1)
834 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
835 else gtk.POLICY_AUTOMATIC,
836 gtk.PolicyType.AUTOMATIC if pygobject
837 else gtk.POLICY_AUTOMATIC)
838 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
839 else gtk.SHADOW_IN)
840
841 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
842 alignment.add(scrolledWindow)
843
844 self.setMainWidget(alignment)
845
846 self.addCancelFlightButton()
847
848 self.addPreviousButton(clicked = self._backClicked)
849
850 self._button = self.addNextButton(sensitive = False,
851 clicked = self._forwardClicked)
852
853 def activate(self):
854 """Fill the gate list."""
855 self._listStore.clear()
856 self._gateList.set_sensitive(True)
857 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
858 for gate in lhbpGates.gates:
859 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
860 self._listStore.append([gate.number])
861
862 def finalize(self):
863 """Finalize the page."""
864 self._gateList.set_sensitive(False)
865
866 def _selectionChanged(self, selection):
867 """Called when the selection is changed."""
868 self._button.set_sensitive(selection.count_selected_rows()==1)
869
870 def _backClicked(self, button):
871 """Called when the Back button is pressed."""
872 self.goBack()
873
874 def _forwardClicked(self, button):
875 """Called when the forward button is clicked."""
876 if not self._completed:
877 self._gateSelected()
878 else:
879 self._wizard.jumpPage("connect")
880
881 def _rowActivated(self, flightList, path, column):
882 """Called when a row is activated."""
883 if not self._completed:
884 self._gateSelected()
885
886 def _gateSelected(self):
887 """Called when a gate has been selected."""
888 selection = self._gateList.get_selection()
889 (listStore, iter) = selection.get_selected()
890 (gateNumber,) = listStore.get(iter, 0)
891
892 self._wizard._departureGate = gateNumber
893
894 self._wizard.updatePlane(self._planeUpdated,
895 self._wizard._bookedFlight.tailNumber,
896 const.PLANE_HOME, gateNumber)
897
898 def _planeUpdated(self, success):
899 """Callback for the plane updating call."""
900 if success is None or success:
901 self._wizard.jumpPage("connect")
902 else:
903 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
904 type = MESSAGETYPE_ERROR,
905 message_format = xstr("gatesel_conflict"))
906 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
907 dialog.set_title(WINDOW_TITLE_BASE)
908 dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
909 dialog.run()
910 dialog.hide()
911
912 self._wizard.getFleet(self._fleetRetrieved)
913
914 def _fleetRetrieved(self, fleet):
915 """Called when the fleet has been retrieved."""
916 if fleet is None:
917 self._wizard.jumpPage("connect")
918 else:
919 self.activate()
920
921#-----------------------------------------------------------------------------
922
923class RegisterPage(Page):
924 """A page to enter the registration data."""
925
926 # The minimal year of birth
927 _minYearOfBirth = 1900
928
929 # The maximal year of birth
930 _maxYearOfBirth = datetime.date.today().year
931
932 # The regular expression to check the e-mail address with
933 _emailAddressRE = re.compile("[^@]+@[^@]+\.[^@]+")
934
935 def __init__(self, wizard):
936 """Construct the registration page."""
937 super(RegisterPage, self).__init__(wizard, "register",
938 xstr("register_title"),
939 xstr("register_help"))
940
941 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
942 xscale = 0.0, yscale = 0.0)
943
944 table = gtk.Table(11, 3)
945 table.set_row_spacings(4)
946 table.set_col_spacings(32)
947 alignment.add(table)
948 self.setMainWidget(alignment)
949
950 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
951 xscale = 0.0, yscale = 0.0)
952 label = gtk.Label(xstr("register_name"))
953 label.set_use_underline(True)
954 labelAlignment.add(label)
955 table.attach(labelAlignment, 0, 1, 0, 1)
956
957 self._name = gtk.Entry()
958 self._name.connect("changed", self._updateButtons)
959 self._name.set_tooltip_text(xstr("register_name_tooltip"))
960 self._name.set_width_chars(30)
961 table.attach(self._name, 1, 2, 0, 1)
962 label.set_mnemonic_widget(self._name)
963
964 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
965 xscale = 0.0, yscale = 0.0)
966 label = gtk.Label(xstr("register_year_of_birth"))
967 label.set_use_underline(True)
968 labelAlignment.add(label)
969 table.attach(labelAlignment, 0, 1, 1, 2)
970
971 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
972 xscale = 0.0, yscale = 0.0)
973
974 self._yearOfBirth = gtk.SpinButton()
975 self._yearOfBirth.set_increments(step = 1, page = 10)
976 self._yearOfBirth.set_range(min = RegisterPage._minYearOfBirth,
977 max = RegisterPage._maxYearOfBirth)
978 self._yearOfBirth.set_numeric(True)
979 self._yearOfBirth.set_tooltip_text(xstr("register_year_of_birth_tooltip"))
980 self._yearOfBirth.set_width_chars(5)
981 self._yearOfBirth.connect("changed", self._updateButtons)
982 self._yearOfBirth.connect("value-changed", self._updateButtons)
983 alignment.add(self._yearOfBirth)
984 table.attach(alignment, 1, 2, 1, 2)
985 label.set_mnemonic_widget(self._yearOfBirth)
986
987 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
988 xscale = 0.0, yscale = 0.0)
989 label = gtk.Label(xstr("register_email"))
990 label.set_use_underline(True)
991 labelAlignment.add(label)
992 table.attach(labelAlignment, 0, 1, 2, 3)
993
994 self._emailAddress = gtk.Entry()
995 self._emailAddress.connect("changed", self._updateButtons)
996 self._emailAddress.set_tooltip_text(xstr("register_email_tooltip"))
997 table.attach(self._emailAddress, 1, 2, 2, 3)
998 label.set_mnemonic_widget(self._emailAddress)
999
1000 self._emailAddressPublic = gtk.CheckButton(xstr("register_email_public"))
1001 self._emailAddressPublic.set_use_underline(True)
1002 self._emailAddressPublic.set_tooltip_text(xstr("register_email_public_tooltip"))
1003 table.attach(self._emailAddressPublic, 2, 3, 2, 3)
1004
1005 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1006 xscale = 0.0, yscale = 0.0)
1007 label = gtk.Label(xstr("register_vatsim_id"))
1008 label.set_use_underline(True)
1009 labelAlignment.add(label)
1010 table.attach(labelAlignment, 0, 1, 3, 4)
1011
1012 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1013 xscale = 0.0, yscale = 0.0)
1014 self._vatsimID = IntegerEntry()
1015 self._vatsimID.connect("changed", self._updateButtons)
1016 self._vatsimID.set_tooltip_text(xstr("register_vatsim_id_tooltip"))
1017 self._vatsimID.set_width_chars(7)
1018 alignment.add(self._vatsimID)
1019 table.attach(alignment, 1, 2, 3, 4)
1020 label.set_mnemonic_widget(self._vatsimID)
1021
1022 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1023 xscale = 0.0, yscale = 0.0)
1024 label = gtk.Label(xstr("register_ivao_id"))
1025 label.set_use_underline(True)
1026 labelAlignment.add(label)
1027 table.attach(labelAlignment, 0, 1, 4, 5)
1028
1029 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1030 xscale = 0.0, yscale = 0.0)
1031 self._ivaoID = IntegerEntry()
1032 self._ivaoID.connect("changed", self._updateButtons)
1033 self._ivaoID.set_tooltip_text(xstr("register_ivao_id_tooltip"))
1034 self._ivaoID.set_width_chars(7)
1035 alignment.add(self._ivaoID)
1036 table.attach(alignment, 1, 2, 4, 5)
1037 label.set_mnemonic_widget(self._ivaoID)
1038
1039 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1040 xscale = 0.0, yscale = 0.0)
1041 label = gtk.Label(xstr("register_phone_num"))
1042 label.set_use_underline(True)
1043 label.set_use_markup(True)
1044 labelAlignment.add(label)
1045 table.attach(labelAlignment, 0, 1, 5, 6)
1046
1047 self._phoneNumber = gtk.Entry()
1048 self._phoneNumber.set_tooltip_text(xstr("register_phone_num_tooltip"))
1049 table.attach(self._phoneNumber, 1, 2, 5, 6)
1050 label.set_mnemonic_widget(self._phoneNumber)
1051
1052 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1053 xscale = 0.0, yscale = 0.0)
1054 label = gtk.Label(xstr("register_nationality"))
1055 label.set_use_underline(True)
1056 label.set_use_markup(True)
1057 labelAlignment.add(label)
1058 table.attach(labelAlignment, 0, 1, 6, 7)
1059
1060
1061 self._nationality = gtk.Entry()
1062 self._nationality.set_tooltip_text(xstr("register_nationality_tooltip"))
1063 table.attach(self._nationality, 1, 2, 6, 7)
1064 label.set_mnemonic_widget(self._nationality)
1065
1066 placeholder = gtk.Label()
1067 placeholder.set_text(xstr("register_password_mismatch"))
1068 placeholder.set_use_markup(True)
1069 placeholder.set_child_visible(False)
1070 placeholder.hide()
1071 table.attach(placeholder, 2, 3, 6, 7)
1072
1073 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1074 xscale = 0.0, yscale = 0.0)
1075 label = gtk.Label(xstr("register_password"))
1076 label.set_use_underline(True)
1077 labelAlignment.add(label)
1078 table.attach(labelAlignment, 0, 1, 8, 9)
1079
1080 self._password = gtk.Entry()
1081 self._password.set_visibility(False)
1082 self._password.connect("changed", self._updateButtons)
1083 self._password.set_tooltip_text(xstr("register_password_tooltip"))
1084 table.attach(self._password, 1, 2, 8, 9)
1085 label.set_mnemonic_widget(self._password)
1086
1087 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1088 xscale = 0.0, yscale = 0.0)
1089 self._passwordStatus = gtk.Label()
1090 alignment.add(self._passwordStatus)
1091 table.attach(alignment, 2, 3, 8, 9)
1092
1093 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1094 xscale = 0.0, yscale = 0.0)
1095 label = gtk.Label(xstr("register_password2"))
1096 label.set_use_underline(True)
1097 labelAlignment.add(label)
1098 table.attach(labelAlignment, 0, 1, 9, 10)
1099
1100 self._password2 = gtk.Entry()
1101 self._password2.set_visibility(False)
1102 self._password2.connect("changed", self._updateButtons)
1103 self._password2.set_tooltip_text(xstr("register_password2_tooltip"))
1104 table.attach(self._password2, 1, 2, 9, 10)
1105 label.set_mnemonic_widget(self._password2)
1106
1107 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1108 xscale = 0.0, yscale = 0.0)
1109 self._password2Status = gtk.Label()
1110 alignment.add(self._password2Status)
1111 table.attach(alignment, 2, 3, 9, 10)
1112
1113 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
1114 self._rememberButton.set_use_underline(True)
1115 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
1116 table.attach(self._rememberButton, 1, 2, 10, 11)
1117
1118 cancelButton = \
1119 self.addButton(xstr("button_cancel"))
1120 cancelButton.connect("clicked", self._cancelClicked)
1121
1122 self._registerButton = \
1123 self.addButton(xstr("button_register"), default = True,
1124 tooltip = xstr("button_register_tooltip"))
1125 self._registerButton.connect("clicked", self._registerClicked)
1126
1127 self._updateButtons()
1128
1129 def activate(self):
1130 """Setup the route from the booked flight."""
1131 self._yearOfBirth.set_value(0)
1132 self._yearOfBirth.set_text("")
1133 self._updateButtons()
1134
1135 def _updateButtons(self, widget = None):
1136 """Update the sensitive state of the buttons"""
1137 name = self._name.get_text()
1138 nameLength = len(name)
1139 nameSpacePosition = name.find(" ")
1140
1141 yearOfBirthText = self._yearOfBirth.get_text()
1142 yearOfBirth = int(yearOfBirthText) if yearOfBirthText else 0
1143
1144 emailAddressText = self._emailAddress.get_text()
1145 emailAddressMatch = RegisterPage._emailAddressRE.match(emailAddressText)
1146
1147 vatsimID = self._vatsimID.get_int()
1148 ivaoID = self._ivaoID.get_int()
1149
1150 password = self._password.get_text()
1151 password2 = self._password2.get_text()
1152 if not password:
1153 self._passwordStatus.set_text("")
1154 elif len(password)<5:
1155 self._passwordStatus.set_text(xstr("register_password_too_short"))
1156 else:
1157 self._passwordStatus.set_text(xstr("register_password_ok"))
1158 self._passwordStatus.set_use_markup(True)
1159
1160 if len(password)<5 or not password2:
1161 self._password2Status.set_text("")
1162 elif password!=password2:
1163 self._password2Status.set_text(xstr("register_password_mismatch"))
1164 else:
1165 self._password2Status.set_text(xstr("register_password_ok"))
1166 self._password2Status.set_use_markup(True)
1167
1168 sensitive = \
1169 nameLength >= 5 and nameSpacePosition != -1 and \
1170 nameSpacePosition<(nameLength-2) and \
1171 yearOfBirth>=RegisterPage._minYearOfBirth and \
1172 yearOfBirth<=RegisterPage._maxYearOfBirth and \
1173 emailAddressMatch is not None and \
1174 (vatsimID>=800000 or ivaoID>=100000) and \
1175 len(password)>=5 and password==password2
1176
1177 self._registerButton.set_sensitive(sensitive)
1178
1179 def _cancelClicked(self, button):
1180 """Called when the Cancel button is clicked."""
1181 self.goBack()
1182
1183 def _registerClicked(self, button):
1184 """Called when the Register button is clicked."""
1185
1186#-----------------------------------------------------------------------------
1187
1188class StudentPage(Page):
1189 """A page displayed to students after logging in."""
1190 def __init__(self, wizard):
1191 """Construct the student page."""
1192 super(StudentPage, self).__init__(wizard, "student",
1193 xstr("student_title"),
1194 xstr("student_help"))
1195
1196#-----------------------------------------------------------------------------
1197
1198class ConnectPage(Page):
1199 """Page which displays the departure airport and gate (if at LHBP)."""
1200 def __init__(self, wizard):
1201 """Construct the connect page."""
1202 help = "Load the aircraft below into the simulator and park it\n" \
1203 "at the given airport, at the gate below, if present.\n\n" \
1204 "Then press the Connect button to connect to the simulator."
1205 completedHelp = "The basic data of your flight can be read below."
1206 super(ConnectPage, self).__init__(wizard, "connect",
1207 xstr("connect_title"),
1208 xstr("connect_help"),
1209 completedHelp = xstr("connect_chelp"))
1210
1211 self._selectSimulator = os.name=="nt" or "FORCE_SELECT_SIM" in os.environ
1212
1213 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1214 xscale = 0.0, yscale = 0.0)
1215
1216 table = gtk.Table(7 if self._selectSimulator else 5, 2)
1217 table.set_row_spacings(4)
1218 table.set_col_spacings(16)
1219 table.set_homogeneous(True)
1220 alignment.add(table)
1221 self.setMainWidget(alignment)
1222
1223 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1224 label = gtk.Label(xstr("connect_flightno"))
1225 labelAlignment.add(label)
1226 table.attach(labelAlignment, 0, 1, 0, 1)
1227
1228 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1229 self._flightNumber = gtk.Label()
1230 self._flightNumber.set_width_chars(9)
1231 self._flightNumber.set_alignment(0.0, 0.5)
1232 labelAlignment.add(self._flightNumber)
1233 table.attach(labelAlignment, 1, 2, 0, 1)
1234
1235 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1236 label = gtk.Label(xstr("connect_acft"))
1237 labelAlignment.add(label)
1238 table.attach(labelAlignment, 0, 1, 1, 2)
1239
1240 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1241 self._aircraft = gtk.Label()
1242 self._aircraft.set_width_chars(25)
1243 self._aircraft.set_alignment(0.0, 0.5)
1244 labelAlignment.add(self._aircraft)
1245 table.attach(labelAlignment, 1, 2, 1, 2)
1246
1247 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1248 label = gtk.Label(xstr("connect_tailno"))
1249 labelAlignment.add(label)
1250 table.attach(labelAlignment, 0, 1, 2, 3)
1251
1252 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1253 self._tailNumber = gtk.Label()
1254 self._tailNumber.set_width_chars(10)
1255 self._tailNumber.set_alignment(0.0, 0.5)
1256 labelAlignment.add(self._tailNumber)
1257 table.attach(labelAlignment, 1, 2, 2, 3)
1258
1259 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1260 label = gtk.Label(xstr("connect_airport"))
1261 labelAlignment.add(label)
1262 table.attach(labelAlignment, 0, 1, 3, 4)
1263
1264 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1265 self._departureICAO = gtk.Label()
1266 self._departureICAO.set_width_chars(6)
1267 self._departureICAO.set_alignment(0.0, 0.5)
1268 labelAlignment.add(self._departureICAO)
1269 table.attach(labelAlignment, 1, 2, 3, 4)
1270
1271 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1272 label = gtk.Label(xstr("connect_gate"))
1273 labelAlignment.add(label)
1274 table.attach(labelAlignment, 0, 1, 4, 5)
1275
1276 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1277 self._departureGate = gtk.Label()
1278 self._departureGate.set_width_chars(5)
1279 self._departureGate.set_alignment(0.0, 0.5)
1280 labelAlignment.add(self._departureGate)
1281 table.attach(labelAlignment, 1, 2, 4, 5)
1282
1283 if self._selectSimulator:
1284 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0, yalign=0.5)
1285 label = gtk.Label(xstr("connect_sim"))
1286 labelAlignment.add(label)
1287 table.attach(labelAlignment, 0, 1, 5, 7)
1288
1289 selectAlignment = gtk.Alignment(xalign=0.0, xscale=0.0, yalign=0.5)
1290
1291 selectBox = gtk.HBox()
1292 if pygobject:
1293 self._selectMSFS = \
1294 gtk.RadioButton.new_with_mnemonic_from_widget(None,
1295 xstr("connect_sim_msfs"))
1296 else:
1297 self._selectMSFS = gtk.RadioButton(None,
1298 xstr("connect_sim_msfs"))
1299
1300 selectBox.pack_start(self._selectMSFS, False, False, 0);
1301
1302 if pygobject:
1303 self._selectXPlane = \
1304 gtk.RadioButton.new_with_mnemonic_from_widget(self._selectMSFS,
1305 xstr("connect_sim_xplane"))
1306 else:
1307 self._selectXPlane = gtk.RadioButton(self._selectMSFS,
1308 xstr("connect_sim_xplane"))
1309
1310 selectBox.pack_start(self._selectXPlane, False, False, 8);
1311
1312 selectAlignment.add(selectBox)
1313 table.attach(selectAlignment, 1, 2, 5, 7)
1314
1315
1316 self.addCancelFlightButton()
1317
1318 self.addPreviousButton(clicked = self._backClicked)
1319
1320 self._button = self.addButton(xstr("button_connect"), default = True,
1321 tooltip = xstr("button_connect_tooltip"))
1322 self._clickedID = self._button.connect("clicked", self._connectClicked)
1323
1324 def activate(self):
1325 """Setup the departure information."""
1326 self._button.set_label(xstr("button_connect"))
1327 self._button.set_use_underline(True)
1328 self._button.set_tooltip_text(xstr("button_connect_tooltip"))
1329 self._button.disconnect(self._clickedID)
1330 self._clickedID = self._button.connect("clicked", self._connectClicked)
1331
1332 bookedFlight = self._wizard._bookedFlight
1333
1334 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
1335
1336 aircraftType = aircraftNames[bookedFlight.aircraftType]
1337 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
1338
1339 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
1340
1341 icao = bookedFlight.departureICAO
1342 self._departureICAO.set_markup("<b>" + icao + "</b>")
1343 gate = self._wizard._departureGate
1344 if gate!="-":
1345 gate = "<b>" + gate + "</b>"
1346 self._departureGate.set_markup(gate)
1347
1348 if self._selectSimulator:
1349 config = self._wizard.gui.config
1350 self._selectMSFS.set_active(config.defaultMSFS)
1351 self._selectXPlane.set_active(not config.defaultMSFS)
1352
1353 def finalize(self):
1354 """Finalize the page."""
1355 self._button.set_label(xstr("button_next"))
1356 self._button.set_use_underline(True)
1357 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1358 self._button.disconnect(self._clickedID)
1359 self._clickedID = self._button.connect("clicked", self._forwardClicked)
1360
1361 def _backClicked(self, button):
1362 """Called when the Back button is pressed."""
1363 self.goBack()
1364
1365 def _connectClicked(self, button):
1366 """Called when the Connect button is pressed."""
1367 if self._selectSimulator:
1368 simulatorType = const.SIM_MSFS9 if self._selectMSFS.get_active() \
1369 else const.SIM_XPLANE10
1370 else:
1371 simulatorType = const.SIM_MSFS9 if os.name=="nt" \
1372 else const.SIM_XPLANE10
1373
1374 config = self._wizard.gui.config
1375 config.defaultMSFS = simulatorType == const.SIM_MSFS9
1376 config.save()
1377
1378 self._wizard._connectSimulator(simulatorType)
1379
1380 def _forwardClicked(self, button):
1381 """Called when the Forward button is pressed."""
1382 self._wizard.nextPage()
1383
1384#-----------------------------------------------------------------------------
1385
1386class PayloadPage(Page):
1387 """Page to allow setting up the payload."""
1388 def __init__(self, wizard):
1389 """Construct the page."""
1390 super(PayloadPage, self).__init__(wizard, "payload",
1391 xstr("payload_title"),
1392 xstr("payload_help"),
1393 completedHelp = xstr("payload_chelp"))
1394
1395 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1396 xscale = 0.0, yscale = 0.0)
1397
1398 table = gtk.Table(7, 3)
1399 table.set_row_spacings(4)
1400 table.set_col_spacings(16)
1401 table.set_homogeneous(False)
1402 alignment.add(table)
1403 self.setMainWidget(alignment)
1404
1405 label = gtk.Label(xstr("payload_crew"))
1406 label.set_use_underline(True)
1407 label.set_alignment(0.0, 0.5)
1408 table.attach(label, 0, 1, 0, 1)
1409
1410 self._numCrew = IntegerEntry(defaultValue = 0)
1411 self._numCrew.set_width_chars(6)
1412 self._numCrew.connect("integer-changed", self._weightChanged)
1413 self._numCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
1414 table.attach(self._numCrew, 1, 2, 0, 1)
1415 label.set_mnemonic_widget(self._numCrew)
1416
1417 label = gtk.Label(xstr("payload_pax"))
1418 label.set_use_underline(True)
1419 label.set_alignment(0.0, 0.5)
1420 table.attach(label, 0, 1, 1, 2)
1421
1422 self._numPassengers = IntegerEntry(defaultValue = 0)
1423 self._numPassengers.set_width_chars(6)
1424 self._numPassengers.connect("integer-changed", self._weightChanged)
1425 self._numPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
1426 table.attach(self._numPassengers, 1, 2, 1, 2)
1427 label.set_mnemonic_widget(self._numPassengers)
1428
1429 label = gtk.Label(xstr("payload_bag"))
1430 label.set_use_underline(True)
1431 label.set_alignment(0.0, 0.5)
1432 table.attach(label, 0, 1, 2, 3)
1433
1434 self._bagWeight = IntegerEntry(defaultValue = 0)
1435 self._bagWeight.set_width_chars(6)
1436 self._bagWeight.connect("integer-changed", self._weightChanged)
1437 self._bagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
1438 table.attach(self._bagWeight, 1, 2, 2, 3)
1439 label.set_mnemonic_widget(self._bagWeight)
1440
1441 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
1442
1443 label = gtk.Label(xstr("payload_cargo"))
1444 label.set_use_underline(True)
1445 label.set_alignment(0.0, 0.5)
1446 table.attach(label, 0, 1, 3, 4)
1447
1448 self._cargoWeight = IntegerEntry(defaultValue = 0)
1449 self._cargoWeight.set_width_chars(6)
1450 self._cargoWeight.connect("integer-changed", self._weightChanged)
1451 self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
1452 table.attach(self._cargoWeight, 1, 2, 3, 4)
1453 label.set_mnemonic_widget(self._cargoWeight)
1454
1455 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
1456
1457 label = gtk.Label(xstr("payload_mail"))
1458 label.set_use_underline(True)
1459 label.set_alignment(0.0, 0.5)
1460 table.attach(label, 0, 1, 4, 5)
1461
1462 self._mailWeight = IntegerEntry(defaultValue = 0)
1463 self._mailWeight.set_width_chars(6)
1464 self._mailWeight.connect("integer-changed", self._weightChanged)
1465 self._mailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
1466 table.attach(self._mailWeight, 1, 2, 4, 5)
1467 label.set_mnemonic_widget(self._mailWeight)
1468
1469 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
1470
1471 label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
1472 label.set_alignment(0.0, 0.5)
1473 label.set_use_markup(True)
1474 table.attach(label, 0, 1, 5, 6)
1475
1476 self._calculatedZFW = gtk.Label()
1477 self._calculatedZFW.set_width_chars(6)
1478 self._calculatedZFW.set_alignment(1.0, 0.5)
1479 table.attach(self._calculatedZFW, 1, 2, 5, 6)
1480
1481 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
1482
1483 self._zfwButton = gtk.Button(xstr("payload_fszfw"))
1484 self._zfwButton.set_use_underline(True)
1485 self._zfwButton.connect("clicked", self._zfwRequested)
1486 self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
1487 table.attach(self._zfwButton, 0, 1, 6, 7)
1488
1489 self._simulatorZFW = gtk.Label("-")
1490 self._simulatorZFW.set_width_chars(6)
1491 self._simulatorZFW.set_alignment(1.0, 0.5)
1492 table.attach(self._simulatorZFW, 1, 2, 6, 7)
1493 self._simulatorZFWValue = None
1494
1495 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
1496
1497 self.addCancelFlightButton()
1498 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1499 self._button = self.addNextButton(clicked = self._forwardClicked)
1500
1501 @property
1502 def numCrew(self):
1503 """The number of the crew members on the flight."""
1504 return self._numCrew.get_int()
1505
1506 @property
1507 def numPassengers(self):
1508 """The number of the passengers on the flight."""
1509 return self._numPassengers.get_int()
1510
1511 @property
1512 def bagWeight(self):
1513 """Get the bag weight entered."""
1514 return self._bagWeight.get_int()
1515
1516 @property
1517 def cargoWeight(self):
1518 """Get the cargo weight entered."""
1519 return self._cargoWeight.get_int()
1520
1521 @property
1522 def mailWeight(self):
1523 """Get the bag weight entered."""
1524 return self._mailWeight.get_int()
1525
1526 def activate(self):
1527 """Setup the information."""
1528 bookedFlight = self._wizard._bookedFlight
1529
1530 self._numCrew.set_int(bookedFlight.numCrew)
1531 self._numCrew.set_sensitive(True)
1532 self._numPassengers.set_int(bookedFlight.numPassengers)
1533 self._numPassengers.set_sensitive(True)
1534
1535 self._bagWeight.set_int(bookedFlight.bagWeight)
1536 self._bagWeight.set_sensitive(True)
1537 self._cargoWeight.set_int(bookedFlight.cargoWeight)
1538 self._cargoWeight.set_sensitive(True)
1539 self._mailWeight.set_int(bookedFlight.mailWeight)
1540 self._mailWeight.set_sensitive(True)
1541
1542 self._simulatorZFW.set_text("-")
1543 self._simulatorZFWValue = None
1544 self._zfwButton.set_sensitive(True)
1545 self._updateCalculatedZFW()
1546
1547 def finalize(self):
1548 """Finalize the payload page."""
1549 self._numCrew.set_sensitive(False)
1550 self._numPassengers.set_sensitive(False)
1551 self._bagWeight.set_sensitive(False)
1552 self._cargoWeight.set_sensitive(False)
1553 self._mailWeight.set_sensitive(False)
1554 self._wizard.gui.initializeWeightHelp()
1555
1556 def calculateZFW(self):
1557 """Calculate the ZFW value."""
1558 zfw = self._wizard.gui._flight.aircraft.dow
1559 zfw += (self._numCrew.get_int() + self._numPassengers.get_int()) * 82
1560 zfw += self._bagWeight.get_int()
1561 zfw += self._cargoWeight.get_int()
1562 zfw += self._mailWeight.get_int()
1563 return zfw
1564
1565 def _updateCalculatedZFW(self):
1566 """Update the calculated ZFW"""
1567 zfw = self.calculateZFW()
1568
1569 markupBegin = "<b>"
1570 markupEnd = "</b>"
1571 if self._simulatorZFWValue is not None and \
1572 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
1573 markupBegin += '<span foreground="red">'
1574 markupEnd = "</span>" + markupEnd
1575 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
1576
1577 def _weightChanged(self, entry, weight):
1578 """Called when one of the weight values or humanm counts has changed."""
1579 self._updateCalculatedZFW()
1580
1581 def _zfwRequested(self, button):
1582 """Called when the ZFW is requested from the simulator."""
1583 self._zfwButton.set_sensitive(False)
1584 self._backButton.set_sensitive(False)
1585 self._button.set_sensitive(False)
1586 gui = self._wizard.gui
1587 gui.beginBusy(xstr("payload_zfw_busy"))
1588 gui.simulator.requestZFW(self._handleZFW)
1589
1590 def _handleZFW(self, zfw):
1591 """Called when the ZFW value is retrieved."""
1592 gobject.idle_add(self._processZFW, zfw)
1593
1594 def _processZFW(self, zfw):
1595 """Process the given ZFW value received from the simulator."""
1596 self._wizard.gui.endBusy()
1597 self._zfwButton.set_sensitive(True)
1598 self._backButton.set_sensitive(True)
1599 self._button.set_sensitive(True)
1600 self._simulatorZFWValue = zfw
1601 self._simulatorZFW.set_text("%.0f" % (zfw,))
1602 self._updateCalculatedZFW()
1603
1604 def _forwardClicked(self, button):
1605 """Called when the forward button is clicked."""
1606 self._wizard.nextPage()
1607
1608 def _backClicked(self, button):
1609 """Called when the Back button is pressed."""
1610 self.goBack()
1611
1612#-----------------------------------------------------------------------------
1613
1614class TimePage(Page):
1615 """Page displaying the departure and arrival times and allows querying the
1616 current time from the flight simulator."""
1617 def __init__(self, wizard):
1618 super(TimePage, self).__init__(wizard, "time",
1619 xstr("time_title"),
1620 xstr("time_help"),
1621 completedHelp = xstr("time_chelp"))
1622
1623 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1624 xscale = 0.0, yscale = 0.0)
1625
1626 table = gtk.Table(3, 2)
1627 table.set_row_spacings(4)
1628 table.set_col_spacings(16)
1629 table.set_homogeneous(False)
1630 alignment.add(table)
1631 self.setMainWidget(alignment)
1632
1633 label = gtk.Label(xstr("time_departure"))
1634 label.set_alignment(0.0, 0.5)
1635 table.attach(label, 0, 1, 0, 1)
1636
1637 self._departure = gtk.Label()
1638 self._departure.set_alignment(0.0, 0.5)
1639 table.attach(self._departure, 1, 2, 0, 1)
1640
1641 label = gtk.Label(xstr("time_arrival"))
1642 label.set_alignment(0.0, 0.5)
1643 table.attach(label, 0, 1, 1, 2)
1644
1645 self._arrival = gtk.Label()
1646 self._arrival.set_alignment(0.0, 0.5)
1647 table.attach(self._arrival, 1, 2, 1, 2)
1648
1649 self._timeButton = gtk.Button(xstr("time_fs"))
1650 self._timeButton.set_use_underline(True)
1651 self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
1652 self._timeButton.connect("clicked", self._timeRequested)
1653 table.attach(self._timeButton, 0, 1, 2, 3)
1654
1655 self._simulatorTime = gtk.Label("-")
1656 self._simulatorTime.set_alignment(0.0, 0.5)
1657 table.attach(self._simulatorTime, 1, 2, 2, 3)
1658
1659 self.addCancelFlightButton()
1660
1661 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1662 self._button = self.addNextButton(clicked = self._forwardClicked)
1663
1664 def activate(self):
1665 """Activate the page."""
1666 self._timeButton.set_sensitive(True)
1667 bookedFlight = self._wizard._bookedFlight
1668 self._departure.set_text(str(bookedFlight.departureTime.time()))
1669 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
1670 self._simulatorTime.set_text("-")
1671
1672 def _timeRequested(self, button):
1673 """Request the time from the simulator."""
1674 self._timeButton.set_sensitive(False)
1675 self._backButton.set_sensitive(False)
1676 self._button.set_sensitive(False)
1677 self._wizard.gui.beginBusy(xstr("time_busy"))
1678 self._wizard.gui.simulator.requestTime(self._handleTime)
1679
1680 def _handleTime(self, timestamp):
1681 """Handle the result of a time retrieval."""
1682 gobject.idle_add(self._processTime, timestamp)
1683
1684 def _processTime(self, timestamp):
1685 """Process the given time."""
1686 self._wizard.gui.endBusy()
1687 self._timeButton.set_sensitive(True)
1688 self._backButton.set_sensitive(True)
1689 self._button.set_sensitive(True)
1690 tm = time.gmtime(timestamp)
1691 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
1692 self._simulatorTime.set_text(str(t))
1693
1694 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
1695 dt = self._wizard._bookedFlight.departureTime.time()
1696 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
1697 diff = dts-ts
1698
1699 markupBegin = ""
1700 markupEnd = ""
1701 if diff < 0:
1702 markupBegin = '<b><span foreground="red">'
1703 markupEnd = '</span></b>'
1704 elif diff < 3*60 or diff > 30*60:
1705 markupBegin = '<b><span foreground="orange">'
1706 markupEnd = '</span></b>'
1707
1708 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
1709
1710 def _backClicked(self, button):
1711 """Called when the Back button is pressed."""
1712 self.goBack()
1713
1714 def _forwardClicked(self, button):
1715 """Called when the forward button is clicked."""
1716 if not self._completed:
1717 gui = self._wizard.gui
1718 gui.beginBusy(xstr("fuel_get_busy"))
1719
1720 gui.simulator.getFuel(self._handleFuel)
1721 else:
1722 self._wizard.nextPage()
1723
1724 def _handleFuel(self, fuelData):
1725 """Callback for the fuel query operation."""
1726 gobject.idle_add(self._processFuel, fuelData)
1727
1728 def _processFuel(self, fuelData):
1729 """Process the given fuel data."""
1730 self._wizard.gui.endBusy()
1731 self._wizard._fuelData = fuelData
1732 self._wizard.nextPage()
1733
1734#-----------------------------------------------------------------------------
1735
1736class RoutePage(Page):
1737 """The page containing the route and the flight level."""
1738 def __init__(self, wizard):
1739 """Construct the page."""
1740 super(RoutePage, self).__init__(wizard, "route",
1741 xstr("route_title"),
1742 xstr("route_help"),
1743 completedHelp = xstr("route_chelp"))
1744
1745 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1746 xscale = 0.0, yscale = 0.0)
1747
1748 mainBox = gtk.VBox()
1749 alignment.add(mainBox)
1750 self.setMainWidget(alignment)
1751
1752 levelBox = gtk.HBox()
1753
1754 label = gtk.Label(xstr("route_level"))
1755 label.set_use_underline(True)
1756 levelBox.pack_start(label, True, True, 0)
1757
1758 self._cruiseLevel = gtk.SpinButton()
1759 self._cruiseLevel.set_increments(step = 10, page = 100)
1760 self._cruiseLevel.set_range(min = 0, max = 500)
1761 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
1762 self._cruiseLevel.set_numeric(True)
1763 self._cruiseLevel.connect("changed", self._cruiseLevelChanged)
1764 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
1765 label.set_mnemonic_widget(self._cruiseLevel)
1766
1767 levelBox.pack_start(self._cruiseLevel, False, False, 8)
1768
1769 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1770 xscale = 0.0, yscale = 0.0)
1771 alignment.add(levelBox)
1772
1773 mainBox.pack_start(alignment, False, False, 0)
1774
1775
1776 routeBox = gtk.VBox()
1777
1778 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1779 xscale = 0.0, yscale = 0.0)
1780 label = gtk.Label(xstr("route_route"))
1781 label.set_use_underline(True)
1782 alignment.add(label)
1783 routeBox.pack_start(alignment, True, True, 0)
1784
1785 routeWindow = gtk.ScrolledWindow()
1786 routeWindow.set_size_request(400, 80)
1787 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
1788 else gtk.SHADOW_IN)
1789 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1790 else gtk.POLICY_AUTOMATIC,
1791 gtk.PolicyType.AUTOMATIC if pygobject
1792 else gtk.POLICY_AUTOMATIC)
1793
1794 self._uppercasingRoute = False
1795
1796 self._route = gtk.TextView()
1797 self._route.set_tooltip_text(xstr("route_route_tooltip"))
1798 self._route.set_wrap_mode(WRAP_WORD)
1799 self._route.get_buffer().connect("changed", self._routeChanged)
1800 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
1801 routeWindow.add(self._route)
1802
1803 label.set_mnemonic_widget(self._route)
1804 routeBox.pack_start(routeWindow, True, True, 0)
1805
1806 mainBox.pack_start(routeBox, True, True, 8)
1807
1808 alternateBox = gtk.HBox()
1809
1810 label = gtk.Label(xstr("route_altn"))
1811 label.set_use_underline(True)
1812 alternateBox.pack_start(label, True, True, 0)
1813
1814 self._alternate = gtk.Entry()
1815 self._alternate.set_width_chars(6)
1816 self._alternate.connect("changed", self._alternateChanged)
1817 self._alternate.set_tooltip_text(xstr("route_altn_tooltip"))
1818 label.set_mnemonic_widget(self._alternate)
1819
1820 alternateBox.pack_start(self._alternate, False, False, 8)
1821
1822 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1823 xscale = 0.0, yscale = 0.0)
1824 alignment.add(alternateBox)
1825
1826 mainBox.pack_start(alignment, False, False, 0)
1827
1828 self.addCancelFlightButton()
1829
1830 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1831 self._button = self.addNextButton(clicked = self._forwardClicked)
1832
1833 @property
1834 def filedCruiseLevel(self):
1835 """Get the filed cruise level."""
1836 return self._cruiseLevel.get_value_as_int()
1837
1838 @property
1839 def route(self):
1840 """Get the route."""
1841 return self._getRoute()
1842
1843 @property
1844 def alternate(self):
1845 """Get the ICAO code of the alternate airport."""
1846 return self._alternate.get_text()
1847
1848 def activate(self):
1849 """Setup the route from the booked flight."""
1850 self._cruiseLevel.set_value(0)
1851 self._cruiseLevel.set_text("")
1852 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
1853 self._alternate.set_text("")
1854 self._updateForwardButton()
1855
1856 def _getRoute(self):
1857 """Get the text of the route."""
1858 buffer = self._route.get_buffer()
1859 return buffer.get_text(buffer.get_start_iter(),
1860 buffer.get_end_iter(), True)
1861
1862 def _updateForwardButton(self):
1863 """Update the sensitivity of the forward button."""
1864 cruiseLevelText = self._cruiseLevel.get_text()
1865 cruiseLevel = int(cruiseLevelText) if cruiseLevelText else 0
1866 alternate = self._alternate.get_text()
1867 self._button.set_sensitive(cruiseLevel>=50 and self._getRoute()!="" and
1868 len(alternate)==4)
1869
1870 def _cruiseLevelChanged(self, *arg):
1871 """Called when the cruise level has changed."""
1872 self._updateForwardButton()
1873
1874 def _routeChanged(self, textBuffer):
1875 """Called when the route has changed."""
1876 if not self._uppercasingRoute:
1877 self._updateForwardButton()
1878
1879 def _routeInserted(self, textBuffer, iter, text, length):
1880 """Called when new characters are inserted into the route.
1881
1882 It uppercases all characters."""
1883 if not self._uppercasingRoute:
1884 self._uppercasingRoute = True
1885
1886 iter1 = iter.copy()
1887 iter1.backward_chars(length)
1888 textBuffer.delete(iter, iter1)
1889
1890 textBuffer.insert(iter, text.upper())
1891
1892 self._uppercasingRoute = False
1893
1894 def _alternateChanged(self, entry):
1895 """Called when the alternate airport has changed."""
1896 entry.set_text(entry.get_text().upper())
1897 self._updateForwardButton()
1898
1899 def _backClicked(self, button):
1900 """Called when the Back button is pressed."""
1901 self.goBack()
1902
1903 def _forwardClicked(self, button):
1904 """Called when the Forward button is clicked."""
1905 if self._wizard.gui.flight.aircraft.simBriefData is None:
1906 self._wizard.usingSimBrief = False
1907 if self._wizard.gui.config.useSimBrief and \
1908 self._wizard.usingSimBrief is not False:
1909 self._wizard.jumpPage("simbrief_setup")
1910 else:
1911 self._wizard.usingSimBrief = False
1912 self._wizard.jumpPage("fuel")
1913
1914#-----------------------------------------------------------------------------
1915
1916class SimBriefCredentialsDialog(gtk.Dialog):
1917 """A dialog window to ask for SimBrief credentials."""
1918 def __init__(self, gui, userName, password, rememberPassword):
1919 """Construct the dialog."""
1920 super(SimBriefCredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
1921 xstr("simbrief_credentials_title"),
1922 gui.mainWindow,
1923 DIALOG_MODAL)
1924 self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
1925 self.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1926
1927 contentArea = self.get_content_area()
1928
1929 contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1930 xscale = 0.0, yscale = 0.0)
1931 contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
1932 padding_left = 8, padding_right = 8)
1933
1934 contentArea.pack_start(contentAlignment, False, False, 0)
1935
1936 contentVBox = gtk.VBox()
1937 contentAlignment.add(contentVBox)
1938
1939 label = gtk.Label(xstr("simbrief_login_failed"))
1940 label.set_alignment(0.0, 0.0)
1941
1942 contentVBox.pack_start(label, False, False, 0)
1943
1944 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1945 xscale = 0.0, yscale = 0.0)
1946 tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
1947 padding_left = 0, padding_right = 0)
1948
1949 table = gtk.Table(3, 2)
1950 table.set_row_spacings(4)
1951 table.set_col_spacings(16)
1952 table.set_homogeneous(False)
1953
1954 tableAlignment.add(table)
1955 contentVBox.pack_start(tableAlignment, True, True, 0)
1956
1957 label = gtk.Label(xstr("simbrief_username"))
1958 label.set_use_underline(True)
1959 label.set_alignment(0.0, 0.5)
1960 table.attach(label, 0, 1, 0, 1)
1961
1962 self._userName = gtk.Entry()
1963 self._userName.set_width_chars(16)
1964 #self._userName.connect("changed",
1965 # lambda button: self._updateForwardButton())
1966 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
1967 self._userName.set_text(userName)
1968 table.attach(self._userName, 1, 2, 0, 1)
1969 label.set_mnemonic_widget(self._userName)
1970
1971 label = gtk.Label(xstr("simbrief_password"))
1972 label.set_use_underline(True)
1973 label.set_alignment(0.0, 0.5)
1974 table.attach(label, 0, 1, 1, 2)
1975
1976 self._password = gtk.Entry()
1977 self._password.set_visibility(False)
1978 #self._password.connect("changed",
1979 # lambda button: self._updateForwardButton())
1980 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
1981 self._password.set_text(password)
1982 table.attach(self._password, 1, 2, 1, 2)
1983 label.set_mnemonic_widget(self._password)
1984
1985 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
1986 self._rememberButton.set_use_underline(True)
1987 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
1988 self._rememberButton.set_active(rememberPassword)
1989 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
1990
1991 @property
1992 def userName(self):
1993 """Get the user name entered."""
1994 return self._userName.get_text()
1995
1996 @property
1997 def password(self):
1998 """Get the password entered."""
1999 return self._password.get_text()
2000
2001 @property
2002 def rememberPassword(self):
2003 """Get whether the password is to be remembered."""
2004 return self._rememberButton.get_active()
2005
2006 def run(self):
2007 """Run the dialog."""
2008 self.show_all()
2009
2010 response = super(SimBriefCredentialsDialog, self).run()
2011
2012 self.hide()
2013
2014 return response
2015
2016#-----------------------------------------------------------------------------
2017
2018class SimBriefSetupPage(Page):
2019 """Page for setting up some parameters for SimBrief."""
2020 monthNum2Name = [
2021 "JAN",
2022 "FEB",
2023 "MAR",
2024 "APR",
2025 "MAY",
2026 "JUN",
2027 "JUL",
2028 "AUG",
2029 "SEP",
2030 "OCT",
2031 "NOV",
2032 "DEC"
2033 ]
2034
2035 progress2Message = {
2036 cef.SIMBRIEF_PROGRESS_SEARCHING_BROWSER: "simbrief_progress_searching_browser",
2037 cef.SIMBRIEF_PROGRESS_LOADING_FORM: "simbrief_progress_loading_form",
2038 cef.SIMBRIEF_PROGRESS_FILLING_FORM: "simbrief_progress_filling_form",
2039 cef.SIMBRIEF_PROGRESS_WAITING_LOGIN: "simbrief_progress_waiting_login",
2040 cef.SIMBRIEF_PROGRESS_LOGGING_IN: "simbrief_progress_logging_in",
2041 cef.SIMBRIEF_PROGRESS_WAITING_RESULT: "simbrief_progress_waiting_result",
2042 cef.SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING: "simbrief_progress_retrieving_briefing"
2043 }
2044
2045 result2Message = {
2046 cef.SIMBRIEF_RESULT_ERROR_OTHER: "simbrief_result_error_other",
2047 cef.SIMBRIEF_RESULT_ERROR_NO_FORM: "simbrief_result_error_no_form",
2048 cef.SIMBRIEF_RESULT_ERROR_NO_POPUP: "simbrief_result_error_no_popup",
2049 cef.SIMBRIEF_RESULT_ERROR_LOGIN_FAILED: "simbrief_result_error_login_failed"
2050 }
2051
2052 @staticmethod
2053 def getHTMLFilePath():
2054 """Get the path of the HTML file to contain the generated flight
2055 plan."""
2056 if os.name=="nt":
2057 return os.path.join(tempfile.gettempdir(),
2058 "mlx_simbrief" +
2059 (".secondary" if secondaryInstallation else "") +
2060 ".html")
2061 else:
2062 import pwd
2063 return os.path.join(tempfile.gettempdir(),
2064 "mlx_simbrief." + pwd.getpwuid(os.getuid())[0] + "" +
2065 (".secondary" if secondaryInstallation else "") +
2066 ".html")
2067
2068 def __init__(self, wizard):
2069 """Construct the setup page."""
2070
2071 super(SimBriefSetupPage, self).__init__(wizard, "simbrief_setup",
2072 xstr("simbrief_setup_title"),
2073 xstr("simbrief_setup_help"),
2074 xstr("simbrief_setup_chelp"))
2075
2076 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2077 xscale = 0.0, yscale = 0.0)
2078
2079 table = gtk.Table(9, 3)
2080 table.set_row_spacings(4)
2081 table.set_col_spacings(16)
2082 table.set_homogeneous(False)
2083 alignment.add(table)
2084 self.setMainWidget(alignment)
2085
2086 label = gtk.Label(xstr("simbrief_username"))
2087 label.set_use_underline(True)
2088 label.set_alignment(0.0, 0.5)
2089 table.attach(label, 0, 1, 0, 1)
2090
2091 self._userName = gtk.Entry()
2092 self._userName.set_width_chars(16)
2093 self._userName.connect("changed",
2094 lambda button: self._updateForwardButton())
2095 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
2096 table.attach(self._userName, 1, 2, 0, 1)
2097 label.set_mnemonic_widget(self._userName)
2098
2099 label = gtk.Label(xstr("simbrief_password"))
2100 label.set_use_underline(True)
2101 label.set_alignment(0.0, 0.5)
2102 table.attach(label, 0, 1, 1, 2)
2103
2104 self._password = gtk.Entry()
2105 self._password.set_visibility(False)
2106 self._password.connect("changed",
2107 lambda button: self._updateForwardButton())
2108 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
2109 table.attach(self._password, 1, 2, 1, 2)
2110 label.set_mnemonic_widget(self._password)
2111
2112 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
2113 self._rememberButton.set_use_underline(True)
2114 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
2115 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
2116
2117 self._credentialsCondition = threading.Condition()
2118 self._credentialsAvailable = False
2119 self._credentialsUserName = None
2120 self._credentialsPassword = None
2121
2122 label = gtk.Label(xstr("simbrief_extra_fuel"))
2123 label.set_use_underline(True)
2124 label.set_alignment(0.0, 0.5)
2125 table.attach(label, 0, 1, 3, 4)
2126
2127 self._extraFuel = IntegerEntry(defaultValue = 0)
2128 self._extraFuel.set_width_chars(6)
2129 self._extraFuel.set_tooltip_text(xstr("simbrief_extra_fuel_tooltip"))
2130 table.attach(self._extraFuel, 1, 2, 3, 4)
2131 label.set_mnemonic_widget(self._extraFuel)
2132
2133 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
2134
2135 label = gtk.Label(xstr("simbrief_takeoff_runway"))
2136 label.set_use_underline(True)
2137 label.set_alignment(0.0, 0.5)
2138 table.attach(label, 0, 1, 4, 5)
2139
2140 self._takeoffRunway = gtk.Entry()
2141 self._takeoffRunway.set_width_chars(10)
2142 self._takeoffRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
2143 self._takeoffRunway.connect("changed", self._upperChanged)
2144 table.attach(self._takeoffRunway, 1, 2, 4, 5)
2145 label.set_mnemonic_widget(self._takeoffRunway)
2146
2147 label = gtk.Label(xstr("simbrief_landing_runway"))
2148 label.set_use_underline(True)
2149 label.set_alignment(0.0, 0.5)
2150 table.attach(label, 0, 1, 5, 6)
2151
2152 self._landingRunway = gtk.Entry()
2153 self._landingRunway.set_width_chars(10)
2154 self._landingRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
2155 self._landingRunway.connect("changed", self._upperChanged)
2156 table.attach(self._landingRunway, 1, 2, 5, 6)
2157 label.set_mnemonic_widget(self._landingRunway)
2158
2159 label = gtk.Label(xstr("simbrief_climb_profile"))
2160 label.set_use_underline(True)
2161 label.set_alignment(0.0, 0.5)
2162 table.attach(label, 0, 1, 6, 7)
2163
2164 self._climbProfile = gtk.ComboBox()
2165 renderer = gtk.CellRendererText()
2166 self._climbProfile.pack_start(renderer, True)
2167 self._climbProfile.add_attribute(renderer, "text", 0)
2168 self._climbProfile.set_tooltip_text(xstr("simbrief_climb_profile_tooltip"))
2169 table.attach(self._climbProfile, 1, 2, 6, 7)
2170 label.set_mnemonic_widget(self._climbProfile)
2171
2172 label = gtk.Label(xstr("simbrief_cruise_profile"))
2173 label.set_use_underline(True)
2174 label.set_alignment(0.0, 0.5)
2175 table.attach(label, 0, 1, 7, 8)
2176
2177 self._cruiseProfile = gtk.ComboBox()
2178 renderer = gtk.CellRendererText()
2179 self._cruiseProfile.pack_start(renderer, True)
2180 self._cruiseProfile.add_attribute(renderer, "text", 0)
2181 self._cruiseProfile.set_tooltip_text(xstr("simbrief_cruise_profile_tooltip"))
2182 table.attach(self._cruiseProfile, 1, 2, 7, 8)
2183 label.set_mnemonic_widget(self._cruiseProfile)
2184
2185 label = gtk.Label(xstr("simbrief_descent_profile"))
2186 label.set_use_underline(True)
2187 label.set_alignment(0.0, 0.5)
2188 table.attach(label, 0, 1, 8, 9)
2189
2190 self._descentProfile = gtk.ComboBox()
2191 renderer = gtk.CellRendererText()
2192 self._descentProfile.pack_start(renderer, True)
2193 self._descentProfile.add_attribute(renderer, "text", 0)
2194 self._descentProfile.set_tooltip_text(xstr("simbrief_descent_profile_tooltip"))
2195 table.attach(self._descentProfile, 1, 2, 8, 9)
2196 label.set_mnemonic_widget(self._descentProfile)
2197
2198 self.addCancelFlightButton()
2199
2200 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2201 self._button = self.addNextButton(clicked = self._forwardClicked)
2202
2203 def activate(self):
2204 """Activate the SimBrief setup page"""
2205 config = self._wizard.gui.config
2206
2207 self._userName.set_text(config.simBriefUserName)
2208 self._userName.set_sensitive(True)
2209
2210 self._password.set_text(config.simBriefPassword)
2211 self._password.set_sensitive(True)
2212
2213 self._rememberButton.set_active(config.rememberSimBriefPassword)
2214 self._rememberButton.set_sensitive(True)
2215
2216 self._extraFuel.set_int(0)
2217 self._extraFuel.set_sensitive(True)
2218
2219 self._takeoffRunway.set_text("")
2220 self._takeoffRunway.set_sensitive(True)
2221
2222 self._landingRunway.set_text("")
2223 self._landingRunway.set_sensitive(True)
2224
2225 simBriefData = self._wizard.gui.flight.aircraft.simBriefData
2226 for (control, profiles) in [(self._climbProfile,
2227 simBriefData.climbProfiles),
2228 (self._cruiseProfile,
2229 simBriefData.cruiseProfiles),
2230 (self._descentProfile,
2231 simBriefData.descentProfiles)]:
2232 model = gtk.ListStore(str)
2233 for profile in profiles:
2234 model.append([profile])
2235 control.set_model(model)
2236 control.set_sensitive(True)
2237
2238 self._climbProfile.set_active(0)
2239 self._cruiseProfile.set_active(0)
2240 self._descentProfile.set_active(0)
2241
2242 self._updateForwardButton()
2243
2244 def _updateForwardButton(self):
2245 """Update the sensitivity of the forward button."""
2246 self._button.set_sensitive(len(self._userName.get_text())>0 and
2247 len(self._password.get_text())>0)
2248
2249 def _backClicked(self, button):
2250 """Called when the Back button is pressed."""
2251 self.goBack()
2252
2253 def _forwardClicked(self, button):
2254 if self._completed:
2255 self._wizard.nextPage()
2256 else:
2257 config = self._wizard.gui.config
2258
2259 config.simBriefUserName = self._userName.get_text()
2260
2261 rememberPassword = self._rememberButton.get_active()
2262 config.simBriefPassword = \
2263 self._password.get_text() if rememberPassword else ""
2264 config.rememberSimBriefPassword = rememberPassword
2265
2266 config.save()
2267
2268 plan = self._getPlan()
2269 print "plan:", plan
2270
2271 takeoffRunway = self._takeoffRunway.get_text()
2272 if takeoffRunway:
2273 self._wizard.takeoffRunway = takeoffRunway
2274
2275 landingRunway = self._landingRunway.get_text()
2276 if landingRunway:
2277 self._wizard.landingRunway = landingRunway
2278
2279 self._userName.set_sensitive(False)
2280 self._password.set_sensitive(False)
2281 self._rememberButton.set_sensitive(False)
2282 self._extraFuel.set_sensitive(False)
2283 self._takeoffRunway.set_sensitive(False)
2284 self._landingRunway.set_sensitive(False)
2285
2286 self._climbProfile.set_sensitive(False)
2287 self._cruiseProfile.set_sensitive(False)
2288 self._descentProfile.set_sensitive(False)
2289
2290 self._wizard.gui.beginBusy(xstr("simbrief_calling"))
2291
2292 cef.startFastTimeout()
2293 cef.callSimBrief(plan,
2294 self._getCredentialsCallback,
2295 self._simBriefProgressCallback,
2296 SimBriefSetupPage.getHTMLFilePath())
2297
2298 startSound(const.SOUND_NOTAM)
2299
2300 def _getCredentialsCallback(self, count):
2301 """Called when the SimBrief home page requests the credentials."""
2302 with self._credentialsCondition:
2303 self._credentialsAvailable = False
2304
2305 gobject.idle_add(self._getCredentials, count)
2306
2307 while not self._credentialsAvailable:
2308 self._credentialsCondition.wait()
2309
2310 return (self._credentialsUserName, self._credentialsPassword)
2311
2312 def _getCredentials(self, count):
2313 """Get the credentials.
2314
2315 If count is 0, the user name and password entered into the setup page
2316 are returned. Otherwise a dialog box is displayed informing the user of
2317 invalid credentials and requesting another set of them."""
2318 with self._credentialsCondition:
2319 if count==0:
2320 self._credentialsUserName = self._userName.get_text()
2321 self._credentialsPassword = self._password.get_text()
2322 else:
2323 gui = self._wizard.gui
2324 config = gui.config
2325
2326 dialog = SimBriefCredentialsDialog(gui,
2327 config.simBriefUserName,
2328 config.simBriefPassword,
2329 config.rememberSimBriefPassword)
2330 response = dialog.run()
2331
2332 if response==RESPONSETYPE_OK:
2333 self._credentialsUserName = dialog.userName
2334 self._userName.set_text(self._credentialsUserName)
2335 self._credentialsPassword = dialog.password
2336 self._password.set_text(self._credentialsPassword)
2337 rememberPassword = dialog.rememberPassword
2338
2339 config.simBriefUserName = self._credentialsUserName
2340
2341 config.simBriefPassword = \
2342 self._credentialsPassword if rememberPassword else ""
2343 config.rememberSimBriefPassword = rememberPassword
2344
2345 config.save()
2346 else:
2347 self._credentialsUserName = None
2348 self._credentialsPassword = None
2349
2350 self._credentialsAvailable = True
2351 self._credentialsCondition.notify()
2352
2353 def _simBriefProgressCallback(self, progress, result, flightInfo):
2354 """Called by the SimBrief handling thread."""
2355 gobject.idle_add(self._simBriefProgress, progress, result, flightInfo)
2356
2357 def _simBriefProgress(self, progress, result, flightInfo):
2358 """The real SimBrief progress handler."""
2359 print "_simBriefProgress", progress, result, flightInfo
2360 if result==cef.SIMBRIEF_RESULT_NONE:
2361 message = SimBriefSetupPage.progress2Message.get(progress,
2362 "simbrief_progress_unknown")
2363 self._wizard.gui.updateBusyState(xstr(message))
2364 else:
2365 cef.stopFastTimeout()
2366 self._wizard.gui.endBusy()
2367
2368 if result==cef.SIMBRIEF_RESULT_OK:
2369 self._wizard.departureMETARChanged(flightInfo["orig_metar"],
2370 self)
2371 self._wizard.arrivalMETARChanged(flightInfo["dest_metar"], self)
2372 self._wizard.nextPage()
2373 else:
2374 message = SimBriefSetupPage.result2Message.get(result,
2375 "simbrief_result_unknown")
2376 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
2377 type = MESSAGETYPE_ERROR,
2378 message_format =
2379 xstr(message) + "\n"+
2380 xstr("simbrief_cancelled"))
2381
2382 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2383 dialog.set_title(WINDOW_TITLE_BASE)
2384 secondary = xstr("flightsel_save_failed_sec")
2385 dialog.format_secondary_markup(secondary)
2386 dialog.run()
2387 dialog.hide()
2388
2389 self._wizard.usingSimBrief = False
2390 self._wizard.jumpPage("fuel", fromPageShift = 1)
2391
2392 def _getPlan(self):
2393 """Get the flight plan data for SimBrief."""
2394 plan = {
2395 "airline": "MAH",
2396 "selcal": "XXXX",
2397 "fuelfactor": "P000",
2398 "contpct": "0.05",
2399 "resvrule": "45",
2400 "taxiout": "10",
2401 "taxiin": "10",
2402 "civalue": "AUTO"
2403 }
2404
2405 wizard = self._wizard
2406 gui = wizard.gui
2407
2408 loginResult = wizard.loginResult
2409 plan["cpt"] = loginResult.pilotName
2410 plan["pid"] = loginResult.pilotID
2411
2412 bookedFlight = wizard.bookedFlight
2413 plan["fltnum"] = wizard.bookedFlight.callsign[2:]
2414 plan["type"] = const.icaoCodes[bookedFlight.aircraftType]
2415 plan["orig"] = bookedFlight.departureICAO
2416 plan["dest"] = bookedFlight.arrivalICAO
2417 plan["reg"] = bookedFlight.tailNumber
2418 plan["fin"] = bookedFlight.tailNumber[3:]
2419 plan["pax"] = str(bookedFlight.numPassengers)
2420
2421 departureTime = bookedFlight.departureTime
2422 plan["date"] = "%d%s%d" % (departureTime.day,
2423 SimBriefSetupPage.monthNum2Name[departureTime.month-1],
2424 departureTime.year%100)
2425 plan["deph"] = str(departureTime.hour)
2426 plan["depm"] = str(departureTime.minute)
2427
2428 arrivalTime = bookedFlight.arrivalTime
2429 plan["steh"] = str(arrivalTime.hour)
2430 plan["stem"] = str(arrivalTime.minute)
2431
2432 plan["manualzfw"] = str(wizard.zfw / 1000.0)
2433 plan["cargo"] = str((wizard.bagWeight + wizard.cargoWeight + wizard.mailWeight)/1000.0)
2434
2435 plan["route"] = wizard.route
2436 plan["fl"] = str(wizard.filedCruiseAltitude)
2437 plan["altn"] = wizard.alternate
2438
2439 plan["addedfuel"] = str(self._extraFuel.get_int() / 1000.0)
2440 plan["origrwy"] = self._takeoffRunway.get_text()
2441 plan["destrwy"] = self._landingRunway.get_text()
2442
2443 for (key, control) in [("climb", self._climbProfile),
2444 ("cruise", self._cruiseProfile),
2445 ("descent", self._descentProfile)]:
2446 model = control.get_model()
2447 active = control.get_active_iter()
2448 value = model.get_value(active, 0)
2449 plan[key] = value
2450
2451 return plan
2452
2453 def _upperChanged(self, entry, arg = None):
2454 """Called when the value of some entry widget has changed and the value
2455 should be converted to uppercase."""
2456 entry.set_text(entry.get_text().upper())
2457
2458#-----------------------------------------------------------------------------
2459
2460class SimBriefingPage(Page):
2461 """Page to display the SimBrief HTML briefing."""
2462 class BrowserLifeSpanHandler(object):
2463 """The life-span handler of a browser."""
2464 def __init__(self, simBriefingPage):
2465 """Construct the life-span handler for the given page."""
2466 self._simBriefingPage = simBriefingPage
2467
2468 def OnBeforeClose(self, browser):
2469 """Called before closing the browser."""
2470 self._simBriefingPage._invalidateBrowser()
2471
2472 def __init__(self, wizard):
2473 """Construct the setup page."""
2474
2475 super(SimBriefingPage, self).__init__(wizard, "simbrief_result",
2476 xstr("simbrief_result_title"), "")
2477
2478 self._alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2479 xscale = 1.0, yscale = 1.0)
2480
2481 self._container = cef.getContainer()
2482 self._alignment.add(self._container)
2483
2484 self.setMainWidget(self._alignment)
2485
2486 self._browser = None
2487
2488 self.addCancelFlightButton()
2489
2490 self.addPreviousButton(clicked = self._backClicked)
2491
2492 self._button = self.addNextButton(clicked = self._forwardClicked)
2493 self._button.set_label(xstr("briefing_button"))
2494 self._button.set_has_tooltip(False)
2495 self._button.set_use_stock(False)
2496
2497 def activate(self):
2498 """Activate the SimBrief flight plan page"""
2499 if self._browser is None:
2500 self._startBrowser()
2501 else:
2502 self._browser.Reload()
2503
2504 def grabDefault(self):
2505 """If the page has a default button, make it the default one."""
2506 super(SimBriefingPage, self).grabDefault()
2507
2508 if self._browser is None:
2509 self._startBrowser()
2510
2511 def _backClicked(self, button):
2512 """Called when the Back button has been pressed."""
2513 self.goBack()
2514
2515 def _forwardClicked(self, button):
2516 """Called when the Forward button has been pressed."""
2517 if not self._completed:
2518 self._button.set_label(xstr("button_next"))
2519 self._button.set_tooltip_text(xstr("button_next_tooltip"))
2520 self._wizard.usingSimBrief = True
2521 self.complete()
2522
2523 self._wizard.nextPage()
2524
2525 def _startBrowser(self):
2526 """Start the browser.
2527
2528 If a container is needed, create one."""
2529 if self._container is None:
2530 self._container = cef.getContainer()
2531 self._alignment.add(self._container)
2532
2533 url = "file://" + SimBriefSetupPage.getHTMLFilePath()
2534 self._browser = cef.startInContainer(self._container, url)
2535
2536 lifeSpanHandler = SimBriefingPage.BrowserLifeSpanHandler(self)
2537 self._browser.SetClientHandler(lifeSpanHandler)
2538
2539 def _invalidateBrowser(self):
2540 """Invalidate the browser (and associated stuff)."""
2541 self._alignment.remove(self._container)
2542 self._container = None
2543 self._browser = None
2544
2545#-----------------------------------------------------------------------------
2546
2547class FuelTank(gtk.VBox):
2548 """Widget for the fuel tank."""
2549 def __init__(self, fuelTank, name, capacity, currentWeight):
2550 """Construct the widget for the tank with the given name."""
2551 super(FuelTank, self).__init__()
2552
2553 self._enabled = True
2554 self.fuelTank = fuelTank
2555 self.capacity = capacity
2556 self.currentWeight = currentWeight
2557 self.expectedWeight = currentWeight
2558
2559 label = gtk.Label("<b>" + name + "</b>")
2560 label.set_use_markup(True)
2561 label.set_use_underline(True)
2562 label.set_justify(JUSTIFY_CENTER)
2563 label.set_alignment(0.5, 1.0)
2564 self.pack_start(label, False, False, 4)
2565
2566 self._tankFigure = gtk.EventBox()
2567 self._tankFigure.set_size_request(38, -1)
2568 self._tankFigure.set_visible_window(False)
2569 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
2570
2571 if pygobject:
2572 self._tankFigure.connect("draw", self._drawTankFigure)
2573 else:
2574 self._tankFigure.connect("expose_event", self._drawTankFigure)
2575 self._tankFigure.connect("button_press_event", self._buttonPressed)
2576 self._tankFigure.connect("motion_notify_event", self._motionNotify)
2577 self._tankFigure.connect("scroll-event", self._scrolled)
2578
2579 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2580 xscale = 0.0, yscale = 1.0)
2581 alignment.add(self._tankFigure)
2582
2583 self.pack_start(alignment, True, True, 4)
2584
2585 self._expectedButton = gtk.SpinButton()
2586 self._expectedButton.set_numeric(True)
2587 self._expectedButton.set_range(0, self.capacity)
2588 self._expectedButton.set_increments(10, 100)
2589 self._expectedButton.set_value(currentWeight)
2590 self._expectedButton.set_alignment(1.0)
2591 self._expectedButton.set_width_chars(5)
2592 self._expectedButton.connect("value-changed", self._expectedChanged)
2593
2594 label.set_mnemonic_widget(self._expectedButton)
2595
2596 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2597 xscale = 0.0, yscale = 1.0)
2598 alignment.add(self._expectedButton)
2599 self.pack_start(alignment, False, False, 4)
2600
2601 def setCurrent(self, currentWeight):
2602 """Set the current weight."""
2603 self.currentWeight = currentWeight
2604 self._redraw()
2605
2606 def isCorrect(self):
2607 """Determine if the contents of the fuel tank are as expected"""
2608 return abs(self.expectedWeight - self.currentWeight)<=1
2609
2610 def disable(self):
2611 """Disable the fuel tank."""
2612 self._expectedButton.set_sensitive(False)
2613 self._enabled = False
2614
2615 def _redraw(self):
2616 """Redraw the tank figure."""
2617 self._tankFigure.queue_draw()
2618
2619 def _drawTankFigure(self, tankFigure, eventOrContext):
2620 """Draw the tank figure."""
2621 triangleSize = 5
2622
2623 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
2624 (xOffset, yOffset) = (0, 0) if pygobject \
2625 else (tankFigure.allocation.x, tankFigure.allocation.y)
2626
2627 width = tankFigure.get_allocated_width() if pygobject \
2628 else tankFigure.allocation.width
2629 height = tankFigure.get_allocated_height() if pygobject \
2630 else tankFigure.allocation.height
2631
2632 rectangleX0 = triangleSize
2633 rectangleY0 = triangleSize
2634 rectangleX1 = width - 1 - triangleSize
2635 rectangleY1 = height - 1 - triangleSize
2636 rectangleLineWidth = 2.0
2637
2638 context.set_source_rgb(0.0, 0.0, 0.0)
2639 context.set_line_width(rectangleLineWidth)
2640 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
2641 yOffset + rectangleY0 + rectangleLineWidth/2,
2642 rectangleX1 - rectangleX0 - rectangleLineWidth,
2643 rectangleY1 - rectangleY0 - rectangleLineWidth)
2644 context.stroke()
2645
2646 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
2647 rectangleInnerRight = rectangleX1 - rectangleLineWidth
2648 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
2649 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
2650
2651 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
2652 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
2653
2654 context.set_source_rgb(1.0, 0.9, 0.6)
2655 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
2656 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
2657 context.rectangle(xOffset + rectangleInnerLeft,
2658 yOffset + rectangleInnerTop +
2659 rectangleInnerHeight - currentHeight,
2660 rectangleInnerWidth, currentHeight)
2661 context.fill()
2662
2663 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
2664 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
2665
2666 context.set_line_width(1.5)
2667 context.set_source_rgb(0.0, 0.85, 0.85)
2668 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
2669 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
2670 context.stroke()
2671
2672 context.set_line_width(0.0)
2673 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
2674 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
2675 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
2676 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
2677 context.fill()
2678
2679 context.set_line_width(0.0)
2680 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
2681 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
2682 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
2683 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
2684 context.fill()
2685
2686 return True
2687
2688 def _setExpectedFromY(self, y):
2689 """Set the expected weight from the given Y-coordinate."""
2690 level = (self._rectangleInnerBottom - y) / \
2691 (self._rectangleInnerBottom - self._rectangleInnerTop)
2692 level = min(1.0, max(0.0, level))
2693 self._expectedButton.set_value(level * self.capacity)
2694
2695 def _buttonPressed(self, tankFigure, event):
2696 """Called when a button is pressed in the figure.
2697
2698 The expected level will be set there."""
2699 if self._enabled and event.button==1:
2700 self._setExpectedFromY(event.y)
2701
2702 def _motionNotify(self, tankFigure, event):
2703 """Called when the mouse pointer moves within the area of a tank figure."""
2704 if self._enabled and event.state==BUTTON1_MASK:
2705 self._setExpectedFromY(event.y)
2706
2707 def _scrolled(self, tankFigure, event):
2708 """Called when a scroll event is received."""
2709 if self._enabled:
2710 increment = 1 if event.state==CONTROL_MASK \
2711 else 100 if event.state==SHIFT_MASK \
2712 else 10 if event.state==0 else 0
2713 if increment!=0:
2714 if event.direction==SCROLL_DOWN:
2715 increment *= -1
2716 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
2717
2718 def _expectedChanged(self, spinButton):
2719 """Called when the expected value has changed."""
2720 self.expectedWeight = spinButton.get_value_as_int()
2721 self._redraw()
2722
2723#-----------------------------------------------------------------------------
2724
2725class FuelPage(Page):
2726 """The page containing the fuel tank filling."""
2727 _pumpStep = 0.02
2728
2729 def __init__(self, wizard):
2730 """Construct the page."""
2731 super(FuelPage, self).__init__(wizard, "fuel",
2732 xstr("fuel_title"),
2733 xstr("fuel_help_pre") +
2734 xstr("fuel_help_post"),
2735 completedHelp = xstr("fuel_chelp"))
2736
2737 self._fuelTanks = []
2738 self._fuelTable = None
2739 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2740 xscale = 0.0, yscale = 1.0)
2741 self.setMainWidget(self._fuelAlignment)
2742
2743 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
2744 self._setupTanks(tankData)
2745
2746 self.addCancelFlightButton()
2747
2748 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2749 self._button = self.addNextButton(clicked = self._forwardClicked)
2750
2751 self._pumpIndex = 0
2752
2753 def activate(self):
2754 """Activate the page."""
2755 self._setupTanks(self._wizard._fuelData)
2756
2757 aircraft = self._wizard.gui.flight.aircraft
2758 minLandingFuel = aircraft.minLandingFuel
2759 recommendedLandingFuel = aircraft.recommendedLandingFuel
2760
2761 middleHelp = "" if minLandingFuel is None else \
2762 (xstr("fuel_help_min") % (minLandingFuel,)) \
2763 if recommendedLandingFuel is None else \
2764 (xstr("fuel_help_min_rec") % (minLandingFuel,
2765 recommendedLandingFuel))
2766 self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
2767
2768 def finalize(self):
2769 """Finalize the page."""
2770 for fuelTank in self._fuelTanks:
2771 fuelTank.disable()
2772
2773 def _backClicked(self, button):
2774 """Called when the Back button is pressed."""
2775 self.goBack()
2776
2777 def _forwardClicked(self, button):
2778 """Called when the forward button is clicked."""
2779 if not self._completed:
2780 self._pumpIndex = 0
2781 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
2782 self._pump()
2783 elif self._wizard.usingSimBrief:
2784 self._wizard.jumpPage("takeoff")
2785 else:
2786 self._wizard.jumpPage("briefing1")
2787
2788 def _setupTanks(self, tankData):
2789 """Setup the tanks for the given data."""
2790 numTanks = len(tankData)
2791 if self._fuelTable is not None:
2792 self._fuelAlignment.remove(self._fuelTable)
2793
2794 self._fuelTanks = []
2795 self._fuelTable = gtk.Table(numTanks, 1)
2796 self._fuelTable.set_col_spacings(16)
2797 index = 0
2798 for (tank, current, capacity) in tankData:
2799 fuelTank = FuelTank(tank,
2800 xstr("fuel_tank_" +
2801 const.fuelTank2string(tank)),
2802 capacity, current)
2803 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
2804 self._fuelTanks.append(fuelTank)
2805 index += 1
2806
2807 self._fuelAlignment.add(self._fuelTable)
2808 self.show_all()
2809
2810 def _pump(self):
2811 """Perform one step of pumping.
2812
2813 It is checked, if the current tank's contents are of the right
2814 quantity. If not, it is filled one step further to the desired
2815 contents. Otherwise the next tank is started. If all tanks are are
2816 filled, the next page is selected."""
2817 numTanks = len(self._fuelTanks)
2818
2819 fuelTank = None
2820 while self._pumpIndex < numTanks:
2821 fuelTank = self._fuelTanks[self._pumpIndex]
2822 if fuelTank.isCorrect():
2823 self._pumpIndex += 1
2824 fuelTank = None
2825 else:
2826 break
2827
2828 if fuelTank is None:
2829 self._wizard.gui.endBusy()
2830 if self._wizard.usingSimBrief:
2831 self._wizard.gui.startMonitoring()
2832 self._wizard.jumpPage("takeoff")
2833 else:
2834 bookedFlight = self._wizard._bookedFlight
2835 self._wizard.gui.beginBusy(xstr("route_down_notams"))
2836 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
2837 bookedFlight.departureICAO,
2838 bookedFlight.arrivalICAO)
2839 startSound(const.SOUND_NOTAM)
2840 else:
2841 currentLevel = fuelTank.currentWeight / fuelTank.capacity
2842 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
2843 if currentLevel<expectedLevel:
2844 currentLevel += FuelPage._pumpStep
2845 if currentLevel>expectedLevel: currentLevel = expectedLevel
2846 else:
2847 currentLevel -= FuelPage._pumpStep
2848 if currentLevel<expectedLevel: currentLevel = expectedLevel
2849 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
2850 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
2851 currentLevel)])
2852 gobject.timeout_add(50, self._pump)
2853
2854 def _notamsCallback(self, returned, result):
2855 """Callback for the NOTAMs."""
2856 gobject.idle_add(self._handleNOTAMs, returned, result)
2857
2858 def _handleNOTAMs(self, returned, result):
2859 """Handle the NOTAMs."""
2860 if returned:
2861 self._wizard._departureNOTAMs = result.departureNOTAMs
2862 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
2863 else:
2864 self._wizard._departureNOTAMs = None
2865 self._wizard._arrivalNOTAMs = None
2866
2867 bookedFlight = self._wizard._bookedFlight
2868 self._wizard.gui.beginBusy(xstr("route_down_metars"))
2869 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
2870 [bookedFlight.departureICAO,
2871 bookedFlight.arrivalICAO])
2872
2873 def _metarsCallback(self, returned, result):
2874 """Callback for the METARs."""
2875 gobject.idle_add(self._handleMETARs, returned, result)
2876
2877 def _handleMETARs(self, returned, result):
2878 """Handle the METARs."""
2879 self._wizard._departureMETAR = None
2880 self._wizard._arrivalMETAR = None
2881 bookedFlight = self._wizard._bookedFlight
2882 if returned:
2883 if bookedFlight.departureICAO in result.metars:
2884 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
2885 if bookedFlight.arrivalICAO in result.metars:
2886 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
2887
2888 self._wizard.gui.endBusy()
2889 self._backButton.set_sensitive(True)
2890 self._button.set_sensitive(True)
2891 self._wizard.nextPage()
2892
2893#-----------------------------------------------------------------------------
2894
2895class BriefingPage(Page):
2896 """Page for the briefing."""
2897 def __init__(self, wizard, departure):
2898 """Construct the briefing page."""
2899 self._departure = departure
2900
2901 number = 1 if departure else 2
2902
2903 title = xstr("briefing_title") % (number,
2904 xstr("briefing_departure")
2905 if departure
2906 else xstr("briefing_arrival"))
2907 super(BriefingPage, self).__init__(wizard,
2908 "briefing%d" % (number,),
2909 title, xstr("briefing_help"),
2910 completedHelp = xstr("briefing_chelp"))
2911
2912 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2913 xscale = 1.0, yscale = 1.0)
2914
2915 mainBox = gtk.VBox()
2916 alignment.add(mainBox)
2917 self.setMainWidget(alignment)
2918
2919 self._notamsFrame = gtk.Frame()
2920 self._notamsFrame.set_label(xstr("briefing_notams_init"))
2921 scrolledWindow = gtk.ScrolledWindow()
2922 scrolledWindow.set_size_request(-1, 128)
2923 # FIXME: these constants should be in common
2924 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2925 else gtk.POLICY_AUTOMATIC,
2926 gtk.PolicyType.AUTOMATIC if pygobject
2927 else gtk.POLICY_AUTOMATIC)
2928 self._notams = gtk.TextView()
2929 self._notams.set_editable(False)
2930 self._notams.set_accepts_tab(False)
2931 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
2932 scrolledWindow.add(self._notams)
2933 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2934 xscale = 1.0, yscale = 1.0)
2935 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2936 padding_left = 0, padding_right = 0)
2937 alignment.add(scrolledWindow)
2938 self._notamsFrame.add(alignment)
2939 mainBox.pack_start(self._notamsFrame, True, True, 4)
2940
2941 self._metarFrame = gtk.Frame()
2942 self._metarFrame.set_label(xstr("briefing_metar_init"))
2943 scrolledWindow = gtk.ScrolledWindow()
2944 scrolledWindow.set_size_request(-1, 32)
2945 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2946 else gtk.POLICY_AUTOMATIC,
2947 gtk.PolicyType.AUTOMATIC if pygobject
2948 else gtk.POLICY_AUTOMATIC)
2949
2950 self._updatingMETAR = False
2951
2952 self._metar = gtk.TextView()
2953 self._metar.set_accepts_tab(False)
2954 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
2955 self._metar.get_buffer().connect("changed", self._metarChanged)
2956 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
2957 scrolledWindow.add(self._metar)
2958 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2959 xscale = 1.0, yscale = 1.0)
2960 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2961 padding_left = 0, padding_right = 0)
2962 alignment.add(scrolledWindow)
2963 self._metarFrame.add(alignment)
2964 mainBox.pack_start(self._metarFrame, True, True, 4)
2965 self.metarEdited = False
2966
2967 self.addCancelFlightButton()
2968
2969 self.addPreviousButton(clicked = self._backClicked)
2970 self._button = self.addNextButton(clicked = self._forwardClicked)
2971
2972 @property
2973 def metar(self):
2974 """Get the METAR on the page."""
2975 buffer = self._metar.get_buffer()
2976 return buffer.get_text(buffer.get_start_iter(),
2977 buffer.get_end_iter(), True)
2978
2979 def setMETAR(self, metar):
2980 """Set the METAR."""
2981 self._metar.get_buffer().set_text(metar)
2982 self.metarEdited = False
2983
2984 def changeMETAR(self, metar):
2985 """Change the METAR as a result of an edit on one of the other
2986 pages."""
2987 self._updatingMETAR = True
2988 self._metar.get_buffer().set_text(metar)
2989 self._updatingMETAR = False
2990
2991 self._updateButton()
2992 self.metarEdited = True
2993
2994 def activate(self):
2995 """Activate the page."""
2996 if not self._departure:
2997 self._button.set_label(xstr("briefing_button"))
2998 self._button.set_has_tooltip(False)
2999 self._button.set_use_stock(False)
3000
3001 bookedFlight = self._wizard._bookedFlight
3002
3003 icao = bookedFlight.departureICAO if self._departure \
3004 else bookedFlight.arrivalICAO
3005 notams = self._wizard._departureNOTAMs if self._departure \
3006 else self._wizard._arrivalNOTAMs
3007 metar = self._wizard._departureMETAR if self._departure \
3008 else self._wizard._arrivalMETAR
3009
3010 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
3011 buffer = self._notams.get_buffer()
3012 if notams is None:
3013 buffer.set_text(xstr("briefing_notams_failed"))
3014 elif not notams:
3015 buffer.set_text(xstr("briefing_notams_missing"))
3016 else:
3017 s = ""
3018 for notam in notams:
3019 s += str(notam)
3020 s += "-------------------- * --------------------\n"
3021 buffer.set_text(s)
3022
3023 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
3024 buffer = self._metar.get_buffer()
3025 self._updatingMETAR = True
3026 if metar is None:
3027 buffer.set_text("")
3028 self.setHelp(xstr("briefing_help_nometar"))
3029 else:
3030 buffer.set_text(metar)
3031 self._updatingMETAR = False
3032 self._updateButton()
3033
3034 label = self._metarFrame.get_label_widget()
3035 label.set_use_underline(True)
3036 label.set_mnemonic_widget(self._metar)
3037
3038 self.metarEdited = False
3039
3040 def _backClicked(self, button):
3041 """Called when the Back button is pressed."""
3042 self.goBack()
3043
3044 def _forwardClicked(self, button):
3045 """Called when the forward button is clicked."""
3046 if not self._departure:
3047 if not self._completed:
3048 self._wizard.gui.startMonitoring()
3049 self._button.set_label(xstr("button_next"))
3050 self._button.set_tooltip_text(xstr("button_next_tooltip"))
3051 self.complete()
3052
3053 self._wizard.nextPage()
3054
3055 def _metarChanged(self, buffer):
3056 """Called when the METAR has changed."""
3057 print "BriefingPage.metarChanged", self._updatingMETAR
3058 if not self._updatingMETAR:
3059 self.metarEdited = True
3060 self._updateButton()
3061 metar = buffer.get_text(buffer.get_start_iter(),
3062 buffer.get_end_iter(), True)
3063 self._wizard.metarChanged(metar, self)
3064
3065 def _metarInserted(self, textBuffer, iter, text, length):
3066 """Called when new characters are inserted into the METAR.
3067
3068 It uppercases all characters."""
3069 print "BriefingPage.metarInserted", self._updatingMETAR
3070 if not self._updatingMETAR:
3071 self._updatingMETAR = True
3072
3073 iter1 = iter.copy()
3074 iter1.backward_chars(length)
3075 textBuffer.delete(iter, iter1)
3076
3077 textBuffer.insert(iter, text.upper())
3078
3079 self._updatingMETAR = False
3080
3081 def _updateButton(self):
3082 """Update the sensitivity of the Next button based on the contents of
3083 the METAR field."""
3084 buffer = self._metar.get_buffer()
3085 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
3086 buffer.get_end_iter(),
3087 True)!="")
3088
3089
3090#-----------------------------------------------------------------------------
3091
3092class TakeoffPage(Page):
3093 """Page for entering the takeoff data."""
3094 def __init__(self, wizard):
3095 """Construct the takeoff page."""
3096 super(TakeoffPage, self).__init__(wizard, "takeoff",
3097 xstr("takeoff_title"),
3098 xstr("takeoff_help"),
3099 completedHelp = xstr("takeoff_chelp"))
3100
3101 self._forwardAllowed = False
3102
3103 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3104 xscale = 0.0, yscale = 0.0)
3105
3106 table = gtk.Table(9, 24)
3107 table.set_row_spacings(4)
3108 table.set_col_spacings(16)
3109 table.set_homogeneous(False)
3110 alignment.add(table)
3111 self.setMainWidget(alignment)
3112
3113 row = 0
3114
3115 label = gtk.Label(xstr("takeoff_metar"))
3116 label.set_use_underline(True)
3117 label.set_alignment(0.0, 0.5)
3118 table.attach(label, 0, 1, row, row+1)
3119
3120 self._metar = gtk.Entry()
3121 self._metar.set_width_chars(40)
3122 self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
3123 self._metar.connect("changed", self._metarChanged)
3124 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3125 table.attach(self._metar, 1, 24, row, row+1)
3126 label.set_mnemonic_widget(self._metar)
3127
3128 self._updatingMETAR = False
3129
3130 row += 1
3131
3132 label = gtk.Label(xstr("takeoff_runway"))
3133 label.set_use_underline(True)
3134 label.set_alignment(0.0, 0.5)
3135 table.attach(label, 0, 1, row, row+1)
3136
3137 self._runway = gtk.Entry()
3138 self._runway.set_width_chars(10)
3139 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
3140 self._runway.connect("changed", self._upperChanged)
3141 table.attach(self._runway, 1, 3, row, row+1)
3142 label.set_mnemonic_widget(self._runway)
3143
3144 row += 1
3145
3146 label = gtk.Label(xstr("takeoff_sid"))
3147 label.set_use_underline(True)
3148 label.set_alignment(0.0, 0.5)
3149 table.attach(label, 0, 1, row, row+1)
3150
3151 if pygobject:
3152 self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
3153 else:
3154 self._sid = gtk.ComboBoxEntry(comboModel)
3155
3156 self._sid.set_entry_text_column(0)
3157 self._sid.get_child().set_width_chars(10)
3158 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
3159 self._sid.connect("changed", self._upperChangedComboBox)
3160 table.attach(self._sid, 1, 3, row, row+1)
3161 label.set_mnemonic_widget(self._sid)
3162
3163 row += 1
3164
3165 label = gtk.Label(xstr("takeoff_v1"))
3166 label.set_use_markup(True)
3167 label.set_use_underline(True)
3168 label.set_alignment(0.0, 0.5)
3169 table.attach(label, 0, 1, row, row+1)
3170
3171 self._v1 = IntegerEntry()
3172 self._v1.set_width_chars(4)
3173 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
3174 self._v1.connect("integer-changed", self._valueChanged)
3175 table.attach(self._v1, 2, 3, row, row+1)
3176 label.set_mnemonic_widget(self._v1)
3177
3178 self._v1Unit = gtk.Label(xstr("label_knots"))
3179 self._v1Unit.set_alignment(0.0, 0.5)
3180 table.attach(self._v1Unit, 3, 4, row, row+1)
3181
3182 row += 1
3183
3184 label = gtk.Label(xstr("takeoff_vr"))
3185 label.set_use_markup(True)
3186 label.set_use_underline(True)
3187 label.set_alignment(0.0, 0.5)
3188 table.attach(label, 0, 1, row, row+1)
3189
3190 self._vr = IntegerEntry()
3191 self._vr.set_width_chars(4)
3192 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
3193 self._vr.connect("integer-changed", self._valueChanged)
3194 table.attach(self._vr, 2, 3, row, row+1)
3195 label.set_mnemonic_widget(self._vr)
3196
3197 self._vrUnit = gtk.Label(xstr("label_knots"))
3198 self._vrUnit.set_alignment(0.0, 0.5)
3199 table.attach(self._vrUnit, 3, 4, row, row+1)
3200
3201 row += 1
3202
3203 label = gtk.Label(xstr("takeoff_v2"))
3204 label.set_use_markup(True)
3205 label.set_use_underline(True)
3206 label.set_alignment(0.0, 0.5)
3207 table.attach(label, 0, 1, row, row+1)
3208
3209 self._v2 = IntegerEntry()
3210 self._v2.set_width_chars(4)
3211 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
3212 self._v2.connect("integer-changed", self._valueChanged)
3213 table.attach(self._v2, 2, 3, row, row+1)
3214 label.set_mnemonic_widget(self._v2)
3215
3216 self._v2Unit = gtk.Label(xstr("label_knots"))
3217 self._v2Unit.set_alignment(0.0, 0.5)
3218 table.attach(self._v2Unit, 3, 4, row, row+1)
3219
3220 row += 1
3221
3222 self._derateType = acft.DERATE_NONE
3223
3224 self._derateLabel = gtk.Label()
3225 self._derateLabel.set_use_underline(True)
3226 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
3227 self._derateLabel.set_alignment(0.0, 0.5)
3228 table.attach(self._derateLabel, 0, 1, row, row+1)
3229
3230 self._derate = gtk.Alignment()
3231 table.attach(self._derate, 2, 4, row, row+1)
3232 self._derateWidget = None
3233 self._derateEntry = None
3234 self._derateUnit = None
3235 self._derateButtons = None
3236
3237 row += 1
3238
3239 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
3240 self._antiIceOn.set_use_underline(True)
3241 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
3242 table.attach(self._antiIceOn, 2, 4, row, row+1)
3243
3244 row += 1
3245
3246 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
3247 self._rto.set_use_underline(True)
3248 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
3249 self._rto.connect("toggled", self._rtoToggled)
3250 table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
3251
3252 self.addCancelFlightButton()
3253
3254 self.addPreviousButton(clicked = self._backClicked)
3255
3256 self._button = self.addNextButton(clicked = self._forwardClicked)
3257
3258 self._active = False
3259
3260 @property
3261 def runway(self):
3262 """Get the runway."""
3263 return self._runway.get_text()
3264
3265 @property
3266 def sid(self):
3267 """Get the SID."""
3268 text = self._sid.get_child().get_text()
3269 return text if self._sid.get_active()!=0 and text and text!="N/A" \
3270 else None
3271
3272 @property
3273 def v1(self):
3274 """Get the v1 speed."""
3275 return self._v1.get_int()
3276
3277 @property
3278 def vr(self):
3279 """Get the vr speed."""
3280 return self._vr.get_int()
3281
3282 @property
3283 def v2(self):
3284 """Get the v2 speed."""
3285 return self._v2.get_int()
3286
3287 @property
3288 def derate(self):
3289 """Get the derate value, if any."""
3290 if self._derateWidget is None:
3291 return None
3292 if self._derateType==acft.DERATE_BOEING:
3293 derate = self._derateEntry.get_text()
3294 return derate if derate else None
3295 elif self._derateType==acft.DERATE_EPR:
3296 derate = self._derateWidget.get_text()
3297 return derate if derate else None
3298 elif self._derateType==acft.DERATE_TUPOLEV:
3299 return acft.DERATE_TUPOLEV_NOMINAL \
3300 if self._derateButtons[0].get_active() \
3301 else acft.DERATE_TUPOLEV_TAKEOFF
3302 elif self._derateType==acft.DERATE_B462:
3303 return self._derateWidget.get_active()
3304 else:
3305 return None
3306
3307 @property
3308 def antiIceOn(self):
3309 """Get whether the anti-ice system has been turned on."""
3310 return self._antiIceOn.get_active()
3311
3312 @antiIceOn.setter
3313 def antiIceOn(self, value):
3314 """Set the anti-ice indicator."""
3315 self._antiIceOn.set_active(value)
3316
3317 @property
3318 def rtoIndicated(self):
3319 """Get whether the pilot has indicated if there was an RTO."""
3320 return self._rto.get_active()
3321
3322 def activate(self):
3323 """Activate the page."""
3324 print "TakeoffPage.activate"
3325
3326 self._updatingMETAR = True
3327 self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
3328 self._updatingMETAR = False
3329
3330 if self._wizard.takeoffRunway is None:
3331 self._runway.set_text("")
3332 else:
3333 self._runway.set_text(self._wizard.takeoffRunway)
3334 self._runway.set_sensitive(True)
3335 self._sid.set_active(0)
3336 self._sid.set_sensitive(True)
3337 self._v1.set_int(None)
3338 self._v1.set_sensitive(True)
3339 self._vr.set_int(None)
3340 self._vr.set_sensitive(True)
3341 self._v2.set_int(None)
3342 self._v2.set_sensitive(True)
3343
3344 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
3345 speedUnit = xstr("label" + i18nSpeedUnit)
3346 self._v1Unit.set_text(speedUnit)
3347 self._vrUnit.set_text(speedUnit)
3348 self._v2Unit.set_text(speedUnit)
3349
3350 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
3351 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
3352 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
3353
3354 self._derateType = self._wizard.gui.flight.aircraft.derateType
3355
3356 self._setupDerateWidget()
3357
3358 self._rto.set_active(False)
3359 self._rto.set_sensitive(False)
3360
3361 self._button.set_sensitive(False)
3362 self._forwardAllowed = False
3363
3364 self._active = True
3365
3366 def allowForward(self):
3367 """Allow going to the next page."""
3368 print "TakeoffPage.allowForward"
3369 self._forwardAllowed = True
3370 self._updateForwardButton()
3371
3372 def reset(self):
3373 """Reset the page if the wizard is reset."""
3374 print "TakeoffPage.reset"
3375
3376 super(TakeoffPage, self).reset()
3377 self._v1.reset()
3378 self._vr.reset()
3379 self._v2.reset()
3380 self._hasDerate = False
3381 self._antiIceOn.set_active(False)
3382 self._active = False
3383
3384 def setRTOEnabled(self, enabled):
3385 """Set the RTO checkbox enabled or disabled."""
3386 if not enabled:
3387 self._rto.set_active(False)
3388 self._rto.set_sensitive(enabled)
3389
3390 def changeMETAR(self, metar):
3391 """Change the METAR as a result of an edit on one of the other
3392 pages."""
3393 if self._active:
3394 print "TakeoffPage.changeMETAR"
3395 self._updatingMETAR = True
3396 self._metar.get_buffer().set_text(metar, -1)
3397 self._updatingMETAR = False
3398
3399 self._updateForwardButton()
3400
3401 def _updateForwardButton(self):
3402 """Update the sensitivity of the forward button based on some conditions."""
3403 sensitive = self._forwardAllowed and \
3404 self._metar.get_text()!="" and \
3405 self._runway.get_text()!="" and \
3406 self.sid is not None and \
3407 self.v1 is not None and \
3408 self.vr is not None and \
3409 self.v2 is not None and \
3410 self.v1 <= self.vr and \
3411 self.vr <= self.v2 and \
3412 (self._derateType==acft.DERATE_NONE or
3413 self.derate is not None)
3414
3415 print "TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive
3416 if self._forwardAllowed:
3417 print " METAR: ", self._metar.get_text()
3418 print " runway: ", self._runway.get_text()
3419 print " SID:", self.sid
3420 print " V1:", self.v1
3421 print " VR:", self.vr
3422 print " V2:", self.v2
3423 print " derateType:", self._derateType
3424 print " derate:", self.derate
3425
3426 self._button.set_sensitive(sensitive)
3427
3428 def _valueChanged(self, widget, arg = None):
3429 """Called when the value of some widget has changed."""
3430 print "TakeoffPage._valueChanged"
3431
3432 self._updateForwardButton()
3433
3434 def _upperChanged(self, entry, arg = None):
3435 """Called when the value of some entry widget has changed and the value
3436 should be converted to uppercase."""
3437 print "TakeoffPage._upperChanged"
3438 entry.set_text(entry.get_text().upper())
3439 self._valueChanged(entry, arg)
3440
3441 def _upperChangedComboBox(self, comboBox):
3442 """Called for combo box widgets that must be converted to uppercase."""
3443 entry = comboBox.get_child()
3444 if comboBox.get_active()==-1:
3445 entry.set_text(entry.get_text().upper())
3446 self._valueChanged(entry)
3447
3448 def _derateChanged(self, entry):
3449 """Called when the value of the derate is changed."""
3450 print "TakeoffPage._derateChanged"
3451 self._updateForwardButton()
3452
3453 def _rtoToggled(self, button):
3454 """Called when the RTO check button is toggled."""
3455 self._wizard.rtoToggled(button.get_active())
3456
3457 def _backClicked(self, button):
3458 """Called when the Back button is pressed."""
3459 self.goBack()
3460
3461 def _forwardClicked(self, button):
3462 """Called when the forward button is clicked."""
3463 aircraft = self._wizard.gui.flight.aircraft
3464 aircraft.updateV1R2()
3465 if self.derate is not None:
3466 aircraft.updateDerate()
3467 aircraft.updateTakeoffAntiIce()
3468 self._wizard.nextPage()
3469
3470 def _setupDerateWidget(self):
3471 """Setup the derate widget."""
3472 if self._derateWidget is not None:
3473 self._derate.remove(self._derateWidget)
3474
3475 if self._derateType==acft.DERATE_BOEING:
3476 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
3477 self._derateLabel.set_use_underline(True)
3478 self._derateLabel.set_sensitive(True)
3479
3480 self._derateEntry = gtk.Entry()
3481 self._derateEntry.set_width_chars(7)
3482 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
3483 self._derateEntry.set_alignment(1.0)
3484 self._derateEntry.connect("changed", self._derateChanged)
3485 self._derateLabel.set_mnemonic_widget(self._derateEntry)
3486
3487 self._derateUnit = gtk.Label("%")
3488 self._derateUnit.set_alignment(0.0, 0.5)
3489
3490 self._derateWidget = gtk.Table(3, 1)
3491 self._derateWidget.set_row_spacings(4)
3492 self._derateWidget.set_col_spacings(16)
3493 self._derateWidget.set_homogeneous(False)
3494
3495 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
3496 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
3497
3498 self._derate.add(self._derateWidget)
3499 elif self._derateType==acft.DERATE_EPR:
3500 self._derateLabel.set_text("_EPR:")
3501 self._derateLabel.set_use_underline(True)
3502 self._derateLabel.set_sensitive(True)
3503
3504 self._derateWidget = gtk.Entry()
3505 self._derateWidget.set_width_chars(7)
3506 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
3507 self._derateWidget.set_alignment(1.0)
3508 self._derateWidget.connect("changed", self._derateChanged)
3509 self._derateLabel.set_mnemonic_widget(self._derateWidget)
3510
3511 self._derate.add(self._derateWidget)
3512 elif self._derateType==acft.DERATE_TUPOLEV:
3513 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
3514 self._derateLabel.set_use_underline(True)
3515 self._derateLabel.set_sensitive(True)
3516
3517 if pygobject:
3518 nominal = gtk.RadioButton.\
3519 new_with_label_from_widget(None,
3520 xstr("takeoff_derate_tupolev_nominal"))
3521 else:
3522 nominal = gtk.RadioButton(None,
3523 xstr("takeoff_derate_tupolev_nominal"))
3524 nominal.set_use_underline(True)
3525 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
3526 nominal.connect("toggled", self._derateChanged)
3527
3528 if pygobject:
3529 takeoff = gtk.RadioButton.\
3530 new_with_label_from_widget(nominal,
3531 xstr("takeoff_derate_tupolev_takeoff"))
3532 else:
3533 takeoff = gtk.RadioButton(nominal,
3534 xstr("takeoff_derate_tupolev_takeoff"))
3535
3536 takeoff.set_use_underline(True)
3537 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
3538 takeoff.connect("toggled", self._derateChanged)
3539
3540 self._derateButtons = [nominal, takeoff]
3541
3542 self._derateWidget = gtk.HBox()
3543 self._derateWidget.pack_start(nominal, False, False, 4)
3544 self._derateWidget.pack_start(takeoff, False, False, 4)
3545
3546 self._derate.add(self._derateWidget)
3547 elif self._derateType==acft.DERATE_B462:
3548 self._derateLabel.set_text("")
3549
3550 self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
3551 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
3552 self._derateWidget.set_use_underline(True)
3553 self._derate.add(self._derateWidget)
3554 else:
3555 self._derateWidget = None
3556 self._derateLabel.set_text("")
3557 self._derateLabel.set_sensitive(False)
3558
3559 def _metarChanged(self, entry):
3560 """Called when the METAR has changed."""
3561 print "TakeoffPage.metarChanged", self._updatingMETAR
3562 if not self._updatingMETAR:
3563 self._updateForwardButton()
3564 self._wizard.metarChanged(entry.get_text(), self)
3565
3566 def _metarInserted(self, buffer, position, text, length):
3567 """Called when new characters are inserted into the METAR.
3568
3569 It uppercases all characters."""
3570 print "TakeoffPage.metarInserted", self._updatingMETAR
3571 if not self._updatingMETAR:
3572 self._updatingMETAR = True
3573
3574 buffer.delete_text(position, length)
3575 buffer.insert_text(position, text.upper(), length)
3576
3577 self._updatingMETAR = False
3578
3579#-----------------------------------------------------------------------------
3580
3581class CruisePage(Page):
3582 """The page containing the flight level that might change during flight."""
3583 def __init__(self, wizard):
3584 """Construct the page."""
3585 super(CruisePage, self).__init__(wizard, "cruise",
3586 xstr("cruise_title"),
3587 xstr("cruise_help"))
3588
3589 self._loggable = False
3590 self._loggedCruiseLevel = 240
3591 self._activated = False
3592
3593 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
3594 xscale = 0.0, yscale = 1.0)
3595
3596 mainBox = gtk.VBox()
3597 alignment.add(mainBox)
3598 self.setMainWidget(alignment)
3599
3600 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3601 xscale = 0.0, yscale = 0.0)
3602 mainBox.pack_start(alignment, False, False, 16)
3603
3604 levelBox = gtk.HBox()
3605
3606 label = gtk.Label(xstr("route_level"))
3607 label.set_use_underline(True)
3608 levelBox.pack_start(label, True, True, 0)
3609
3610 self._cruiseLevel = gtk.SpinButton()
3611 self._cruiseLevel.set_increments(step = 10, page = 100)
3612 self._cruiseLevel.set_range(min = 50, max = 500)
3613 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
3614 self._cruiseLevel.set_numeric(True)
3615 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
3616 label.set_mnemonic_widget(self._cruiseLevel)
3617
3618 levelBox.pack_start(self._cruiseLevel, False, False, 8)
3619
3620 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
3621 self._updateButton.set_use_underline(True)
3622 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
3623 self._updateButton.connect("clicked", self._updateButtonClicked)
3624
3625 levelBox.pack_start(self._updateButton, False, False, 16)
3626
3627 mainBox.pack_start(levelBox, False, False, 0)
3628
3629 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3630 xscale = 0.0, yscale = 1.0)
3631 mainBox.pack_start(alignment, True, True, 0)
3632
3633 self.addCancelFlightButton()
3634
3635 self._backButton = self.addPreviousButton(clicked = self._backClicked)
3636 self._button = self.addNextButton(clicked = self._forwardClicked)
3637
3638 @property
3639 def activated(self):
3640 """Determine if the page is already activated or not."""
3641 return self._activated
3642
3643 @property
3644 def cruiseLevel(self):
3645 """Get the cruise level."""
3646 return self._loggedCruiseLevel
3647
3648 @property
3649 def loggableCruiseLevel(self):
3650 """Get the cruise level which should be logged."""
3651 return self._cruiseLevel.get_value_as_int()
3652
3653 def setLoggable(self, loggable):
3654 """Set whether the cruise altitude can be logged."""
3655 self._loggable = loggable
3656 self._updateButtons()
3657
3658 def activate(self):
3659 """Setup the route from the booked flight."""
3660 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
3661 self._cruiseLevel.set_value(self._loggedCruiseLevel)
3662 self._activated = True
3663
3664 def reset(self):
3665 """Reset the page."""
3666 self._loggable = False
3667 self._activated = False
3668 super(CruisePage, self).reset()
3669
3670 def _updateButtons(self):
3671 """Update the sensitivity of the buttons."""
3672 self._updateButton.set_sensitive(self._loggable and
3673 self.loggableCruiseLevel!=
3674 self._loggedCruiseLevel)
3675
3676 def _cruiseLevelChanged(self, spinButton):
3677 """Called when the cruise level has changed."""
3678 self._updateButtons()
3679
3680 def _updateButtonClicked(self, button):
3681 """Called when the update button is clicked."""
3682 if self._wizard.cruiseLevelChanged():
3683 self._loggedCruiseLevel = self.loggableCruiseLevel
3684 self._updateButtons()
3685
3686 def _backClicked(self, button):
3687 """Called when the Back button is pressed."""
3688 self.goBack()
3689
3690 def _forwardClicked(self, button):
3691 """Called when the Forward button is clicked."""
3692 self._wizard.nextPage()
3693
3694#-----------------------------------------------------------------------------
3695
3696class LandingPage(Page):
3697 """Page for entering landing data."""
3698 def __init__(self, wizard):
3699 """Construct the landing page."""
3700 super(LandingPage, self).__init__(wizard, "landing",
3701 xstr("landing_title"),
3702 xstr("landing_help"),
3703 completedHelp = xstr("landing_chelp"))
3704
3705 self._flightEnded = False
3706
3707 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3708 xscale = 0.0, yscale = 0.0)
3709
3710 table = gtk.Table(7, 24)
3711 table.set_row_spacings(4)
3712 table.set_col_spacings(16)
3713 table.set_homogeneous(False)
3714 alignment.add(table)
3715 self.setMainWidget(alignment)
3716
3717 row = 0
3718
3719 label = gtk.Label(xstr("landing_metar"))
3720 label.set_use_underline(True)
3721 label.set_alignment(0.0, 0.5)
3722 table.attach(label, 1, 2, row, row+1)
3723
3724 self._metar = gtk.Entry()
3725 self._metar.set_width_chars(40)
3726 self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
3727 self._metar.connect("changed", self._metarChanged)
3728 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3729 table.attach(self._metar, 2, 24, row, row+1)
3730 label.set_mnemonic_widget(self._metar)
3731
3732 self._updatingMETAR = False
3733
3734 row += 1
3735
3736 label = gtk.Label(xstr("landing_star"))
3737 label.set_use_underline(True)
3738 label.set_alignment(0.0, 0.5)
3739 table.attach(label, 1, 2, row, row + 1)
3740
3741 if pygobject:
3742 self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
3743 else:
3744 self._star = gtk.ComboBoxEntry(comboModel)
3745
3746 self._star.set_entry_text_column(0)
3747 self._star.get_child().set_width_chars(10)
3748 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
3749 self._star.connect("changed", self._upperChangedComboBox)
3750 self._star.set_sensitive(False)
3751 table.attach(self._star, 2, 4, row, row + 1)
3752 label.set_mnemonic_widget(self._star)
3753
3754 row += 1
3755
3756 label = gtk.Label(xstr("landing_transition"))
3757 label.set_use_underline(True)
3758 label.set_alignment(0.0, 0.5)
3759 table.attach(label, 1, 2, row, row + 1)
3760
3761 if pygobject:
3762 self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
3763 else:
3764 self._transition = gtk.ComboBoxEntry(comboModel)
3765
3766 self._transition.set_entry_text_column(0)
3767 self._transition.get_child().set_width_chars(10)
3768 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
3769 self._transition.connect("changed", self._upperChangedComboBox)
3770 self._transition.set_sensitive(False)
3771 table.attach(self._transition, 2, 4, row, row + 1)
3772 label.set_mnemonic_widget(self._transition)
3773
3774 row += 1
3775
3776 label = gtk.Label(xstr("landing_runway"))
3777 label.set_use_underline(True)
3778 label.set_alignment(0.0, 0.5)
3779 table.attach(label, 1, 2, row, row + 1)
3780
3781 self._runway = gtk.Entry()
3782 self._runway.set_width_chars(10)
3783 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
3784 self._runway.connect("changed", self._upperChanged)
3785 table.attach(self._runway, 2, 4, row, row + 1)
3786 label.set_mnemonic_widget(self._runway)
3787
3788 row += 1
3789
3790 label = gtk.Label(xstr("landing_approach"))
3791 label.set_use_underline(True)
3792 label.set_alignment(0.0, 0.5)
3793 table.attach(label, 1, 2, row, row + 1)
3794
3795 self._approachType = gtk.Entry()
3796 self._approachType.set_width_chars(10)
3797 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
3798 self._approachType.connect("changed", self._upperChanged)
3799 table.attach(self._approachType, 2, 4, row, row + 1)
3800 label.set_mnemonic_widget(self._approachType)
3801
3802 row += 1
3803
3804 label = gtk.Label(xstr("landing_vref"))
3805 label.set_use_markup(True)
3806 label.set_use_underline(True)
3807 label.set_alignment(0.0, 0.5)
3808 table.attach(label, 1, 2, row, row + 1)
3809
3810 self._vref = IntegerEntry()
3811 self._vref.set_width_chars(5)
3812 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
3813 self._vref.connect("integer-changed", self._vrefChanged)
3814 table.attach(self._vref, 3, 4, row, row + 1)
3815 label.set_mnemonic_widget(self._vref)
3816
3817 self._vrefUnit = gtk.Label(xstr("label_knots"))
3818 table.attach(self._vrefUnit, 4, 5, row, row + 1)
3819
3820 row += 1
3821
3822 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
3823 self._antiIceOn.set_use_underline(True)
3824 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
3825 table.attach(self._antiIceOn, 3, 5, row, row + 1)
3826
3827 self.addCancelFlightButton()
3828
3829 self.addPreviousButton(clicked = self._backClicked)
3830
3831 self._button = self.addNextButton(clicked = self._forwardClicked)
3832
3833 self._active = False
3834
3835 @property
3836 def star(self):
3837 """Get the STAR or None if none entered."""
3838 text = self._star.get_child().get_text()
3839 return text if self._star.get_active()!=0 and text and text!="N/A" \
3840 else None
3841
3842 @property
3843 def transition(self):
3844 """Get the transition or None if none entered."""
3845 text = self._transition.get_child().get_text()
3846 return text if self._transition.get_active()!=0 and text and text!="N/A" \
3847 else None
3848
3849 @property
3850 def approachType(self):
3851 """Get the approach type."""
3852 return self._approachType.get_text()
3853
3854 @property
3855 def runway(self):
3856 """Get the runway."""
3857 return self._runway.get_text()
3858
3859 @property
3860 def vref(self):
3861 """Return the landing reference speed."""
3862 return self._vref.get_int()
3863
3864 @property
3865 def antiIceOn(self):
3866 """Get whether the anti-ice system has been turned on."""
3867 return self._antiIceOn.get_active()
3868
3869 @antiIceOn.setter
3870 def antiIceOn(self, value):
3871 """Set the anti-ice indicator."""
3872 self._antiIceOn.set_active(value)
3873
3874 def reset(self):
3875 """Reset the page if the wizard is reset."""
3876 super(LandingPage, self).reset()
3877 self._vref.reset()
3878 self._antiIceOn.set_active(False)
3879 self._flightEnded = False
3880 self._active = False
3881
3882 def activate(self):
3883 """Called when the page is activated."""
3884 self._updatingMETAR = True
3885 self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
3886 self._updatingMETAR = False
3887
3888 self._star.set_active(0)
3889 self._star.set_sensitive(True)
3890
3891 self._transition.set_active(0)
3892 self._transition.set_sensitive(True)
3893
3894 if self._wizard.landingRunway is None:
3895 self._runway.set_text("")
3896 else:
3897 self._runway.set_text(self._wizard.landingRunway)
3898 self._runway.set_sensitive(True)
3899
3900 self._approachType.set_text("")
3901 self._approachType.set_sensitive(True)
3902
3903 self._vref.set_int(None)
3904 self._vref.set_sensitive(True)
3905
3906 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
3907 speedUnit = xstr("label" + i18nSpeedUnit)
3908 self._vrefUnit.set_text(speedUnit)
3909
3910 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
3911 i18nSpeedUnit))
3912
3913 self._updateForwardButton()
3914
3915 self._active = True
3916
3917 def flightEnded(self):
3918 """Called when the flight has ended."""
3919 super(LandingPage, self).flightEnded()
3920 self._flightEnded = True
3921 self._updateForwardButton()
3922
3923 def changeMETAR(self, metar):
3924 """Change the METAR as a result of an edit on one of the other
3925 pages."""
3926 if self._active:
3927 print "LandingPage.changeMETAR"
3928 self._updatingMETAR = True
3929 self._metar.get_buffer().set_text(metar, -1)
3930 self._updatingMETAR = False
3931
3932 self._updateForwardButton()
3933
3934 def _updateForwardButton(self):
3935 """Update the sensitivity of the forward button."""
3936 sensitive = self._flightEnded and \
3937 self._metar.get_text()!="" and \
3938 (self.star is not None or
3939 self.transition is not None) and \
3940 self._runway.get_text()!="" and \
3941 self._approachType.get_text()!="" and \
3942 self.vref is not None
3943 self._button.set_sensitive(sensitive)
3944
3945 def _upperChanged(self, entry):
3946 """Called for entry widgets that must be converted to uppercase."""
3947 entry.set_text(entry.get_text().upper())
3948 self._updateForwardButton()
3949
3950 def _upperChangedComboBox(self, comboBox):
3951 """Called for combo box widgets that must be converted to uppercase."""
3952 if comboBox.get_active()==-1:
3953 entry = comboBox.get_child()
3954 entry.set_text(entry.get_text().upper())
3955 self._updateForwardButton()
3956
3957 def _vrefChanged(self, widget, value):
3958 """Called when the Vref has changed."""
3959 self._updateForwardButton()
3960
3961 def _backClicked(self, button):
3962 """Called when the Back button is pressed."""
3963 self.goBack()
3964
3965 def _forwardClicked(self, button):
3966 """Called when the forward button is clicked."""
3967 aircraft = self._wizard.gui.flight.aircraft
3968 aircraft.updateVRef()
3969 aircraft.updateLandingAntiIce()
3970 if self._wizard.gui.config.onlineGateSystem and \
3971 self._wizard.loggedIn and not self._completed and \
3972 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3973 not self._wizard.entranceExam:
3974 self._wizard.getFleet(callback = self._fleetRetrieved,
3975 force = True)
3976 else:
3977 self._wizard.nextPage()
3978
3979 def _fleetRetrieved(self, fleet):
3980 """Callback for the fleet retrieval."""
3981 self._wizard.nextPage()
3982
3983 def _metarChanged(self, entry):
3984 """Called when the METAR has changed."""
3985 print "LandingPage.metarChanged", self._updatingMETAR
3986 if not self._updatingMETAR:
3987 self._updateForwardButton()
3988 self._wizard.metarChanged(entry.get_text(), self)
3989
3990 def _metarInserted(self, buffer, position, text, length):
3991 """Called when new characters are inserted into the METAR.
3992
3993 It uppercases all characters."""
3994 print "LandingPage.metarInserted", self._updatingMETAR
3995 if not self._updatingMETAR:
3996 self._updatingMETAR = True
3997
3998 buffer.delete_text(position, length)
3999 buffer.insert_text(position, text.upper(), length)
4000
4001 self._updatingMETAR = False
4002
4003#-----------------------------------------------------------------------------
4004
4005class FinishPage(Page):
4006 """Flight finish page."""
4007 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
4008 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
4009 ("flighttype_vip", const.FLIGHTTYPE_VIP),
4010 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
4011
4012 def __init__(self, wizard):
4013 """Construct the finish page."""
4014 help = xstr("finish_help") + xstr("finish_help_goodtime")
4015 super(FinishPage, self).__init__(wizard, "finish",
4016 xstr("finish_title"), help)
4017
4018 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4019 xscale = 0.0, yscale = 0.0)
4020
4021 table = gtk.Table(10, 2)
4022 table.set_row_spacings(4)
4023 table.set_col_spacings(16)
4024 table.set_homogeneous(False)
4025 alignment.add(table)
4026 self.setMainWidget(alignment)
4027
4028 row = 0
4029
4030 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4031 label = gtk.Label(xstr("finish_rating"))
4032 labelAlignment.add(label)
4033 table.attach(labelAlignment, 0, 1, row, row+1)
4034
4035 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4036 self._flightRating = gtk.Label()
4037 self._flightRating.set_width_chars(8)
4038 self._flightRating.set_alignment(0.0, 0.5)
4039 self._flightRating.set_use_markup(True)
4040 labelAlignment.add(self._flightRating)
4041 table.attach(labelAlignment, 1, 2, row, row+1)
4042
4043 row += 1
4044
4045 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4046 label = gtk.Label(xstr("finish_dep_time"))
4047 labelAlignment.add(label)
4048 table.attach(labelAlignment, 0, 1, row, row+1)
4049
4050 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4051 self._depTime = gtk.Label()
4052 self._depTime.set_width_chars(13)
4053 self._depTime.set_alignment(0.0, 0.5)
4054 self._depTime.set_use_markup(True)
4055 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
4056 labelAlignment.add(self._depTime)
4057 table.attach(labelAlignment, 1, 2, row, row+1)
4058
4059 row += 1
4060
4061 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4062 label = gtk.Label(xstr("finish_flight_time"))
4063 labelAlignment.add(label)
4064 table.attach(labelAlignment, 0, 1, row, row+1)
4065
4066 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4067 self._flightTime = gtk.Label()
4068 self._flightTime.set_width_chars(10)
4069 self._flightTime.set_alignment(0.0, 0.5)
4070 self._flightTime.set_use_markup(True)
4071 labelAlignment.add(self._flightTime)
4072 table.attach(labelAlignment, 1, 2, row, row+1)
4073
4074 row += 1
4075
4076 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4077 label = gtk.Label(xstr("finish_block_time"))
4078 labelAlignment.add(label)
4079 table.attach(labelAlignment, 0, 1, row, row+1)
4080
4081 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4082 self._blockTime = gtk.Label()
4083 self._blockTime.set_width_chars(10)
4084 self._blockTime.set_alignment(0.0, 0.5)
4085 self._blockTime.set_use_markup(True)
4086 labelAlignment.add(self._blockTime)
4087 table.attach(labelAlignment, 1, 2, row, row+1)
4088
4089 row += 1
4090
4091 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4092 label = gtk.Label(xstr("finish_arr_time"))
4093 labelAlignment.add(label)
4094 table.attach(labelAlignment, 0, 1, row, row+1)
4095
4096 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4097 self._arrTime = gtk.Label()
4098 self._arrTime.set_width_chars(13)
4099 self._arrTime.set_alignment(0.0, 0.5)
4100 self._arrTime.set_use_markup(True)
4101 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
4102 labelAlignment.add(self._arrTime)
4103 table.attach(labelAlignment, 1, 2, row, row+1)
4104
4105 row += 1
4106
4107 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4108 label = gtk.Label(xstr("finish_distance"))
4109 labelAlignment.add(label)
4110 table.attach(labelAlignment, 0, 1, row, row+1)
4111
4112 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4113 self._distanceFlown = gtk.Label()
4114 self._distanceFlown.set_width_chars(10)
4115 self._distanceFlown.set_alignment(0.0, 0.5)
4116 self._distanceFlown.set_use_markup(True)
4117 labelAlignment.add(self._distanceFlown)
4118 table.attach(labelAlignment, 1, 2, row, row+1)
4119
4120 row += 1
4121
4122 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4123 label = gtk.Label(xstr("finish_fuel"))
4124 labelAlignment.add(label)
4125 table.attach(labelAlignment, 0, 1, row, row+1)
4126
4127 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4128 self._fuelUsed = gtk.Label()
4129 self._fuelUsed.set_width_chars(10)
4130 self._fuelUsed.set_alignment(0.0, 0.5)
4131 self._fuelUsed.set_use_markup(True)
4132 labelAlignment.add(self._fuelUsed)
4133 table.attach(labelAlignment, 1, 2, row, row+1)
4134
4135 row += 1
4136
4137 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
4138 yalign = 0.5, yscale = 0.0)
4139 label = gtk.Label(xstr("finish_type"))
4140 label.set_use_underline(True)
4141 labelAlignment.add(label)
4142 table.attach(labelAlignment, 0, 1, row, row+1)
4143
4144 flightTypeModel = gtk.ListStore(str, int)
4145 for (name, type) in FinishPage._flightTypes:
4146 flightTypeModel.append([xstr(name), type])
4147
4148 self._flightType = gtk.ComboBox(model = flightTypeModel)
4149 renderer = gtk.CellRendererText()
4150 self._flightType.pack_start(renderer, True)
4151 self._flightType.add_attribute(renderer, "text", 0)
4152 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
4153 self._flightType.set_active(0)
4154 self._flightType.connect("changed", self._flightTypeChanged)
4155 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4156 flightTypeAlignment.add(self._flightType)
4157 table.attach(flightTypeAlignment, 1, 2, row, row+1)
4158 label.set_mnemonic_widget(self._flightType)
4159
4160 row += 1
4161
4162 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
4163 self._onlineFlight.set_use_underline(True)
4164 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
4165 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4166 onlineFlightAlignment.add(self._onlineFlight)
4167 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
4168
4169 row += 1
4170
4171 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
4172 yalign = 0.5, yscale = 0.0)
4173 self._gateLabel = gtk.Label(xstr("finish_gate"))
4174 self._gateLabel.set_use_underline(True)
4175 labelAlignment.add(self._gateLabel)
4176 table.attach(labelAlignment, 0, 1, row, row+1)
4177
4178 self._gatesModel = gtk.ListStore(str)
4179
4180 self._gate = gtk.ComboBox(model = self._gatesModel)
4181 renderer = gtk.CellRendererText()
4182 self._gate.pack_start(renderer, True)
4183 self._gate.add_attribute(renderer, "text", 0)
4184 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
4185 self._gate.connect("changed", self._gateChanged)
4186 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
4187 gateAlignment.add(self._gate)
4188 table.attach(gateAlignment, 1, 2, row, row+1)
4189 self._gateLabel.set_mnemonic_widget(self._gate)
4190
4191 self.addButton(xstr("finish_newFlight"),
4192 sensitive = True,
4193 clicked = self._newFlightClicked,
4194 tooltip = xstr("finish_newFlight_tooltip"),
4195 padding = 16)
4196
4197 self.addPreviousButton(clicked = self._backClicked)
4198
4199 self._saveButton = self.addButton(xstr("finish_save"),
4200 sensitive = False,
4201 clicked = self._saveClicked,
4202 tooltip = xstr("finish_save_tooltip"))
4203 self._savePIREPDialog = None
4204 self._lastSavePath = None
4205
4206 self._tooBigTimeDifference = False
4207 self._deferredAutoSave = False
4208 self._pirepSaved = False
4209 self._pirepSent = False
4210
4211 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
4212 sensitive = False,
4213 clicked = self._sendClicked,
4214 tooltip = xstr("sendPIREP_tooltip"))
4215
4216 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
4217 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
4218 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
4219 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
4220 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
4221 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
4222 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
4223 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
4224
4225 @property
4226 def flightType(self):
4227 """Get the flight type."""
4228 index = self._flightType.get_active()
4229 return None if index<0 else self._flightType.get_model()[index][1]
4230
4231 @property
4232 def online(self):
4233 """Get whether the flight was an online flight or not."""
4234 return self._onlineFlight.get_active()
4235
4236 def activate(self):
4237 """Activate the page."""
4238 self._deferredAutoSave = False
4239 self._pirepSaved = False
4240 self._pirepSent = False
4241
4242 flight = self._wizard.gui._flight
4243 rating = flight.logger.getRating()
4244 if rating<0:
4245 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
4246 else:
4247 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
4248
4249 flightLength = flight.flightTimeEnd - flight.flightTimeStart
4250 self._flightTime.set_markup("<b>%s</b>" % \
4251 (util.getTimeIntervalString(flightLength),))
4252
4253 blockLength = flight.blockTimeEnd - flight.blockTimeStart
4254 self._blockTime.set_markup("<b>%s</b>" % \
4255 (util.getTimeIntervalString(blockLength),))
4256
4257 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
4258 (flight.flownDistance,))
4259
4260 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
4261 (flight.startFuel - flight.endFuel,))
4262
4263 self._flightType.set_active(-1)
4264 self._onlineFlight.set_active(self._wizard.loggedIn)
4265
4266 self._gatesModel.clear()
4267 if self._wizard.gui.config.onlineGateSystem and \
4268 self._wizard.loggedIn and \
4269 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
4270 not self._wizard.entranceExam:
4271 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
4272 for gate in lhbpGates.gates:
4273 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
4274 self._gatesModel.append([gate.number])
4275 self._gateLabel.set_sensitive(True)
4276 self._gate.set_sensitive(True)
4277 self._gate.set_active(-1)
4278 else:
4279 self._gateLabel.set_sensitive(False)
4280 self._gate.set_sensitive(False)
4281
4282 self._updateTimes()
4283
4284 def updateButtons(self):
4285 """Update the sensitivity state of the buttons."""
4286 gui = self._wizard.gui
4287 faultsExplained = gui.faultsFullyExplained
4288 timesCorrect = self.flightType is None or \
4289 not self._tooBigTimeDifference or \
4290 gui.hasComments or gui.hasDelayCode
4291 sensitive = gui.flight is not None and \
4292 gui.flight.stage==const.STAGE_END and \
4293 self._flightType.get_active()>=0 and \
4294 (self._gatesModel.get_iter_first() is None or
4295 self._gate.get_active()>=0) and \
4296 faultsExplained and timesCorrect
4297
4298 self._updateHelp(faultsExplained, timesCorrect)
4299
4300 wasSensitive = self._saveButton.get_sensitive()
4301
4302 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
4303 if gui.isWizardActive():
4304 self._autoSavePIREP()
4305 else:
4306 self._deferredAutoSave = True
4307
4308 if not sensitive:
4309 self._deferredAutoSave = False
4310
4311 self._saveButton.set_sensitive(sensitive)
4312 self._sendButton.set_sensitive(sensitive and
4313 self._wizard.bookedFlight.id is not None)
4314
4315 def grabDefault(self):
4316 """If the page has a default button, make it the default one."""
4317 super(FinishPage, self).grabDefault()
4318 if self._deferredAutoSave:
4319 self._autoSavePIREP()
4320 self._deferredAutoSave = False
4321
4322 def _autoSavePIREP(self):
4323 """Perform the automatic saving of the PIREP."""
4324 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
4325 self._getDefaultPIREPName())
4326 self._lastSavePath = text2unicode(self._lastSavePath)
4327 self._savePIREP(automatic = True)
4328
4329 def _backClicked(self, button):
4330 """Called when the Back button is pressed."""
4331 self.goBack()
4332
4333 def _flightTypeChanged(self, comboBox):
4334 """Called when the flight type has changed."""
4335 self._updateTimes()
4336
4337 def _gateChanged(self, comboBox):
4338 """Called when the arrival gate has changed."""
4339 self.updateButtons()
4340
4341 def _newFlightClicked(self, button):
4342 """Called when the new flight button is clicked."""
4343 gui = self._wizard.gui
4344 if not self._pirepSent and not self._pirepSaved:
4345 dialog = gtk.MessageDialog(parent = gui.mainWindow,
4346 type = MESSAGETYPE_QUESTION,
4347 message_format = xstr("finish_newFlight_question"))
4348
4349 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
4350 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
4351
4352 dialog.set_title(WINDOW_TITLE_BASE)
4353 result = dialog.run()
4354 dialog.hide()
4355 if result!=RESPONSETYPE_YES:
4356 return
4357
4358 gui.reset()
4359
4360 def _getDefaultPIREPName(self):
4361 """Get the default name of the PIREP."""
4362 gui = self._wizard.gui
4363
4364 bookedFlight = gui.bookedFlight
4365 tm = time.gmtime()
4366
4367 pilotID = self._wizard.pilotID
4368 if pilotID: pilotID += " "
4369 return "%s%s %02d%02d %s-%s.pirep" % \
4370 (pilotID, str(bookedFlight.departureTime.date()),
4371 tm.tm_hour, tm.tm_min,
4372 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
4373
4374
4375 def _saveClicked(self, button):
4376 """Called when the Save PIREP button is clicked."""
4377 gui = self._wizard.gui
4378
4379 fileName = self._getDefaultPIREPName()
4380
4381 dialog = self._getSaveDialog()
4382
4383 if self._lastSavePath is None:
4384 pirepDirectory = gui.config.pirepDirectory
4385 if pirepDirectory is not None:
4386 dialog.set_current_folder(pirepDirectory)
4387 else:
4388 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
4389
4390 dialog.set_current_name(fileName)
4391 result = dialog.run()
4392 dialog.hide()
4393
4394 if result==RESPONSETYPE_OK:
4395 self._lastSavePath = text2unicode(dialog.get_filename())
4396 self._savePIREP()
4397
4398 def _savePIREP(self, automatic = False):
4399 """Perform the saving of the PIREP."""
4400
4401 gui = self._wizard.gui
4402
4403 if automatic:
4404 gui.beginBusy(xstr("finish_autosave_busy"))
4405
4406 pirep = PIREP(gui.flight)
4407 error = pirep.save(self._lastSavePath)
4408
4409 if automatic:
4410 gui.endBusy()
4411
4412 if error:
4413 type = MESSAGETYPE_ERROR
4414 message = xstr("finish_save_failed")
4415 secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
4416 else:
4417 type = MESSAGETYPE_INFO
4418 message = xstr("finish_save_done")
4419 if automatic:
4420 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
4421 else:
4422 secondary = None
4423 self._pirepSaved = True
4424
4425 dialog = gtk.MessageDialog(parent = gui.mainWindow,
4426 type = type, message_format = message)
4427 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4428 dialog.set_title(WINDOW_TITLE_BASE)
4429 if secondary is not None:
4430 dialog.format_secondary_markup(secondary)
4431
4432 dialog.run()
4433 dialog.hide()
4434
4435 def _getSaveDialog(self):
4436 """Get the PIREP saving dialog.
4437
4438 If it does not exist yet, create it."""
4439 if self._savePIREPDialog is None:
4440 gui = self._wizard.gui
4441 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
4442 xstr("finish_save_title"),
4443 action = FILE_CHOOSER_ACTION_SAVE,
4444 buttons = (gtk.STOCK_CANCEL,
4445 RESPONSETYPE_CANCEL,
4446 gtk.STOCK_OK, RESPONSETYPE_OK),
4447 parent = gui.mainWindow)
4448 dialog.set_modal(True)
4449 dialog.set_do_overwrite_confirmation(True)
4450
4451 filter = gtk.FileFilter()
4452 filter.set_name(xstr("file_filter_pireps"))
4453 filter.add_pattern("*.pirep")
4454 dialog.add_filter(filter)
4455
4456 filter = gtk.FileFilter()
4457 filter.set_name(xstr("file_filter_all"))
4458 filter.add_pattern("*.*")
4459 dialog.add_filter(filter)
4460
4461 self._savePIREPDialog = dialog
4462
4463 return self._savePIREPDialog
4464
4465
4466 def _sendClicked(self, button):
4467 """Called when the Send button is clicked."""
4468 pirep = PIREP(self._wizard.gui.flight)
4469 self._wizard.gui.sendPIREP(pirep,
4470 callback = self._handlePIREPSent)
4471
4472 def _handlePIREPSent(self, returned, result):
4473 """Callback for the PIREP sending result."""
4474 self._pirepSent = returned and result.success
4475 if self._wizard.gui.config.onlineGateSystem and \
4476 self._wizard.loggedIn and not self._wizard.entranceExam and \
4477 returned and result.success:
4478 bookedFlight = self._wizard.bookedFlight
4479 if bookedFlight.arrivalICAO=="LHBP":
4480 iter = self._gate.get_active_iter()
4481 gateNumber = None if iter is None \
4482 else self._gatesModel.get_value(iter, 0)
4483
4484 status = const.PLANE_PARKING if gateNumber is None \
4485 else const.PLANE_HOME
4486 else:
4487 gateNumber = None
4488 status = const.PLANE_AWAY
4489
4490 self._wizard.updatePlane(self._planeUpdated,
4491 bookedFlight.tailNumber,
4492 status, gateNumber = gateNumber)
4493
4494 def _planeUpdated(self, success):
4495 """Callback for the plane updating."""
4496 pass
4497
4498 def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
4499 """Format the departure or arrival time based on the given data as a
4500 markup for a label."""
4501 realTime = time.gmtime(realTimestamp)
4502
4503 if warning:
4504 colour = "red" if error else "orange"
4505 markupBegin = '<span foreground="%s">' % (colour,)
4506 markupEnd = '</span>'
4507 else:
4508 markupBegin = markupEnd = ""
4509
4510 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
4511 (markupBegin,
4512 realTime.tm_hour, realTime.tm_min,
4513 scheduledTime.hour, scheduledTime.minute,
4514 markupEnd)
4515
4516 return markup
4517
4518 def _updateTimes(self):
4519 """Format the flight times and the help text according to the flight
4520 type.
4521
4522 The buttons are also updated.
4523 """
4524 flight = self._wizard.gui._flight
4525 bookedFlight = flight.bookedFlight
4526
4527 (departureWarning, departureError) = flight.blockTimeStartWrong
4528 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
4529
4530 if self.flightType==const.FLIGHTTYPE_VIP:
4531 departureError = arrivalError = False
4532
4533 self._tooBigTimeDifference = departureError or arrivalError
4534
4535 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
4536 flight.blockTimeStart,
4537 (departureWarning,
4538 departureError)))
4539
4540 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
4541 flight.blockTimeEnd,
4542 (arrivalWarning,
4543 arrivalError)))
4544
4545 self.updateButtons()
4546
4547 def _updateHelp(self, faultsExplained, timesCorrect):
4548 """Update the help text according to the actual situation."""
4549 if not faultsExplained:
4550 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
4551 elif not timesCorrect:
4552 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
4553 else:
4554 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
4555
4556
4557#-----------------------------------------------------------------------------
4558
4559class Wizard(gtk.VBox):
4560 """The flight wizard."""
4561 def __init__(self, gui):
4562 """Construct the wizard."""
4563 super(Wizard, self).__init__()
4564
4565 self.gui = gui
4566
4567 self._pages = []
4568 self._currentPage = None
4569
4570 self._loginPage = LoginPage(self)
4571 self._pages.append(self._loginPage)
4572 self._pages.append(FlightSelectionPage(self))
4573 self._pages.append(GateSelectionPage(self))
4574 self._pages.append(RegisterPage(self))
4575 self._pages.append(StudentPage(self))
4576 self._pages.append(ConnectPage(self))
4577 self._payloadPage = PayloadPage(self)
4578 self._pages.append(self._payloadPage)
4579 self._payloadIndex = len(self._pages)
4580 self._pages.append(TimePage(self))
4581 self._routePage = RoutePage(self)
4582 self._pages.append(self._routePage)
4583 self._simBriefSetupPage = SimBriefSetupPage(self)
4584 self._pages.append(self._simBriefSetupPage)
4585 self._simBriefingPage = SimBriefingPage(self)
4586 self._pages.append(self._simBriefingPage)
4587 self._pages.append(FuelPage(self))
4588 self._departureBriefingPage = BriefingPage(self, True)
4589 self._pages.append(self._departureBriefingPage)
4590 self._arrivalBriefingPage = BriefingPage(self, False)
4591 self._pages.append(self._arrivalBriefingPage)
4592 self._arrivalBriefingIndex = len(self._pages)
4593 self._takeoffPage = TakeoffPage(self)
4594 self._pages.append(self._takeoffPage)
4595 self._cruisePage = CruisePage(self)
4596 self._pages.append(self._cruisePage)
4597 self._landingPage = LandingPage(self)
4598 self._pages.append(self._landingPage)
4599 self._finishPage = FinishPage(self)
4600 self._pages.append(self._finishPage)
4601
4602 self._requestedWidth = None
4603 self._requestedHeight = None
4604
4605 self.connect("size-allocate", self._sizeAllocate)
4606
4607 for page in self._pages:
4608 page.show_all()
4609 page.setStyle()
4610
4611 self._initialize()
4612
4613 def _sizeAllocate(self, widget, allocation):
4614 if self._requestedWidth is not None and \
4615 self._requestedHeight is not None:
4616 return
4617
4618 if self._currentPage is not None:
4619 self.remove(self._pages[self._currentPage])
4620
4621 maxWidth = 0
4622 maxHeight = 0
4623 for page in self._pages:
4624 self.add(page)
4625 self.show_all()
4626 pageSizeRequest = page.size_request()
4627 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
4628 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
4629 maxWidth = max(maxWidth, width)
4630 maxHeight = max(maxHeight, height)
4631 self.remove(page)
4632
4633 if self._currentPage is not None:
4634 self.add(self._pages[self._currentPage])
4635
4636 self._requestedWidth = maxWidth
4637 self._requestedHeight = maxHeight
4638 self.set_size_request(maxWidth, maxHeight)
4639
4640 @property
4641 def pilotID(self):
4642 """Get the pilot ID, if given."""
4643 return self._loginPage.pilotID
4644
4645 @property
4646 def entranceExam(self):
4647 """Get whether an entrance exam is about to be taken."""
4648 return self._loginPage.entranceExam
4649
4650 @property
4651 def loggedIn(self):
4652 """Indicate if there was a successful login."""
4653 return self._loginResult is not None
4654
4655 @property
4656 def loginResult(self):
4657 """Get the login result."""
4658 return self._loginResult
4659
4660 def setCurrentPage(self, index, finalize = False, fromPageShift = None):
4661 """Set the current page to the one with the given index.
4662
4663 @param fromPageShift if given, the relative index of one of the
4664 previous pages that should be used as the from-page of the next
4665 page. E.g. if fromPageShift is 1, the previous page will be the
4666 from-page."""
4667 assert index < len(self._pages)
4668
4669 fromPage = self._currentPage
4670 if fromPage is not None:
4671 page = self._pages[fromPage]
4672 if finalize and not page._completed:
4673 page.complete()
4674 self.remove(page)
4675 if fromPageShift is not None:
4676 fromPage -= fromPageShift
4677
4678 self._currentPage = index
4679 page = self._pages[index]
4680 self.add(page)
4681 if page._fromPage is None:
4682 page._fromPage = fromPage
4683 page.initialize()
4684 self.show_all()
4685 if fromPage is not None:
4686 self.grabDefault()
4687
4688 @property
4689 def bookedFlight(self):
4690 """Get the booked flight selected."""
4691 return self._bookedFlight
4692
4693 @property
4694 def numCrew(self):
4695 """Get the number of crew members."""
4696 return self._payloadPage.numCrew
4697
4698 @property
4699 def numPassengers(self):
4700 """Get the number of passengers."""
4701 return self._payloadPage.numPassengers
4702
4703 @property
4704 def bagWeight(self):
4705 """Get the baggage weight."""
4706 return self._payloadPage.bagWeight
4707
4708 @property
4709 def cargoWeight(self):
4710 """Get the cargo weight."""
4711 return self._payloadPage.cargoWeight
4712
4713 @property
4714 def mailWeight(self):
4715 """Get the mail weight."""
4716 return self._payloadPage.mailWeight
4717
4718 @property
4719 def zfw(self):
4720 """Get the calculated ZFW value."""
4721 return 0 if self._bookedFlight is None \
4722 else self._payloadPage.calculateZFW()
4723
4724 @property
4725 def filedCruiseLevel(self):
4726 """Get the filed cruise level."""
4727 return self._routePage.filedCruiseLevel
4728
4729 @property
4730 def filedCruiseAltitude(self):
4731 """Get the filed cruise altitude."""
4732 return self._routePage.filedCruiseLevel * 100
4733
4734 @property
4735 def cruiseAltitude(self):
4736 """Get the cruise altitude."""
4737 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
4738 else self._routePage.filedCruiseLevel
4739 return level * 100
4740
4741 @property
4742 def loggableCruiseAltitude(self):
4743 """Get the cruise altitude that can be logged."""
4744 if self._cruisePage.activated:
4745 return self._cruisePage.loggableCruiseLevel * 100
4746 else:
4747 return 0
4748
4749 @property
4750 def route(self):
4751 """Get the route."""
4752 return self._routePage.route
4753
4754 @property
4755 def alternate(self):
4756 """Get the ICAO code of the alternate airport."""
4757 return self._routePage.alternate
4758
4759 @property
4760 def departureMETAR(self):
4761 """Get the METAR of the departure airport."""
4762 return self._departureBriefingPage.metar
4763
4764 @property
4765 def arrivalMETAR(self):
4766 """Get the METAR of the arrival airport."""
4767 return self._arrivalBriefingPage.metar
4768
4769 @property
4770 def departureRunway(self):
4771 """Get the departure runway."""
4772 return self._takeoffPage.runway
4773
4774 @property
4775 def sid(self):
4776 """Get the SID."""
4777 return self._takeoffPage.sid
4778
4779 @property
4780 def v1(self):
4781 """Get the V1 speed."""
4782 return self._takeoffPage.v1
4783
4784 @property
4785 def vr(self):
4786 """Get the Vr speed."""
4787 return self._takeoffPage.vr
4788
4789 @property
4790 def v2(self):
4791 """Get the V2 speed."""
4792 return self._takeoffPage.v2
4793
4794 @property
4795 def derate(self):
4796 """Get the derate value."""
4797 return self._takeoffPage.derate
4798
4799 @property
4800 def takeoffAntiIceOn(self):
4801 """Get whether the anti-ice system was on during take-off."""
4802 return self._takeoffPage.antiIceOn
4803
4804 @takeoffAntiIceOn.setter
4805 def takeoffAntiIceOn(self, value):
4806 """Set anti-ice on indicator."""
4807 self._takeoffPage.antiIceOn = value
4808
4809 @property
4810 def rtoIndicated(self):
4811 """Get whether the pilot has indicated that an RTO has occured."""
4812 return self._takeoffPage.rtoIndicated
4813
4814 @property
4815 def arrivalRunway(self):
4816 """Get the arrival runway."""
4817 return self._landingPage.runway
4818
4819 @property
4820 def star(self):
4821 """Get the STAR."""
4822 return self._landingPage.star
4823
4824 @property
4825 def transition(self):
4826 """Get the transition."""
4827 return self._landingPage.transition
4828
4829 @property
4830 def approachType(self):
4831 """Get the approach type."""
4832 return self._landingPage.approachType
4833
4834 @property
4835 def vref(self):
4836 """Get the Vref speed."""
4837 return self._landingPage.vref
4838
4839 @property
4840 def landingAntiIceOn(self):
4841 """Get whether the anti-ice system was on during landing."""
4842 return self._landingPage.antiIceOn
4843
4844 @landingAntiIceOn.setter
4845 def landingAntiIceOn(self, value):
4846 """Set anti-ice on indicator."""
4847 self._landingPage.antiIceOn = value
4848
4849 @property
4850 def flightType(self):
4851 """Get the flight type."""
4852 return self._finishPage.flightType
4853
4854 @property
4855 def online(self):
4856 """Get whether the flight was online or not."""
4857 return self._finishPage.online
4858
4859 @property
4860 def usingSimBrief(self):
4861 """Indicate if we are using a SimBrief briefing or not."""
4862 return self._usingSimBrief
4863
4864 @usingSimBrief.setter
4865 def usingSimBrief(self, x):
4866 """Set whether we are using a SimBrief briefing or not."""
4867 self._usingSimBrief = x
4868
4869 def nextPage(self, finalize = True):
4870 """Go to the next page."""
4871 nextPageID = self._pages[self._currentPage].nextPageID
4872 self.jumpPage(1 if nextPageID is None else nextPageID, finalize)
4873
4874 def jumpPage(self, countOrID, finalize = True, fromPageShift = None):
4875 """Go to the page which is 'count' pages after the current one."""
4876 if isinstance(countOrID, str):
4877 targetIndex = self._getIndexOf(countOrID)
4878 else:
4879 targetIndex = self._currentPage + countOrID
4880 self.setCurrentPage(targetIndex,
4881 finalize = finalize, fromPageShift = fromPageShift)
4882
4883 def grabDefault(self):
4884 """Make the default button of the current page the default."""
4885 self._pages[self._currentPage].grabDefault()
4886
4887 def connected(self, fsType, descriptor):
4888 """Called when the connection could be made to the simulator."""
4889 self.nextPage()
4890
4891 def reset(self, loginResult):
4892 """Resets the wizard to go back to the login page."""
4893 self._initialize(keepLoginResult = loginResult is None,
4894 loginResult = loginResult)
4895
4896 def setStage(self, stage):
4897 """Set the flight stage to the given one."""
4898 if stage!=const.STAGE_END:
4899 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
4900
4901 if stage==const.STAGE_TAKEOFF:
4902 self._takeoffPage.allowForward()
4903 elif stage==const.STAGE_LANDING:
4904 if not self._arrivalBriefingPage.metarEdited:
4905 print "Downloading arrival METAR again"
4906 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
4907 [self._bookedFlight.arrivalICAO])
4908
4909 elif stage==const.STAGE_END:
4910 for page in self._pages:
4911 page.flightEnded()
4912
4913 def _initialize(self, keepLoginResult = False, loginResult = None):
4914 """Initialize the wizard."""
4915 if not keepLoginResult:
4916 self._loginResult = loginResult
4917
4918 self._loginCallback = None
4919
4920 self._fleet = None
4921 self._fleetCallback = None
4922
4923 self._bookedFlight = None
4924 self._departureGate = "-"
4925 self._fuelData = None
4926 self._departureNOTAMs = None
4927 self._departureMETAR = None
4928 self._arrivalNOTAMs = None
4929 self._arrivalMETAR = None
4930 self._usingSimBrief = None
4931 self.takeoffRunway = None
4932 self.landingRunway = None
4933
4934 firstPage = 0 if self._loginResult is None else 1
4935 for page in self._pages[firstPage:]:
4936 page.reset()
4937
4938 self.setCurrentPage(firstPage)
4939 #self.setCurrentPage(10)
4940
4941 def login(self, callback, pilotID, password, entranceExam):
4942 """Called when the login button was clicked."""
4943 self._loginCallback = callback
4944 if pilotID is None:
4945 loginResult = self._loginResult
4946 assert loginResult is not None and loginResult.loggedIn
4947 pilotID = loginResult.pilotID
4948 password = loginResult.password
4949 entranceExam = loginResult.entranceExam
4950 busyMessage = xstr("reload_busy")
4951 else:
4952 self._loginResult = None
4953 busyMessage = xstr("login_busy")
4954
4955 self.gui.beginBusy(busyMessage)
4956
4957 self.gui.webHandler.login(self._loginResultCallback,
4958 pilotID, password,
4959 entranceExam = entranceExam)
4960
4961 def reloadFlights(self, callback):
4962 """Reload the flights from the MAVA server."""
4963 self.login(callback, None, None, None)
4964
4965 def cruiseLevelChanged(self):
4966 """Called when the cruise level is changed."""
4967 return self.gui.cruiseLevelChanged()
4968
4969 def metarChanged(self, metar, originator):
4970 """Called when a METER is changed on on of the pages.
4971
4972 originator is the page that originated the changed. It will be used to
4973 determine which METAR (departure or arrival) has changed."""
4974 metar = metar.upper()
4975 if originator in [self._departureBriefingPage, self._takeoffPage]:
4976 self.departureMETARChanged(metar, originator)
4977 else:
4978 self.arrivalMETARChanged(metar, originator)
4979
4980 def departureMETARChanged(self, metar, originator):
4981 """Called when the departure METAR has been edited on one of the
4982 pages.
4983
4984 originator is the page that originated the change. It will not be
4985 called to set the METAR, while others will be."""
4986 for page in [self._departureBriefingPage, self._takeoffPage]:
4987 if page is not originator:
4988 page.changeMETAR(metar)
4989
4990 def arrivalMETARChanged(self, metar, originator):
4991 """Called when the arrival METAR has been edited on one of the
4992 pages.
4993
4994 originator is the page that originated the change. It will not be
4995 called to set the METAR, while others will be."""
4996 for page in [self._arrivalBriefingPage, self._landingPage]:
4997 if page is not originator:
4998 page.changeMETAR(metar)
4999
5000 def _loginResultCallback(self, returned, result):
5001 """The login result callback, called in the web handler's thread."""
5002 gobject.idle_add(self._handleLoginResult, returned, result)
5003
5004 def _handleLoginResult(self, returned, result):
5005 """Handle the login result."""
5006 self.gui.endBusy()
5007 isReload = self._loginResult is not None
5008 if returned:
5009 if result.loggedIn:
5010 self._loginResult = result
5011 else:
5012 if isReload:
5013 message = xstr("reload_failed")
5014 else:
5015 message = xstr("login_entranceExam_invalid"
5016 if self.entranceExam else
5017 xstr("login_invalid"))
5018 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5019 type = MESSAGETYPE_ERROR,
5020 message_format = message)
5021 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5022 dialog.set_title(WINDOW_TITLE_BASE)
5023 if isReload:
5024 secondary = xstr("reload_failed_sec")
5025 else:
5026 secondary = xstr("login_entranceExam_invalid_sec"
5027 if self.entranceExam else
5028 xstr("login_invalid_sec"))
5029 dialog.format_secondary_markup(secondary)
5030 dialog.run()
5031 dialog.hide()
5032 else:
5033 message = xstr("reload_failconn") if isReload \
5034 else xstr("login_failconn")
5035 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5036 type = MESSAGETYPE_ERROR,
5037 message_format = message)
5038 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5039 dialog.set_title(WINDOW_TITLE_BASE)
5040 secondary = xstr("reload_failconn_sec") if isReload \
5041 else xstr("login_failconn_sec")
5042 dialog.format_secondary_markup(secondary)
5043
5044 dialog.run()
5045 dialog.hide()
5046
5047 callback = self._loginCallback
5048 self._loginCallback = None
5049 callback(returned, result)
5050
5051 def getFleet(self, callback, force = False):
5052 """Get the fleet via the GUI and call the given callback."""
5053 self._fleetCallback = callback
5054 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
5055
5056 def _fleetRetrieved(self, fleet):
5057 """Callback for the fleet retrieval."""
5058 self._fleet = fleet
5059 if self._fleetCallback is not None:
5060 self._fleetCallback(fleet)
5061 self._fleetCallback = None
5062
5063 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
5064 """Update the given plane's gate information."""
5065 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
5066 callback = callback)
5067
5068 def updateRTO(self):
5069 """Update the RTO state.
5070
5071 The RTO checkbox will be enabled if the flight has an RTO state and the
5072 comments field contains some text."""
5073 flight = self.gui.flight
5074 rtoEnabled = flight is not None and flight.hasRTO and \
5075 self.gui.hasComments
5076 self._takeoffPage.setRTOEnabled(rtoEnabled)
5077
5078 def commentsChanged(self):
5079 """Called when the comments have changed."""
5080 self.updateRTO()
5081 self._finishPage.updateButtons()
5082
5083 def delayCodesChanged(self):
5084 """Called when the delay codes have changed."""
5085 self._finishPage.updateButtons()
5086
5087 def faultExplanationsChanged(self):
5088 """Called when the faults and their explanations have changed."""
5089 self._finishPage.updateButtons()
5090
5091 def rtoToggled(self, indicated):
5092 """Called when the RTO indication has changed."""
5093 self.gui.rtoToggled(indicated)
5094
5095 def _connectSimulator(self, simulatorType):
5096 """Connect to the simulator."""
5097 self.gui.connectSimulator(self._bookedFlight.aircraftType,
5098 simulatorType)
5099
5100 def _arrivalMETARCallback(self, returned, result):
5101 """Called when the METAR of the arrival airport is retrieved."""
5102 gobject.idle_add(self._handleArrivalMETAR, returned, result)
5103
5104 def _handleArrivalMETAR(self, returned, result):
5105 """Called when the METAR of the arrival airport is retrieved."""
5106 icao = self._bookedFlight.arrivalICAO
5107 if returned and icao in result.metars:
5108 metar = result.metars[icao]
5109 if metar!="":
5110 self._arrivalBriefingPage.setMETAR(metar)
5111
5112 def _getIndexOf(self, pageID):
5113 """Get the index for the given page ID.
5114
5115 It is an assertion failure if the ID is not found."""
5116 for index in range(0, len(self._pages)):
5117 page = self._pages[index]
5118 if page.id==pageID:
5119 return index
5120 assert False
5121
5122#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.