source: src/mlx/gui/flight.py@ 1050:52d9043ae7f2

python3
Last change on this file since 1050:52d9043ae7f2 was 1046:12bbf8604117, checked in by István Váradi <ivaradi@…>, 3 years ago

New style briefing (re #357)

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