source: src/mlx/gui/flight.py@ 764:8fd245311db5

Last change on this file since 764:8fd245311db5 was 764:8fd245311db5, checked in by István Váradi <ivaradi@…>, 8 years ago

The entry exam is called in the browser when requested and the status is displayed in text as well (re #285)

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