source: src/mlx/gui/flight.py@ 1075:98d4c4481dd6

python3
Last change on this file since 1075:98d4c4481dd6 was 1075:98d4c4481dd6, checked in by István Váradi <ivaradi@…>, 15 months ago

The flight wizard pages have new callback functions to prepare for showing/hiding them.

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