source: src/mlx/gui/flight.py

python3
Last change on this file was 1071:1a5ab6d7fe1b, checked in by István Váradi <ivaradi@…>, 5 weeks ago

The Next button on the flight selection page can only be sensitive if there is at least on flight

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