source: src/mlx/gui/flight.py@ 863:4c7bfec09347

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

The briefing can be printed from the program (re #307)

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