source: src/mlx/gui/flight.py@ 865:b452afb620fa

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

The flight selection table popup menu includes printing (re #304)

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