source: src/mlx/gui/flight.py@ 864:b154a5d879cb

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

A flight can be deleted from the flight list (re #308)

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