source: src/mlx/gui/flight.py@ 278:f4860ca38620

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

Double click is handled on the gate selection page

File size: 116.6 KB
Line 
1# The flight handling "wizard"
2
3from mlx.gui.common import *
4
5import mlx.const as const
6import mlx.fs as fs
7import mlx.acft as acft
8from mlx.checks import PayloadChecker
9import mlx.util as util
10from mlx.pirep import PIREP
11from mlx.i18n import xstr
12from mlx.sound import startSound
13import mlx.web as web
14
15import datetime
16import time
17
18#-----------------------------------------------------------------------------
19
20class Page(gtk.Alignment):
21 """A page in the flight wizard."""
22 def __init__(self, wizard, title, help, completedHelp = None):
23 """Construct the page."""
24 super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
25 xscale = 1.0, yscale = 1.0)
26 self.set_padding(padding_top = 4, padding_bottom = 4,
27 padding_left = 12, padding_right = 12)
28
29 frame = gtk.Frame()
30 self.add(frame)
31
32 self._vbox = gtk.VBox()
33 self._vbox.set_homogeneous(False)
34 frame.add(self._vbox)
35
36 eventBox = gtk.EventBox()
37
38 alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
39
40 titleLabel = gtk.Label(title)
41 titleLabel.modify_font(pango.FontDescription("bold 24"))
42 alignment.set_padding(padding_top = 4, padding_bottom = 4,
43 padding_left = 6, padding_right = 0)
44
45 alignment.add(titleLabel)
46 eventBox.add(alignment)
47
48 self._vbox.pack_start(eventBox, False, False, 0)
49
50 self._titleEventBox = eventBox
51 self._titleLabel = titleLabel
52
53 mainBox = gtk.VBox()
54
55 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
56 xscale = 1.0, yscale = 1.0)
57 alignment.set_padding(padding_top = 16, padding_bottom = 16,
58 padding_left = 16, padding_right = 16)
59 alignment.add(mainBox)
60 self._vbox.pack_start(alignment, True, True, 0)
61
62 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
63 xscale = 0.0, yscale = 0.0)
64 alignment.set_padding(padding_top = 0, padding_bottom = 16,
65 padding_left = 0, padding_right = 0)
66
67 self._help = help
68 self._completedHelp = completedHelp
69
70 if self._completedHelp is None or \
71 len(help.splitlines())>=len(completedHelp.splitlines()):
72 longerHelp = help
73 else:
74 longerHelp = completedHelp
75
76 self._helpLabel = gtk.Label(completedHelp)
77 # FIXME: should be a constant in common
78 self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
79 else gtk.JUSTIFY_CENTER)
80 self._helpLabel.set_use_markup(True)
81 alignment.add(self._helpLabel)
82 mainBox.pack_start(alignment, False, False, 0)
83
84 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
85 xscale = 1.0, yscale = 1.0)
86 mainBox.pack_start(self._mainAlignment, True, True, 0)
87
88 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
89 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
90 padding_left = 16, padding_right = 16)
91
92 self._buttonBox = gtk.HBox()
93 self._buttonBox.set_homogeneous(False)
94 self._defaultButton = None
95 buttonAlignment.add(self._buttonBox)
96
97 self._vbox.pack_start(buttonAlignment, False, False, 0)
98
99 self._wizard = wizard
100
101 self._cancelFlightButton = None
102
103 self._completed = False
104 self._fromPage = None
105
106 def setMainWidget(self, widget):
107 """Set the given widget as the main one."""
108 self._mainAlignment.add(widget)
109
110 def addButton(self, label, default = False, sensitive = True,
111 tooltip = None, clicked = None, padding = 4):
112 """Add a button with the given label.
113
114 Return the button object created."""
115 button = gtk.Button(label)
116 self._buttonBox.pack_start(button, False, False, padding)
117 button.set_use_underline(True)
118 if default:
119 button.set_can_default(True)
120 self._defaultButton = button
121 button.set_sensitive(sensitive)
122 if tooltip is not None:
123 button.set_tooltip_text(tooltip)
124 if clicked is not None:
125 button.connect("clicked", clicked)
126 return button
127
128 def addCancelFlightButton(self):
129 """Add the 'Cancel flight' button to the page."""
130 self._cancelFlightButton = \
131 self.addButton(xstr("button_cancelFlight"),
132 sensitive = True,
133 tooltip = xstr("button_cancelFlight_tooltip"),
134 clicked = self._cancelFlight,
135 padding = 16)
136 return self._cancelFlightButton
137
138 def addPreviousButton(self, sensitive = True, clicked = None):
139 """Add the 'Next' button to the page."""
140 return self.addButton(xstr("button_previous"),
141 sensitive = sensitive,
142 tooltip = xstr("button_previous_tooltip"),
143 clicked = clicked)
144
145 def addNextButton(self, default = True, sensitive = True,
146 clicked = None):
147 """Add the 'Next' button to the page."""
148 return self.addButton(xstr("button_next"),
149 default = default,
150 sensitive = sensitive,
151 tooltip = xstr("button_next_tooltip"),
152 clicked = clicked)
153
154 def setStyle(self):
155 """Set the styles of some of the items on the page."""
156 style = self.get_style() if pygobject else self.rc_get_style()
157
158 self._titleEventBox.modify_bg(0, style.bg[3])
159 self._titleLabel.modify_fg(0, style.fg[3])
160
161 def initialize(self):
162 """Initialize the page.
163
164 It sets up the primary help, and calls the activate() function."""
165 self._helpLabel.set_markup(self._help)
166 self._helpLabel.set_sensitive(True)
167 self.activate()
168
169 def activate(self):
170 """Called when this page becomes active.
171
172 This default implementation does nothing."""
173 pass
174
175 def complete(self):
176 """Called when the page is completed.
177
178 It greys out/changes the help text and then calls finalize()."""
179 self.finalize()
180 if self._completedHelp is None:
181 self._helpLabel.set_sensitive(False)
182 else:
183 self._helpLabel.set_markup(self._completedHelp)
184 self._completed = True
185
186 def finalize(self):
187 """Called when the page is finalized."""
188 pass
189
190 def grabDefault(self):
191 """If the page has a default button, make it the default one."""
192 if self._defaultButton is not None:
193 self._defaultButton.grab_default()
194
195 def reset(self):
196 """Reset the page if the wizard is reset."""
197 self._completed = False
198 self._fromPage = None
199 if self._cancelFlightButton is not None:
200 self._cancelFlightButton.set_sensitive(True)
201
202 def goBack(self):
203 """Go to the page we were invoked from."""
204 assert self._fromPage is not None
205
206 self._wizard.setCurrentPage(self._fromPage, finalize = False)
207
208 def flightEnded(self):
209 """Called when the flight has ended.
210
211 This default implementation disables the cancel flight button."""
212 if self._cancelFlightButton is not None:
213 self._cancelFlightButton.set_sensitive(False)
214
215 def _cancelFlight(self, button):
216 """Called when the Cancel flight button is clicked."""
217 self._wizard.gui.cancelFlight()
218
219#-----------------------------------------------------------------------------
220
221class LoginPage(Page):
222 """The login page."""
223 def __init__(self, wizard):
224 """Construct the login page."""
225 super(LoginPage, self).__init__(wizard, xstr("login"),
226 xstr("loginHelp"))
227
228 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
229 xscale = 0.0, yscale = 0.0)
230
231 table = gtk.Table(4, 2)
232 table.set_row_spacings(4)
233 table.set_col_spacings(32)
234 alignment.add(table)
235 self.setMainWidget(alignment)
236
237 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
238 xscale = 0.0, yscale = 0.0)
239 label = gtk.Label(xstr("label_pilotID"))
240 label.set_use_underline(True)
241 labelAlignment.add(label)
242 table.attach(labelAlignment, 0, 1, 0, 1)
243
244 self._pilotID = gtk.Entry()
245 self._pilotID.connect("changed", self._setControls)
246 self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
247 table.attach(self._pilotID, 1, 2, 0, 1)
248 label.set_mnemonic_widget(self._pilotID)
249
250 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
251 xscale = 0.0, yscale = 0.0)
252 label = gtk.Label(xstr("label_password"))
253 label.set_use_underline(True)
254 labelAlignment.add(label)
255 table.attach(labelAlignment, 0, 1, 1, 2)
256
257 self._password = gtk.Entry()
258 self._password.set_visibility(False)
259 self._password.connect("changed", self._setControls)
260 self._password.set_tooltip_text(xstr("login_password_tooltip"))
261 table.attach(self._password, 1, 2, 1, 2)
262 label.set_mnemonic_widget(self._password)
263
264 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
265 self._rememberButton.set_use_underline(True)
266 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
267 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
268
269 self._entranceExam = gtk.CheckButton(xstr("login_entranceExam"))
270 self._entranceExam.set_use_underline(True)
271 self._entranceExam.set_tooltip_text(xstr("login_entranceExam_tooltip"))
272 self._entranceExam.connect("toggled", self._setControls)
273 table.attach(self._entranceExam, 1, 2, 3, 4, ypadding = 12)
274
275 self.addButton(xstr("button_offline"),
276 clicked = self._offlineClicked,
277 tooltip = xstr("button_offline_tooltip"))
278
279 self._loginButton = self.addButton(xstr("button_login"), default = True)
280 self._loginButton.connect("clicked", self._loginClicked)
281 self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
282
283
284 @property
285 def entranceExam(self):
286 """Get whether an entrance exam is being performed."""
287 return self._entranceExam.get_active() and \
288 self._pilotID.get_text()!=""
289
290 @property
291 def pilotID(self):
292 """Get the pilot ID, if given."""
293 return self._pilotID.get_text()
294
295 def activate(self):
296 """Activate the page."""
297 config = self._wizard.gui.config
298 self._pilotID.set_text(config.pilotID)
299 self._password.set_text(config.password)
300 self._rememberButton.set_active(config.rememberPassword)
301 self._setControls(None)
302
303 def _setControls(self, entry = None):
304 """Set the sensitivity of the various controls.
305
306 The login button is sensitive only if both the pilot ID and the
307 password fields contain values.
308
309 The password field is sensitive only, if the entrance exam checkbox is
310 not selected.
311
312 The remember password checkbox is sensitive only, if the password field
313 contains text.
314
315 The entrance exam checkbox is sensitive only, if the pilot ID is not
316 empty."""
317 pilotID = self._pilotID.get_text()
318 password = self._password.get_text()
319 entranceExam = self._entranceExam.get_active()
320 self._password.set_sensitive(not entranceExam)
321 self._rememberButton.set_sensitive(password!="" and not entranceExam)
322 self._entranceExam.set_sensitive(pilotID!="")
323 self._loginButton.set_sensitive(pilotID!="" and
324 (password!="" or entranceExam))
325
326 def _offlineClicked(self, button):
327 """Called when the offline button was clicked."""
328 self._wizard.nextPage()
329
330 def _loginClicked(self, button):
331 """Called when the login button was clicked."""
332 self._wizard.login(self._handleLoginResult,
333 self._pilotID.get_text(),
334 self._password.get_text(),
335 self.entranceExam)
336
337 def _handleLoginResult(self, returned, result):
338 """Handle the login result."""
339 self._loginButton.set_sensitive(True)
340 if returned and result.loggedIn:
341 config = self._wizard.gui.config
342
343 config.pilotID = self._pilotID.get_text()
344
345 rememberPassword = self._rememberButton.get_active()
346 config.password = result.password if rememberPassword else ""
347
348 config.rememberPassword = rememberPassword
349
350 config.save()
351 self._wizard.nextPage()
352
353#-----------------------------------------------------------------------------
354
355class FlightSelectionPage(Page):
356 """The page to select the flight."""
357 def __init__(self, wizard):
358 """Construct the flight selection page."""
359 help = xstr("flightsel_help")
360 completedHelp = xstr("flightsel_chelp")
361 super(FlightSelectionPage, self).__init__(wizard, xstr("flightsel_title"),
362 help, completedHelp = completedHelp)
363
364
365 self._listStore = gtk.ListStore(str, str, str, str)
366 self._flightList = gtk.TreeView(self._listStore)
367 column = gtk.TreeViewColumn(xstr("flightsel_no"), gtk.CellRendererText(),
368 text = 1)
369 column.set_expand(True)
370 self._flightList.append_column(column)
371 column = gtk.TreeViewColumn(xstr("flightsel_deptime"), gtk.CellRendererText(),
372 text = 0)
373 column.set_expand(True)
374 self._flightList.append_column(column)
375 column = gtk.TreeViewColumn(xstr("flightsel_from"), gtk.CellRendererText(),
376 text = 2)
377 column.set_expand(True)
378 self._flightList.append_column(column)
379 column = gtk.TreeViewColumn(xstr("flightsel_to"), gtk.CellRendererText(),
380 text = 3)
381 column.set_expand(True)
382 self._flightList.append_column(column)
383 self._flightList.connect("row-activated", self._rowActivated)
384 self._flightList.connect("button-press-event", self._listButtonPressed)
385
386 self._flightListPopupMenu = None
387
388 flightSelection = self._flightList.get_selection()
389 flightSelection.connect("changed", self._selectionChanged)
390
391 scrolledWindow = gtk.ScrolledWindow()
392 scrolledWindow.add(self._flightList)
393 scrolledWindow.set_size_request(400, -1)
394 # FIXME: these should be constants in common.py
395 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
396 else gtk.POLICY_AUTOMATIC,
397 gtk.PolicyType.AUTOMATIC if pygobject
398 else gtk.POLICY_AUTOMATIC)
399 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
400 else gtk.SHADOW_IN)
401
402 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
403 alignment.add(scrolledWindow)
404
405 self.setMainWidget(alignment)
406
407 self._saveButton = self.addButton(xstr("flightsel_save"),
408 sensitive = False,
409 clicked = self._saveClicked,
410 tooltip = xstr("flightsel_save_tooltip"))
411 self._saveDialog = None
412
413 self._refreshButton = self.addButton(xstr("flightsel_refresh"),
414 sensitive = True,
415 clicked = self._refreshClicked,
416 tooltip = xstr("flightsel_refresh_tooltip"))
417
418 self._loadButton = self.addButton(xstr("flightsel_load"),
419 sensitive = True,
420 tooltip = xstr("flightsel_load_tooltip"))
421 self._loadButton.connect("clicked", self._loadButtonClicked)
422 self._loadDialog = None
423
424 self._button = self.addNextButton(sensitive = False,
425 clicked = self._forwardClicked)
426
427 self._flights = []
428
429 def activate(self):
430 """Fill the flight list."""
431 self._flightList.set_sensitive(True)
432 self._loadButton.set_sensitive(True)
433 self._refreshButton.set_sensitive(self._wizard.loggedIn)
434 self._buildFlights()
435
436 def finalize(self):
437 """Finalize the page."""
438 self._flightList.set_sensitive(False)
439 self._loadButton.set_sensitive(False)
440 self._refreshButton.set_sensitive(False)
441
442 def _buildFlights(self):
443 """Rebuild the flights from the login result."""
444 self._flights = []
445 self._listStore.clear()
446 if self._wizard.loggedIn:
447 for flight in self._wizard.loginResult.flights:
448 self._addFlight(flight)
449
450 def _addFlight(self, flight):
451 """Add the given file to the list of flights."""
452 self._flights.append(flight)
453 self._listStore.append([str(flight.departureTime),
454 flight.callsign,
455 flight.departureICAO,
456 flight.arrivalICAO])
457
458 def _saveClicked(self, button):
459 """Called when the Save flight button is clicked."""
460 self._saveSelected()
461
462 def _saveSelected(self):
463 """Save the selected flight."""
464 flight = self._getSelectedFlight()
465 date = flight.departureTime.date()
466 name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
467 (date.year, date.month, date.day, flight.callsign,
468 flight.departureICAO, flight.arrivalICAO)
469
470 dialog = self._getSaveDialog()
471 dialog.set_current_name(name)
472 dialog.show_all()
473 response = dialog.run()
474 dialog.hide()
475
476 if response==RESPONSETYPE_OK:
477 fileName = text2unicode(dialog.get_filename())
478 print "Saving", fileName
479 try:
480 with open(fileName, "wt") as f:
481 flight.writeIntoFile(f)
482 except Exception, e:
483 print "Failed to save flight:", str(e)
484 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
485 type = MESSAGETYPE_ERROR,
486 message_format =
487 xstr("flightsel_save_failed"))
488 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
489 dialog.set_title(WINDOW_TITLE_BASE)
490 secondary = xstr("flightsel_save_failed_sec")
491 dialog.format_secondary_markup(secondary)
492 dialog.run()
493 dialog.hide()
494
495 def _refreshClicked(self, button):
496 """Called when the refresh button is clicked."""
497 self._wizard.reloadFlights(self._refreshCallback)
498
499 def _refreshCallback(self, returned, result):
500 """Callback for the refresh."""
501 if returned and result.loggedIn:
502 self._buildFlights()
503
504 def _selectionChanged(self, selection):
505 """Called when the selection is changed."""
506 selected = selection.count_selected_rows()==1
507 self._saveButton.set_sensitive(selected)
508 self._button.set_sensitive(selected)
509
510 def _loadButtonClicked(self, loadButton):
511 """Called when the load a flight button is clicked."""
512 dialog = self._getLoadDialog()
513 dialog.show_all()
514 response = dialog.run()
515 dialog.hide()
516
517 if response==RESPONSETYPE_OK:
518 fileName = text2unicode(dialog.get_filename())
519 print "Loading", fileName
520 bookedFlight = web.BookedFlight()
521 try:
522 with open(fileName, "rt") as f:
523 bookedFlight.readFromFile(f)
524 self._addFlight(bookedFlight)
525 except Exception, e:
526 print "Failed to load flight:", str(e)
527 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
528 type = MESSAGETYPE_ERROR,
529 message_format =
530 xstr("flightsel_load_failed"))
531 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
532 dialog.set_title(WINDOW_TITLE_BASE)
533 secondary = xstr("flightsel_load_failed_sec")
534 dialog.format_secondary_markup(secondary)
535 dialog.run()
536 dialog.hide()
537
538 def _forwardClicked(self, button):
539 """Called when the forward button was clicked."""
540 if self._completed:
541 self._wizard.jumpPage(self._nextDistance, finalize = False)
542 else:
543 self._flightSelected()
544
545 def _rowActivated(self, flightList, path, column):
546 """Called when a row is activated."""
547 if not self._completed:
548 self._flightSelected()
549
550 def _flightSelected(self):
551 """Called when a flight has been selected."""
552 flight = self._getSelectedFlight()
553 self._wizard._bookedFlight = flight
554 self._wizard.gui.enableFlightInfo()
555
556 self._updateDepartureGate()
557
558 def _getSelectedFlight(self):
559 """Get the currently selected flight."""
560 selection = self._flightList.get_selection()
561 (listStore, iter) = selection.get_selected()
562 path = listStore.get_path(iter)
563 [index] = path.get_indices() if pygobject else path
564
565 return self._flights[index]
566
567 def _listButtonPressed(self, widget, event):
568 """Called when a mouse button is pressed on the flight list."""
569 if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
570 return
571
572 (path, _, _, _) = self._flightList.get_path_at_pos(int(event.x),
573 int(event.y))
574 selection = self._flightList.get_selection()
575 selection.unselect_all()
576 selection.select_path(path)
577
578 menu = self._getListPopupMenu()
579 if pygobject:
580 menu.popup(None, None, None, None, event.button, event.time)
581 else:
582 menu.popup(None, None, None, event.button, event.time)
583
584 def _updateDepartureGate(self):
585 """Update the departure gate for the booked flight."""
586 flight = self._wizard._bookedFlight
587 if self._wizard.gui.config.onlineGateSystem and \
588 self._wizard.loggedIn and not self._wizard.entranceExam:
589 if flight.departureICAO=="LHBP":
590 self._wizard.getFleet(self._fleetRetrieved)
591 else:
592 self._wizard.updatePlane(self._planeUpdated,
593 flight.tailNumber,
594 const.PLANE_AWAY)
595 else:
596 self._nextDistance = 2
597 self._wizard.jumpPage(2)
598
599 def _fleetRetrieved(self, fleet):
600 """Called when the fleet has been retrieved."""
601 if fleet is None:
602 self._nextDistance = 2
603 self._wizard.jumpPage(2)
604 else:
605 plane = fleet[self._wizard._bookedFlight.tailNumber]
606 if plane is None:
607 self._nextDistance = 2
608 self._wizard.jumpPage(2)
609 elif plane.gateNumber is not None and \
610 not fleet.isGateConflicting(plane):
611 self._wizard._departureGate = plane.gateNumber
612 self._nextDistance = 2
613 self._wizard.jumpPage(2)
614 else:
615 self._nextDistance = 1
616 self._wizard.nextPage()
617
618 def _planeUpdated(self, success):
619 """Callback for the plane updating."""
620 self._nextDistance = 2
621 self._wizard.jumpPage(2)
622
623 def _getSaveDialog(self):
624 """Get the dialog to load a flight file."""
625 if self._saveDialog is not None:
626 return self._saveDialog
627
628 gui = self._wizard.gui
629 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
630 xstr("flightsel_save_title"),
631 action = FILE_CHOOSER_ACTION_SAVE,
632 buttons = (gtk.STOCK_CANCEL,
633 RESPONSETYPE_CANCEL,
634 gtk.STOCK_OK, RESPONSETYPE_OK),
635 parent = gui.mainWindow)
636 dialog.set_modal(True)
637 dialog.set_do_overwrite_confirmation(True)
638
639 filter = gtk.FileFilter()
640 filter.set_name(xstr("flightsel_filter_flights"))
641 filter.add_pattern("*.vaflight")
642 dialog.add_filter(filter)
643
644 filter = gtk.FileFilter()
645 filter.set_name(xstr("file_filter_all"))
646 filter.add_pattern("*.*")
647 dialog.add_filter(filter)
648
649 self._saveDialog = dialog
650
651 return dialog
652
653 def _getLoadDialog(self):
654 """Get the dialog to load a flight file."""
655 if self._loadDialog is not None:
656 return self._loadDialog
657
658 gui = self._wizard.gui
659 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
660 xstr("flightsel_load_title"),
661 action = FILE_CHOOSER_ACTION_OPEN,
662 buttons = (gtk.STOCK_CANCEL,
663 RESPONSETYPE_CANCEL,
664 gtk.STOCK_OK, RESPONSETYPE_OK),
665 parent = gui.mainWindow)
666 dialog.set_modal(True)
667
668 filter = gtk.FileFilter()
669 filter.set_name(xstr("flightsel_filter_flights"))
670 filter.add_pattern("*.vaflight")
671 dialog.add_filter(filter)
672
673 filter = gtk.FileFilter()
674 filter.set_name(xstr("file_filter_all"))
675 filter.add_pattern("*.*")
676 dialog.add_filter(filter)
677
678 self._loadDialog = dialog
679
680 return dialog
681
682 def _getListPopupMenu(self):
683 """Get the flight list popup menu."""
684 if self._flightListPopupMenu is None:
685 menu = gtk.Menu()
686
687 menuItem = gtk.MenuItem()
688 menuItem.set_label(xstr("flightsel_popup_select"))
689 menuItem.set_use_underline(True)
690 menuItem.connect("activate", self._popupSelect)
691 menuItem.show()
692
693 menu.append(menuItem)
694
695 menuItem = gtk.MenuItem()
696 menuItem.set_label(xstr("flightsel_popup_save"))
697 menuItem.set_use_underline(True)
698 menuItem.connect("activate", self._popupSave)
699 menuItem.show()
700
701 menu.append(menuItem)
702
703 self._flightListPopupMenu = menu
704
705 return self._flightListPopupMenu
706
707 def _popupSelect(self, menuItem):
708 """Called when the Select menu item is activated in the popup menu."""
709 if not self._completed:
710 self._flightSelected()
711
712 def _popupSave(self, menuItem):
713 """Called when the Save menu item is activated in the popup menu."""
714 if not self._completed:
715 self._saveSelected()
716
717#-----------------------------------------------------------------------------
718
719class GateSelectionPage(Page):
720 """Page to select a free gate at LHBP.
721 This page should be displayed only if we have fleet information!."""
722 def __init__(self, wizard):
723 """Construct the gate selection page."""
724 super(GateSelectionPage, self).__init__(wizard, xstr("gatesel_title"),
725 xstr("gatesel_help"))
726
727 self._listStore = gtk.ListStore(str)
728 self._gateList = gtk.TreeView(self._listStore)
729 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
730 text = 0)
731 column.set_expand(True)
732 self._gateList.append_column(column)
733 self._gateList.set_headers_visible(False)
734 self._gateList.connect("row-activated", self._rowActivated)
735
736 gateSelection = self._gateList.get_selection()
737 gateSelection.connect("changed", self._selectionChanged)
738
739 scrolledWindow = gtk.ScrolledWindow()
740 scrolledWindow.add(self._gateList)
741 scrolledWindow.set_size_request(50, -1)
742 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
743 else gtk.POLICY_AUTOMATIC,
744 gtk.PolicyType.AUTOMATIC if pygobject
745 else gtk.POLICY_AUTOMATIC)
746 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
747 else gtk.SHADOW_IN)
748
749 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
750 alignment.add(scrolledWindow)
751
752 self.setMainWidget(alignment)
753
754 self.addCancelFlightButton()
755
756 self.addPreviousButton(clicked = self._backClicked)
757
758 self._button = self.addNextButton(sensitive = False,
759 clicked = self._forwardClicked)
760
761 def activate(self):
762 """Fill the gate list."""
763 self._listStore.clear()
764 self._gateList.set_sensitive(True)
765 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
766 for gateNumber in const.lhbpGateNumbers:
767 if gateNumber not in occupiedGateNumbers:
768 self._listStore.append([gateNumber])
769
770 def finalize(self):
771 """Finalize the page."""
772 self._gateList.set_sensitive(False)
773
774 def _selectionChanged(self, selection):
775 """Called when the selection is changed."""
776 self._button.set_sensitive(selection.count_selected_rows()==1)
777
778 def _backClicked(self, button):
779 """Called when the Back button is pressed."""
780 self.goBack()
781
782 def _forwardClicked(self, button):
783 """Called when the forward button is clicked."""
784 if not self._completed:
785 self._gateSelected()
786 else:
787 self._wizard.nextPage()
788
789 def _rowActivated(self, flightList, path, column):
790 """Called when a row is activated."""
791 if not self._completed:
792 self._gateSelected()
793
794 def _gateSelected(self):
795 """Called when a gate has been selected."""
796 selection = self._gateList.get_selection()
797 (listStore, iter) = selection.get_selected()
798 (gateNumber,) = listStore.get(iter, 0)
799
800 self._wizard._departureGate = gateNumber
801
802 self._wizard.updatePlane(self._planeUpdated,
803 self._wizard._bookedFlight.tailNumber,
804 const.PLANE_HOME, gateNumber)
805
806 def _planeUpdated(self, success):
807 """Callback for the plane updating call."""
808 if success is None or success:
809 self._wizard.nextPage()
810 else:
811 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
812 type = MESSAGETYPE_ERROR,
813 message_format = xstr("gatesel_conflict"))
814 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
815 dialog.set_title(WINDOW_TITLE_BASE)
816 dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
817 dialog.run()
818 dialog.hide()
819
820 self._wizard.getFleet(self._fleetRetrieved)
821
822 def _fleetRetrieved(self, fleet):
823 """Called when the fleet has been retrieved."""
824 if fleet is None:
825 self._wizard.nextPage()
826 else:
827 self.activate()
828
829#-----------------------------------------------------------------------------
830
831class ConnectPage(Page):
832 """Page which displays the departure airport and gate (if at LHBP)."""
833 def __init__(self, wizard):
834 """Construct the connect page."""
835 help = "Load the aircraft below into the simulator and park it\n" \
836 "at the given airport, at the gate below, if present.\n\n" \
837 "Then press the Connect button to connect to the simulator."
838 completedHelp = "The basic data of your flight can be read below."
839 super(ConnectPage, self).__init__(wizard, xstr("connect_title"),
840 xstr("connect_help"),
841 completedHelp = xstr("connect_chelp"))
842
843 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
844 xscale = 0.0, yscale = 0.0)
845
846 table = gtk.Table(5, 2)
847 table.set_row_spacings(4)
848 table.set_col_spacings(16)
849 table.set_homogeneous(True)
850 alignment.add(table)
851 self.setMainWidget(alignment)
852
853 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
854 label = gtk.Label(xstr("connect_flightno"))
855 labelAlignment.add(label)
856 table.attach(labelAlignment, 0, 1, 0, 1)
857
858 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
859 self._flightNumber = gtk.Label()
860 self._flightNumber.set_width_chars(9)
861 self._flightNumber.set_alignment(0.0, 0.5)
862 labelAlignment.add(self._flightNumber)
863 table.attach(labelAlignment, 1, 2, 0, 1)
864
865 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
866 label = gtk.Label(xstr("connect_acft"))
867 labelAlignment.add(label)
868 table.attach(labelAlignment, 0, 1, 1, 2)
869
870 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
871 self._aircraft = gtk.Label()
872 self._aircraft.set_width_chars(25)
873 self._aircraft.set_alignment(0.0, 0.5)
874 labelAlignment.add(self._aircraft)
875 table.attach(labelAlignment, 1, 2, 1, 2)
876
877 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
878 label = gtk.Label(xstr("connect_tailno"))
879 labelAlignment.add(label)
880 table.attach(labelAlignment, 0, 1, 2, 3)
881
882 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
883 self._tailNumber = gtk.Label()
884 self._tailNumber.set_width_chars(10)
885 self._tailNumber.set_alignment(0.0, 0.5)
886 labelAlignment.add(self._tailNumber)
887 table.attach(labelAlignment, 1, 2, 2, 3)
888
889 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
890 label = gtk.Label(xstr("connect_airport"))
891 labelAlignment.add(label)
892 table.attach(labelAlignment, 0, 1, 3, 4)
893
894 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
895 self._departureICAO = gtk.Label()
896 self._departureICAO.set_width_chars(6)
897 self._departureICAO.set_alignment(0.0, 0.5)
898 labelAlignment.add(self._departureICAO)
899 table.attach(labelAlignment, 1, 2, 3, 4)
900
901 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
902 label = gtk.Label(xstr("connect_gate"))
903 labelAlignment.add(label)
904 table.attach(labelAlignment, 0, 1, 4, 5)
905
906 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
907 self._departureGate = gtk.Label()
908 self._departureGate.set_width_chars(5)
909 self._departureGate.set_alignment(0.0, 0.5)
910 labelAlignment.add(self._departureGate)
911 table.attach(labelAlignment, 1, 2, 4, 5)
912
913 self.addCancelFlightButton()
914
915 self.addPreviousButton(clicked = self._backClicked)
916
917 self._button = self.addButton(xstr("button_connect"), default = True,
918 tooltip = xstr("button_connect_tooltip"))
919 self._clickedID = self._button.connect("clicked", self._connectClicked)
920
921 def activate(self):
922 """Setup the departure information."""
923 self._button.set_label(xstr("button_connect"))
924 self._button.set_use_underline(True)
925 self._button.set_tooltip_text(xstr("button_connect_tooltip"))
926 self._button.disconnect(self._clickedID)
927 self._clickedID = self._button.connect("clicked", self._connectClicked)
928
929 bookedFlight = self._wizard._bookedFlight
930
931 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
932
933 aircraftType = aircraftNames[bookedFlight.aircraftType]
934 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
935
936 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
937
938 icao = bookedFlight.departureICAO
939 self._departureICAO.set_markup("<b>" + icao + "</b>")
940 gate = self._wizard._departureGate
941 if gate!="-":
942 gate = "<b>" + gate + "</b>"
943 self._departureGate.set_markup(gate)
944
945 def finalize(self):
946 """Finalize the page."""
947 self._button.set_label(xstr("button_next"))
948 self._button.set_use_underline(True)
949 self._button.set_tooltip_text(xstr("button_next_tooltip"))
950 self._button.disconnect(self._clickedID)
951 self._clickedID = self._button.connect("clicked", self._forwardClicked)
952
953 def _backClicked(self, button):
954 """Called when the Back button is pressed."""
955 self.goBack()
956
957 def _connectClicked(self, button):
958 """Called when the Connect button is pressed."""
959 self._wizard._connectSimulator()
960
961 def _forwardClicked(self, button):
962 """Called when the Forward button is pressed."""
963 self._wizard.nextPage()
964
965#-----------------------------------------------------------------------------
966
967class PayloadPage(Page):
968 """Page to allow setting up the payload."""
969 def __init__(self, wizard):
970 """Construct the page."""
971 super(PayloadPage, self).__init__(wizard, xstr("payload_title"),
972 xstr("payload_help"),
973 completedHelp = xstr("payload_chelp"))
974
975 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
976 xscale = 0.0, yscale = 0.0)
977
978 table = gtk.Table(7, 3)
979 table.set_row_spacings(4)
980 table.set_col_spacings(16)
981 table.set_homogeneous(False)
982 alignment.add(table)
983 self.setMainWidget(alignment)
984
985 label = gtk.Label(xstr("payload_crew"))
986 label.set_alignment(0.0, 0.5)
987 table.attach(label, 0, 1, 0, 1)
988
989 self._numCrew = gtk.Label()
990 self._numCrew.set_width_chars(6)
991 self._numCrew.set_alignment(1.0, 0.5)
992 table.attach(self._numCrew, 1, 2, 0, 1)
993
994 label = gtk.Label(xstr("payload_pax"))
995 label.set_alignment(0.0, 0.5)
996 table.attach(label, 0, 1, 1, 2)
997
998 self._numPassengers = gtk.Label()
999 self._numPassengers.set_width_chars(6)
1000 self._numPassengers.set_alignment(1.0, 0.5)
1001 table.attach(self._numPassengers, 1, 2, 1, 2)
1002
1003 label = gtk.Label(xstr("payload_bag"))
1004 label.set_alignment(0.0, 0.5)
1005 table.attach(label, 0, 1, 2, 3)
1006
1007 self._bagWeight = gtk.Label()
1008 self._bagWeight.set_width_chars(6)
1009 self._bagWeight.set_alignment(1.0, 0.5)
1010 table.attach(self._bagWeight, 1, 2, 2, 3)
1011
1012 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
1013
1014 label = gtk.Label(xstr("payload_cargo"))
1015 label.set_use_underline(True)
1016 label.set_alignment(0.0, 0.5)
1017 table.attach(label, 0, 1, 3, 4)
1018
1019 self._cargoWeight = IntegerEntry(defaultValue = 0)
1020 self._cargoWeight.set_width_chars(6)
1021 self._cargoWeight.connect("integer-changed", self._cargoWeightChanged)
1022 self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
1023 table.attach(self._cargoWeight, 1, 2, 3, 4)
1024 label.set_mnemonic_widget(self._cargoWeight)
1025
1026 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
1027
1028 label = gtk.Label(xstr("payload_mail"))
1029 label.set_alignment(0.0, 0.5)
1030 table.attach(label, 0, 1, 4, 5)
1031
1032 self._mailWeight = gtk.Label()
1033 self._mailWeight.set_width_chars(6)
1034 self._mailWeight.set_alignment(1.0, 0.5)
1035 table.attach(self._mailWeight, 1, 2, 4, 5)
1036
1037 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
1038
1039 label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
1040 label.set_alignment(0.0, 0.5)
1041 label.set_use_markup(True)
1042 table.attach(label, 0, 1, 5, 6)
1043
1044 self._calculatedZFW = gtk.Label()
1045 self._calculatedZFW.set_width_chars(6)
1046 self._calculatedZFW.set_alignment(1.0, 0.5)
1047 table.attach(self._calculatedZFW, 1, 2, 5, 6)
1048
1049 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
1050
1051 self._zfwButton = gtk.Button(xstr("payload_fszfw"))
1052 self._zfwButton.set_use_underline(True)
1053 self._zfwButton.connect("clicked", self._zfwRequested)
1054 self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
1055 table.attach(self._zfwButton, 0, 1, 6, 7)
1056
1057 self._simulatorZFW = gtk.Label("-")
1058 self._simulatorZFW.set_width_chars(6)
1059 self._simulatorZFW.set_alignment(1.0, 0.5)
1060 table.attach(self._simulatorZFW, 1, 2, 6, 7)
1061 self._simulatorZFWValue = None
1062
1063 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
1064
1065 self.addCancelFlightButton()
1066 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1067 self._button = self.addNextButton(clicked = self._forwardClicked)
1068
1069 @property
1070 def cargoWeight(self):
1071 """Get the cargo weight entered."""
1072 return self._cargoWeight.get_int()
1073
1074 def activate(self):
1075 """Setup the information."""
1076 bookedFlight = self._wizard._bookedFlight
1077 self._numCrew.set_text(str(bookedFlight.numCrew))
1078 self._numPassengers.set_text(str(bookedFlight.numPassengers))
1079 self._bagWeight.set_text(str(bookedFlight.bagWeight))
1080 self._cargoWeight.set_int(bookedFlight.cargoWeight)
1081 self._cargoWeight.set_sensitive(True)
1082 self._mailWeight.set_text(str(bookedFlight.mailWeight))
1083 self._simulatorZFW.set_text("-")
1084 self._simulatorZFWValue = None
1085 self._zfwButton.set_sensitive(True)
1086 self._updateCalculatedZFW()
1087
1088 def finalize(self):
1089 """Finalize the payload page."""
1090 self._cargoWeight.set_sensitive(False)
1091 self._wizard.gui.initializeWeightHelp()
1092
1093 def calculateZFW(self):
1094 """Calculate the ZFW value."""
1095 zfw = self._wizard.gui._flight.aircraft.dow
1096 bookedFlight = self._wizard._bookedFlight
1097 zfw += (bookedFlight.numCrew + bookedFlight.numPassengers) * 82
1098 zfw += bookedFlight.bagWeight
1099 zfw += self._cargoWeight.get_int()
1100 zfw += bookedFlight.mailWeight
1101 return zfw
1102
1103 def _updateCalculatedZFW(self):
1104 """Update the calculated ZFW"""
1105 zfw = self.calculateZFW()
1106
1107 markupBegin = "<b>"
1108 markupEnd = "</b>"
1109 if self._simulatorZFWValue is not None and \
1110 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
1111 markupBegin += '<span foreground="red">'
1112 markupEnd = "</span>" + markupEnd
1113 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
1114
1115 def _cargoWeightChanged(self, entry, weight):
1116 """Called when the cargo weight has changed."""
1117 self._updateCalculatedZFW()
1118
1119 def _zfwRequested(self, button):
1120 """Called when the ZFW is requested from the simulator."""
1121 self._zfwButton.set_sensitive(False)
1122 self._backButton.set_sensitive(False)
1123 self._button.set_sensitive(False)
1124 gui = self._wizard.gui
1125 gui.beginBusy(xstr("payload_zfw_busy"))
1126 gui.simulator.requestZFW(self._handleZFW)
1127
1128 def _handleZFW(self, zfw):
1129 """Called when the ZFW value is retrieved."""
1130 gobject.idle_add(self._processZFW, zfw)
1131
1132 def _processZFW(self, zfw):
1133 """Process the given ZFW value received from the simulator."""
1134 self._wizard.gui.endBusy()
1135 self._zfwButton.set_sensitive(True)
1136 self._backButton.set_sensitive(True)
1137 self._button.set_sensitive(True)
1138 self._simulatorZFWValue = zfw
1139 self._simulatorZFW.set_text("%.0f" % (zfw,))
1140 self._updateCalculatedZFW()
1141
1142 def _forwardClicked(self, button):
1143 """Called when the forward button is clicked."""
1144 self._wizard.nextPage()
1145
1146 def _backClicked(self, button):
1147 """Called when the Back button is pressed."""
1148 self.goBack()
1149
1150#-----------------------------------------------------------------------------
1151
1152class TimePage(Page):
1153 """Page displaying the departure and arrival times and allows querying the
1154 current time from the flight simulator."""
1155 def __init__(self, wizard):
1156 super(TimePage, self).__init__(wizard, xstr("time_title"),
1157 xstr("time_help"),
1158 completedHelp = xstr("time_chelp"))
1159
1160 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1161 xscale = 0.0, yscale = 0.0)
1162
1163 table = gtk.Table(3, 2)
1164 table.set_row_spacings(4)
1165 table.set_col_spacings(16)
1166 table.set_homogeneous(False)
1167 alignment.add(table)
1168 self.setMainWidget(alignment)
1169
1170 label = gtk.Label(xstr("time_departure"))
1171 label.set_alignment(0.0, 0.5)
1172 table.attach(label, 0, 1, 0, 1)
1173
1174 self._departure = gtk.Label()
1175 self._departure.set_alignment(0.0, 0.5)
1176 table.attach(self._departure, 1, 2, 0, 1)
1177
1178 label = gtk.Label(xstr("time_arrival"))
1179 label.set_alignment(0.0, 0.5)
1180 table.attach(label, 0, 1, 1, 2)
1181
1182 self._arrival = gtk.Label()
1183 self._arrival.set_alignment(0.0, 0.5)
1184 table.attach(self._arrival, 1, 2, 1, 2)
1185
1186 self._timeButton = gtk.Button(xstr("time_fs"))
1187 self._timeButton.set_use_underline(True)
1188 self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
1189 self._timeButton.connect("clicked", self._timeRequested)
1190 table.attach(self._timeButton, 0, 1, 2, 3)
1191
1192 self._simulatorTime = gtk.Label("-")
1193 self._simulatorTime.set_alignment(0.0, 0.5)
1194 table.attach(self._simulatorTime, 1, 2, 2, 3)
1195
1196 self.addCancelFlightButton()
1197
1198 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1199 self._button = self.addNextButton(clicked = self._forwardClicked)
1200
1201 def activate(self):
1202 """Activate the page."""
1203 self._timeButton.set_sensitive(True)
1204 bookedFlight = self._wizard._bookedFlight
1205 self._departure.set_text(str(bookedFlight.departureTime.time()))
1206 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
1207 self._simulatorTime.set_text("-")
1208
1209 def _timeRequested(self, button):
1210 """Request the time from the simulator."""
1211 self._timeButton.set_sensitive(False)
1212 self._backButton.set_sensitive(False)
1213 self._button.set_sensitive(False)
1214 self._wizard.gui.beginBusy(xstr("time_busy"))
1215 self._wizard.gui.simulator.requestTime(self._handleTime)
1216
1217 def _handleTime(self, timestamp):
1218 """Handle the result of a time retrieval."""
1219 gobject.idle_add(self._processTime, timestamp)
1220
1221 def _processTime(self, timestamp):
1222 """Process the given time."""
1223 self._wizard.gui.endBusy()
1224 self._timeButton.set_sensitive(True)
1225 self._backButton.set_sensitive(True)
1226 self._button.set_sensitive(True)
1227 tm = time.gmtime(timestamp)
1228 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
1229 self._simulatorTime.set_text(str(t))
1230
1231 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
1232 dt = self._wizard._bookedFlight.departureTime.time()
1233 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
1234 diff = dts-ts
1235
1236 markupBegin = ""
1237 markupEnd = ""
1238 if diff < 0:
1239 markupBegin = '<b><span foreground="red">'
1240 markupEnd = '</span></b>'
1241 elif diff < 3*60 or diff > 30*60:
1242 markupBegin = '<b><span foreground="orange">'
1243 markupEnd = '</span></b>'
1244
1245 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
1246
1247 def _backClicked(self, button):
1248 """Called when the Back button is pressed."""
1249 self.goBack()
1250
1251 def _forwardClicked(self, button):
1252 """Called when the forward button is clicked."""
1253 if not self._completed:
1254 gui = self._wizard.gui
1255 gui.beginBusy(xstr("fuel_get_busy"))
1256
1257 gui.simulator.getFuel(self._handleFuel)
1258 else:
1259 self._wizard.nextPage()
1260
1261 def _handleFuel(self, fuelData):
1262 """Callback for the fuel query operation."""
1263 gobject.idle_add(self._processFuel, fuelData)
1264
1265 def _processFuel(self, fuelData):
1266 """Process the given fuel data."""
1267 self._wizard.gui.endBusy()
1268 self._wizard._fuelData = fuelData
1269 self._wizard.nextPage()
1270
1271#-----------------------------------------------------------------------------
1272
1273class FuelTank(gtk.VBox):
1274 """Widget for the fuel tank."""
1275 def __init__(self, fuelTank, name, capacity, currentWeight):
1276 """Construct the widget for the tank with the given name."""
1277 super(FuelTank, self).__init__()
1278
1279 self._enabled = True
1280 self.fuelTank = fuelTank
1281 self.capacity = capacity
1282 self.currentWeight = currentWeight
1283 self.expectedWeight = currentWeight
1284
1285 label = gtk.Label("<b>" + name + "</b>")
1286 label.set_use_markup(True)
1287 label.set_use_underline(True)
1288 label.set_justify(JUSTIFY_CENTER)
1289 label.set_alignment(0.5, 1.0)
1290 self.pack_start(label, False, False, 4)
1291
1292 self._tankFigure = gtk.EventBox()
1293 self._tankFigure.set_size_request(38, -1)
1294 self._tankFigure.set_visible_window(False)
1295 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
1296
1297 if pygobject:
1298 self._tankFigure.connect("draw", self._drawTankFigure)
1299 else:
1300 self._tankFigure.connect("expose_event", self._drawTankFigure)
1301 self._tankFigure.connect("button_press_event", self._buttonPressed)
1302 self._tankFigure.connect("motion_notify_event", self._motionNotify)
1303 self._tankFigure.connect("scroll-event", self._scrolled)
1304
1305 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1306 xscale = 0.0, yscale = 1.0)
1307 alignment.add(self._tankFigure)
1308
1309 self.pack_start(alignment, True, True, 4)
1310
1311 self._expectedButton = gtk.SpinButton()
1312 self._expectedButton.set_numeric(True)
1313 self._expectedButton.set_range(0, self.capacity)
1314 self._expectedButton.set_increments(10, 100)
1315 self._expectedButton.set_value(currentWeight)
1316 self._expectedButton.set_alignment(1.0)
1317 self._expectedButton.set_width_chars(5)
1318 self._expectedButton.connect("value-changed", self._expectedChanged)
1319
1320 label.set_mnemonic_widget(self._expectedButton)
1321
1322 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1323 xscale = 0.0, yscale = 1.0)
1324 alignment.add(self._expectedButton)
1325 self.pack_start(alignment, False, False, 4)
1326
1327 def setCurrent(self, currentWeight):
1328 """Set the current weight."""
1329 self.currentWeight = currentWeight
1330 self._redraw()
1331
1332 def isCorrect(self):
1333 """Determine if the contents of the fuel tank are as expected"""
1334 return abs(self.expectedWeight - self.currentWeight)<=1
1335
1336 def disable(self):
1337 """Disable the fuel tank."""
1338 self._expectedButton.set_sensitive(False)
1339 self._enabled = False
1340
1341 def _redraw(self):
1342 """Redraw the tank figure."""
1343 self._tankFigure.queue_draw()
1344
1345 def _drawTankFigure(self, tankFigure, eventOrContext):
1346 """Draw the tank figure."""
1347 triangleSize = 5
1348
1349 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
1350 (xOffset, yOffset) = (0, 0) if pygobject \
1351 else (tankFigure.allocation.x, tankFigure.allocation.y)
1352
1353 width = tankFigure.get_allocated_width() if pygobject \
1354 else tankFigure.allocation.width
1355 height = tankFigure.get_allocated_height() if pygobject \
1356 else tankFigure.allocation.height
1357
1358 rectangleX0 = triangleSize
1359 rectangleY0 = triangleSize
1360 rectangleX1 = width - 1 - triangleSize
1361 rectangleY1 = height - 1 - triangleSize
1362 rectangleLineWidth = 2.0
1363
1364 context.set_source_rgb(0.0, 0.0, 0.0)
1365 context.set_line_width(rectangleLineWidth)
1366 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
1367 yOffset + rectangleY0 + rectangleLineWidth/2,
1368 rectangleX1 - rectangleX0 - rectangleLineWidth,
1369 rectangleY1 - rectangleY0 - rectangleLineWidth)
1370 context.stroke()
1371
1372 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
1373 rectangleInnerRight = rectangleX1 - rectangleLineWidth
1374 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
1375 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
1376
1377 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
1378 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
1379
1380 context.set_source_rgb(1.0, 0.9, 0.6)
1381 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
1382 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
1383 context.rectangle(xOffset + rectangleInnerLeft,
1384 yOffset + rectangleInnerTop +
1385 rectangleInnerHeight - currentHeight,
1386 rectangleInnerWidth, currentHeight)
1387 context.fill()
1388
1389 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
1390 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
1391
1392 context.set_line_width(1.5)
1393 context.set_source_rgb(0.0, 0.85, 0.85)
1394 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
1395 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
1396 context.stroke()
1397
1398 context.set_line_width(0.0)
1399 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
1400 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
1401 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
1402 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
1403 context.fill()
1404
1405 context.set_line_width(0.0)
1406 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
1407 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
1408 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
1409 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
1410 context.fill()
1411
1412 return True
1413
1414 def _setExpectedFromY(self, y):
1415 """Set the expected weight from the given Y-coordinate."""
1416 level = (self._rectangleInnerBottom - y) / \
1417 (self._rectangleInnerBottom - self._rectangleInnerTop)
1418 level = min(1.0, max(0.0, level))
1419 self._expectedButton.set_value(level * self.capacity)
1420
1421 def _buttonPressed(self, tankFigure, event):
1422 """Called when a button is pressed in the figure.
1423
1424 The expected level will be set there."""
1425 if self._enabled and event.button==1:
1426 self._setExpectedFromY(event.y)
1427
1428 def _motionNotify(self, tankFigure, event):
1429 """Called when the mouse pointer moves within the area of a tank figure."""
1430 if self._enabled and event.state==BUTTON1_MASK:
1431 self._setExpectedFromY(event.y)
1432
1433 def _scrolled(self, tankFigure, event):
1434 """Called when a scroll event is received."""
1435 if self._enabled:
1436 increment = 1 if event.state==CONTROL_MASK \
1437 else 100 if event.state==SHIFT_MASK \
1438 else 10 if event.state==0 else 0
1439 if increment!=0:
1440 if event.direction==SCROLL_DOWN:
1441 increment *= -1
1442 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
1443
1444 def _expectedChanged(self, spinButton):
1445 """Called when the expected value has changed."""
1446 self.expectedWeight = spinButton.get_value_as_int()
1447 self._redraw()
1448
1449#-----------------------------------------------------------------------------
1450
1451class FuelPage(Page):
1452 """The page containing the fuel tank filling."""
1453 _pumpStep = 0.02
1454
1455 def __init__(self, wizard):
1456 """Construct the page."""
1457 super(FuelPage, self).__init__(wizard, xstr("fuel_title"),
1458 xstr("fuel_help"),
1459 completedHelp = xstr("fuel_chelp"))
1460
1461 self._fuelTanks = []
1462 self._fuelTable = None
1463 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1464 xscale = 0.0, yscale = 1.0)
1465 self.setMainWidget(self._fuelAlignment)
1466
1467 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
1468 self._setupTanks(tankData)
1469
1470 self.addCancelFlightButton()
1471
1472 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1473 self._button = self.addNextButton(clicked = self._forwardClicked)
1474
1475 self._pumpIndex = 0
1476
1477 def activate(self):
1478 """Activate the page."""
1479 self._setupTanks(self._wizard._fuelData)
1480
1481 def finalize(self):
1482 """Finalize the page."""
1483 for fuelTank in self._fuelTanks:
1484 fuelTank.disable()
1485
1486 def _backClicked(self, button):
1487 """Called when the Back button is pressed."""
1488 self.goBack()
1489
1490 def _forwardClicked(self, button):
1491 """Called when the forward button is clicked."""
1492 if not self._completed:
1493 self._pumpIndex = 0
1494 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
1495 self._pump()
1496 else:
1497 self._wizard.nextPage()
1498
1499 def _setupTanks(self, tankData):
1500 """Setup the tanks for the given data."""
1501 numTanks = len(tankData)
1502 if self._fuelTable is not None:
1503 self._fuelAlignment.remove(self._fuelTable)
1504
1505 self._fuelTanks = []
1506 self._fuelTable = gtk.Table(numTanks, 1)
1507 self._fuelTable.set_col_spacings(16)
1508 index = 0
1509 for (tank, current, capacity) in tankData:
1510 fuelTank = FuelTank(tank,
1511 xstr("fuel_tank_" +
1512 const.fuelTank2string(tank)),
1513 capacity, current)
1514 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
1515 self._fuelTanks.append(fuelTank)
1516 index += 1
1517
1518 self._fuelAlignment.add(self._fuelTable)
1519 self.show_all()
1520
1521 def _pump(self):
1522 """Perform one step of pumping.
1523
1524 It is checked, if the current tank's contents are of the right
1525 quantity. If not, it is filled one step further to the desired
1526 contents. Otherwise the next tank is started. If all tanks are are
1527 filled, the next page is selected."""
1528 numTanks = len(self._fuelTanks)
1529
1530 fuelTank = None
1531 while self._pumpIndex < numTanks:
1532 fuelTank = self._fuelTanks[self._pumpIndex]
1533 if fuelTank.isCorrect():
1534 self._pumpIndex += 1
1535 fuelTank = None
1536 else:
1537 break
1538
1539 if fuelTank is None:
1540 self._wizard.gui.endBusy()
1541 self._wizard.nextPage()
1542 else:
1543 currentLevel = fuelTank.currentWeight / fuelTank.capacity
1544 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
1545 if currentLevel<expectedLevel:
1546 currentLevel += FuelPage._pumpStep
1547 if currentLevel>expectedLevel: currentLevel = expectedLevel
1548 else:
1549 currentLevel -= FuelPage._pumpStep
1550 if currentLevel<expectedLevel: currentLevel = expectedLevel
1551 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
1552 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
1553 currentLevel)])
1554 gobject.timeout_add(50, self._pump)
1555
1556#-----------------------------------------------------------------------------
1557
1558class RoutePage(Page):
1559 """The page containing the route and the flight level."""
1560 def __init__(self, wizard):
1561 """Construct the page."""
1562 super(RoutePage, self).__init__(wizard, xstr("route_title"),
1563 xstr("route_help"),
1564 completedHelp = xstr("route_chelp"))
1565
1566 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1567 xscale = 0.0, yscale = 0.0)
1568
1569 mainBox = gtk.VBox()
1570 alignment.add(mainBox)
1571 self.setMainWidget(alignment)
1572
1573 levelBox = gtk.HBox()
1574
1575 label = gtk.Label(xstr("route_level"))
1576 label.set_use_underline(True)
1577 levelBox.pack_start(label, True, True, 0)
1578
1579 self._cruiseLevel = gtk.SpinButton()
1580 self._cruiseLevel.set_increments(step = 10, page = 100)
1581 self._cruiseLevel.set_range(min = 50, max = 500)
1582 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
1583 self._cruiseLevel.set_numeric(True)
1584 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
1585 label.set_mnemonic_widget(self._cruiseLevel)
1586 self._filedCruiseLevel = 240
1587
1588 levelBox.pack_start(self._cruiseLevel, False, False, 8)
1589
1590 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1591 xscale = 0.0, yscale = 0.0)
1592 alignment.add(levelBox)
1593
1594 mainBox.pack_start(alignment, False, False, 0)
1595
1596
1597 routeBox = gtk.VBox()
1598
1599 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1600 xscale = 0.0, yscale = 0.0)
1601 label = gtk.Label(xstr("route_route"))
1602 label.set_use_underline(True)
1603 alignment.add(label)
1604 routeBox.pack_start(alignment, True, True, 0)
1605
1606 routeWindow = gtk.ScrolledWindow()
1607 routeWindow.set_size_request(400, 80)
1608 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
1609 else gtk.SHADOW_IN)
1610 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1611 else gtk.POLICY_AUTOMATIC,
1612 gtk.PolicyType.AUTOMATIC if pygobject
1613 else gtk.POLICY_AUTOMATIC)
1614
1615 self._uppercasingRoute = False
1616
1617 self._route = gtk.TextView()
1618 self._route.set_tooltip_text(xstr("route_route_tooltip"))
1619 self._route.set_wrap_mode(WRAP_WORD)
1620 self._route.get_buffer().connect("changed", self._routeChanged)
1621 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
1622 routeWindow.add(self._route)
1623
1624 label.set_mnemonic_widget(self._route)
1625 routeBox.pack_start(routeWindow, True, True, 0)
1626
1627 mainBox.pack_start(routeBox, True, True, 8)
1628
1629 self.addCancelFlightButton()
1630
1631 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1632 self._button = self.addNextButton(clicked = self._forwardClicked)
1633
1634 @property
1635 def filedCruiseLevel(self):
1636 """Get the filed cruise level."""
1637 return self._filedCruiseLevel
1638
1639 @property
1640 def cruiseLevel(self):
1641 """Get the cruise level."""
1642 return self._cruiseLevel.get_value_as_int()
1643
1644 @property
1645 def route(self):
1646 """Get the route."""
1647 return self._getRoute()
1648
1649 def activate(self):
1650 """Setup the route from the booked flight."""
1651 self._cruiseLevel.set_value(240)
1652 self._filedCruiseLevel = 240
1653 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
1654 self._updateForwardButton()
1655
1656 def _getRoute(self):
1657 """Get the text of the route."""
1658 buffer = self._route.get_buffer()
1659 return buffer.get_text(buffer.get_start_iter(),
1660 buffer.get_end_iter(), True)
1661
1662 def _updateForwardButton(self):
1663 """Update the sensitivity of the forward button."""
1664 self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
1665 self._getRoute()!="")
1666
1667 def _cruiseLevelChanged(self, spinButton):
1668 """Called when the cruise level has changed."""
1669 self._updateForwardButton()
1670
1671 def _routeChanged(self, textBuffer):
1672 """Called when the route has changed."""
1673 if not self._uppercasingRoute:
1674 self._updateForwardButton()
1675
1676 def _routeInserted(self, textBuffer, iter, text, length):
1677 """Called when new characters are inserted into the route.
1678
1679 It uppercases all characters."""
1680 if not self._uppercasingRoute:
1681 self._uppercasingRoute = True
1682
1683 iter1 = iter.copy()
1684 iter1.backward_chars(length)
1685 textBuffer.delete(iter, iter1)
1686
1687 textBuffer.insert(iter, text.upper())
1688
1689 self._uppercasingRoute = False
1690
1691 def _backClicked(self, button):
1692 """Called when the Back button is pressed."""
1693 self.goBack()
1694
1695 def _forwardClicked(self, button):
1696 """Called when the Forward button is clicked."""
1697 if self._completed:
1698 self._wizard.nextPage()
1699 else:
1700 bookedFlight = self._wizard._bookedFlight
1701 self._filedCruiseLevel = self.cruiseLevel
1702 self._wizard.gui.beginBusy(xstr("route_down_notams"))
1703 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
1704 bookedFlight.departureICAO,
1705 bookedFlight.arrivalICAO)
1706 startSound(const.SOUND_NOTAM)
1707
1708 def _notamsCallback(self, returned, result):
1709 """Callback for the NOTAMs."""
1710 gobject.idle_add(self._handleNOTAMs, returned, result)
1711
1712 def _handleNOTAMs(self, returned, result):
1713 """Handle the NOTAMs."""
1714 if returned:
1715 self._wizard._departureNOTAMs = result.departureNOTAMs
1716 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
1717 else:
1718 self._wizard._departureNOTAMs = None
1719 self._wizard._arrivalNOTAMs = None
1720
1721 bookedFlight = self._wizard._bookedFlight
1722 self._wizard.gui.beginBusy(xstr("route_down_metars"))
1723 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
1724 [bookedFlight.departureICAO,
1725 bookedFlight.arrivalICAO])
1726
1727 def _metarsCallback(self, returned, result):
1728 """Callback for the METARs."""
1729 gobject.idle_add(self._handleMETARs, returned, result)
1730
1731 def _handleMETARs(self, returned, result):
1732 """Handle the METARs."""
1733 self._wizard._departureMETAR = None
1734 self._wizard._arrivalMETAR = None
1735 bookedFlight = self._wizard._bookedFlight
1736 if returned:
1737 if bookedFlight.departureICAO in result.metars:
1738 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
1739 if bookedFlight.arrivalICAO in result.metars:
1740 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
1741
1742 self._wizard.gui.endBusy()
1743 self._backButton.set_sensitive(True)
1744 self._button.set_sensitive(True)
1745 self._wizard.nextPage()
1746
1747#-----------------------------------------------------------------------------
1748
1749class BriefingPage(Page):
1750 """Page for the briefing."""
1751 def __init__(self, wizard, departure):
1752 """Construct the briefing page."""
1753 self._departure = departure
1754
1755 title = xstr("briefing_title") % (1 if departure else 2,
1756 xstr("briefing_departure")
1757 if departure
1758 else xstr("briefing_arrival"))
1759 super(BriefingPage, self).__init__(wizard, title, xstr("briefing_help"),
1760 completedHelp = xstr("briefing_chelp"))
1761
1762 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1763 xscale = 1.0, yscale = 1.0)
1764
1765 mainBox = gtk.VBox()
1766 alignment.add(mainBox)
1767 self.setMainWidget(alignment)
1768
1769 self._notamsFrame = gtk.Frame()
1770 self._notamsFrame.set_label(xstr("briefing_notams_init"))
1771 scrolledWindow = gtk.ScrolledWindow()
1772 scrolledWindow.set_size_request(-1, 128)
1773 # FIXME: these constants should be in common
1774 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1775 else gtk.POLICY_AUTOMATIC,
1776 gtk.PolicyType.AUTOMATIC if pygobject
1777 else gtk.POLICY_AUTOMATIC)
1778 self._notams = gtk.TextView()
1779 self._notams.set_editable(False)
1780 self._notams.set_accepts_tab(False)
1781 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1782 scrolledWindow.add(self._notams)
1783 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1784 xscale = 1.0, yscale = 1.0)
1785 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1786 padding_left = 0, padding_right = 0)
1787 alignment.add(scrolledWindow)
1788 self._notamsFrame.add(alignment)
1789 mainBox.pack_start(self._notamsFrame, True, True, 4)
1790
1791 self._metarFrame = gtk.Frame()
1792 self._metarFrame.set_label(xstr("briefing_metar_init"))
1793 scrolledWindow = gtk.ScrolledWindow()
1794 scrolledWindow.set_size_request(-1, 32)
1795 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1796 else gtk.POLICY_AUTOMATIC,
1797 gtk.PolicyType.AUTOMATIC if pygobject
1798 else gtk.POLICY_AUTOMATIC)
1799
1800 self._uppercasingMETAR = False
1801
1802 self._metar = gtk.TextView()
1803 self._metar.set_accepts_tab(False)
1804 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1805 self._metar.get_buffer().connect("changed", self._metarChanged)
1806 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
1807 scrolledWindow.add(self._metar)
1808 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1809 xscale = 1.0, yscale = 1.0)
1810 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1811 padding_left = 0, padding_right = 0)
1812 alignment.add(scrolledWindow)
1813 self._metarFrame.add(alignment)
1814 mainBox.pack_start(self._metarFrame, True, True, 4)
1815 self.metarEdited = False
1816
1817 self.addCancelFlightButton()
1818
1819 self.addPreviousButton(clicked = self._backClicked)
1820 self._button = self.addNextButton(clicked = self._forwardClicked)
1821
1822 @property
1823 def metar(self):
1824 """Get the METAR on the page."""
1825 buffer = self._metar.get_buffer()
1826 return buffer.get_text(buffer.get_start_iter(),
1827 buffer.get_end_iter(), True)
1828
1829 def setMETAR(self, metar):
1830 """Set the metar."""
1831 self._metar.get_buffer().set_text(metar)
1832 self.metarEdited = False
1833
1834 def activate(self):
1835 """Activate the page."""
1836 if not self._departure:
1837 self._button.set_label(xstr("briefing_button"))
1838 self._button.set_has_tooltip(False)
1839 self._button.set_use_stock(False)
1840
1841 bookedFlight = self._wizard._bookedFlight
1842
1843 icao = bookedFlight.departureICAO if self._departure \
1844 else bookedFlight.arrivalICAO
1845 notams = self._wizard._departureNOTAMs if self._departure \
1846 else self._wizard._arrivalNOTAMs
1847 metar = self._wizard._departureMETAR if self._departure \
1848 else self._wizard._arrivalMETAR
1849
1850 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
1851 buffer = self._notams.get_buffer()
1852 if notams is None:
1853 buffer.set_text(xstr("briefing_notams_failed"))
1854 elif not notams:
1855 buffer.set_text(xstr("briefing_notams_missing"))
1856 else:
1857 s = ""
1858 for notam in notams:
1859 s += str(notam.begin)
1860 if notam.end is not None:
1861 s += " - " + str(notam.end)
1862 elif notam.permanent:
1863 s += " - PERMANENT"
1864 s += "\n"
1865 if notam.repeatCycle:
1866 s += "Repeat cycle: " + notam.repeatCycle + "\n"
1867 s += notam.notice + "\n"
1868 s += "-------------------- * --------------------\n"
1869 buffer.set_text(s)
1870
1871 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
1872 buffer = self._metar.get_buffer()
1873 if metar is None:
1874 buffer.set_text(xstr("briefing_metar_failed"))
1875 else:
1876 buffer.set_text(metar)
1877
1878 label = self._metarFrame.get_label_widget()
1879 label.set_use_underline(True)
1880 label.set_mnemonic_widget(self._metar)
1881
1882 self.metarEdited = False
1883
1884 def _backClicked(self, button):
1885 """Called when the Back button is pressed."""
1886 self.goBack()
1887
1888 def _forwardClicked(self, button):
1889 """Called when the forward button is clicked."""
1890 if not self._departure:
1891 if not self._completed:
1892 self._wizard.gui.startMonitoring()
1893 self._button.set_label(xstr("button_next"))
1894 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1895 self.complete()
1896
1897 self._wizard.nextPage()
1898
1899 def _metarChanged(self, buffer):
1900 """Called when the METAR has changed."""
1901 if not self._uppercasingMETAR:
1902 self.metarEdited = True
1903 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
1904 buffer.get_end_iter(),
1905 True)!="")
1906
1907 def _metarInserted(self, textBuffer, iter, text, length):
1908 """Called when new characters are inserted into the METAR.
1909
1910 It uppercases all characters."""
1911 if not self._uppercasingMETAR:
1912 self._uppercasingMETAR = True
1913
1914 iter1 = iter.copy()
1915 iter1.backward_chars(length)
1916 textBuffer.delete(iter, iter1)
1917
1918 textBuffer.insert(iter, text.upper())
1919
1920 self._uppercasingMETAR = False
1921
1922#-----------------------------------------------------------------------------
1923
1924class TakeoffPage(Page):
1925 """Page for entering the takeoff data."""
1926 def __init__(self, wizard):
1927 """Construct the takeoff page."""
1928 super(TakeoffPage, self).__init__(wizard, xstr("takeoff_title"),
1929 xstr("takeoff_help"),
1930 completedHelp = xstr("takeoff_chelp"))
1931
1932 self._forwardAllowed = False
1933
1934 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1935 xscale = 0.0, yscale = 0.0)
1936
1937 table = gtk.Table(5, 4)
1938 table.set_row_spacings(4)
1939 table.set_col_spacings(16)
1940 table.set_homogeneous(False)
1941 alignment.add(table)
1942 self.setMainWidget(alignment)
1943
1944 label = gtk.Label(xstr("takeoff_runway"))
1945 label.set_use_underline(True)
1946 label.set_alignment(0.0, 0.5)
1947 table.attach(label, 0, 1, 0, 1)
1948
1949 self._runway = gtk.Entry()
1950 self._runway.set_width_chars(10)
1951 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
1952 self._runway.connect("changed", self._upperChanged)
1953 table.attach(self._runway, 1, 3, 0, 1)
1954 label.set_mnemonic_widget(self._runway)
1955
1956 label = gtk.Label(xstr("takeoff_sid"))
1957 label.set_use_underline(True)
1958 label.set_alignment(0.0, 0.5)
1959 table.attach(label, 0, 1, 1, 2)
1960
1961 self._sid = gtk.Entry()
1962 self._sid.set_width_chars(10)
1963 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
1964 self._sid.connect("changed", self._upperChanged)
1965 table.attach(self._sid, 1, 3, 1, 2)
1966 label.set_mnemonic_widget(self._sid)
1967
1968 label = gtk.Label(xstr("takeoff_v1"))
1969 label.set_use_markup(True)
1970 label.set_use_underline(True)
1971 label.set_alignment(0.0, 0.5)
1972 table.attach(label, 0, 1, 2, 3)
1973
1974 self._v1 = IntegerEntry()
1975 self._v1.set_width_chars(4)
1976 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
1977 self._v1.connect("integer-changed", self._valueChanged)
1978 table.attach(self._v1, 2, 3, 2, 3)
1979 label.set_mnemonic_widget(self._v1)
1980
1981 self._v1Unit = gtk.Label(xstr("label_knots"))
1982 table.attach(self._v1Unit, 3, 4, 2, 3)
1983
1984 label = gtk.Label(xstr("takeoff_vr"))
1985 label.set_use_markup(True)
1986 label.set_use_underline(True)
1987 label.set_alignment(0.0, 0.5)
1988 table.attach(label, 0, 1, 3, 4)
1989
1990 self._vr = IntegerEntry()
1991 self._vr.set_width_chars(4)
1992 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
1993 self._vr.connect("integer-changed", self._valueChanged)
1994 table.attach(self._vr, 2, 3, 3, 4)
1995 label.set_mnemonic_widget(self._vr)
1996
1997 self._vrUnit = gtk.Label(xstr("label_knots"))
1998 table.attach(self._vrUnit, 3, 4, 3, 4)
1999
2000 label = gtk.Label(xstr("takeoff_v2"))
2001 label.set_use_markup(True)
2002 label.set_use_underline(True)
2003 label.set_alignment(0.0, 0.5)
2004 table.attach(label, 0, 1, 4, 5)
2005
2006 self._v2 = IntegerEntry()
2007 self._v2.set_width_chars(4)
2008 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
2009 self._v2.connect("integer-changed", self._valueChanged)
2010 table.attach(self._v2, 2, 3, 4, 5)
2011 label.set_mnemonic_widget(self._v2)
2012
2013 self._v2Unit = gtk.Label(xstr("label_knots"))
2014 table.attach(self._v2Unit, 3, 4, 4, 5)
2015
2016 self.addCancelFlightButton()
2017
2018 self.addPreviousButton(clicked = self._backClicked)
2019
2020 self._button = self.addNextButton(clicked = self._forwardClicked)
2021
2022 @property
2023 def runway(self):
2024 """Get the runway."""
2025 return self._runway.get_text()
2026
2027 @property
2028 def sid(self):
2029 """Get the SID."""
2030 return self._sid.get_text()
2031
2032 @property
2033 def v1(self):
2034 """Get the v1 speed."""
2035 return self._v1.get_int()
2036
2037 @property
2038 def vr(self):
2039 """Get the vr speed."""
2040 return self._vr.get_int()
2041
2042 @property
2043 def v2(self):
2044 """Get the v2 speed."""
2045 return self._v2.get_int()
2046
2047 def activate(self):
2048 """Activate the page."""
2049 self._runway.set_text("")
2050 self._runway.set_sensitive(True)
2051 self._sid.set_text("")
2052 self._sid.set_sensitive(True)
2053 self._v1.set_int(None)
2054 self._v1.set_sensitive(True)
2055 self._vr.set_int(None)
2056 self._vr.set_sensitive(True)
2057 self._v2.set_int(None)
2058 self._v2.set_sensitive(True)
2059
2060 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2061 speedUnit = xstr("label" + i18nSpeedUnit)
2062 self._v1Unit.set_text(speedUnit)
2063 self._vrUnit.set_text(speedUnit)
2064 self._v2Unit.set_text(speedUnit)
2065
2066 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
2067 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
2068 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
2069
2070 self._button.set_sensitive(False)
2071 self._forwardAllowed = False
2072
2073 def allowForward(self):
2074 """Allow going to the next page."""
2075 self._forwardAllowed = True
2076 self._updateForwardButton()
2077
2078 def reset(self):
2079 """Reset the page if the wizard is reset."""
2080 super(TakeoffPage, self).reset()
2081 self._v1.reset()
2082 self._vr.reset()
2083 self._v2.reset()
2084
2085 def _updateForwardButton(self):
2086 """Update the sensitivity of the forward button based on some conditions."""
2087 sensitive = self._forwardAllowed and \
2088 self._runway.get_text()!="" and \
2089 self._sid.get_text()!="" and \
2090 self.v1 is not None and \
2091 self.vr is not None and \
2092 self.v2 is not None and \
2093 self.v1 <= self.vr and \
2094 self.vr <= self.v2
2095 self._button.set_sensitive(sensitive)
2096
2097 def _valueChanged(self, widget, arg = None):
2098 """Called when the value of some widget has changed."""
2099 self._updateForwardButton()
2100
2101 def _upperChanged(self, entry, arg = None):
2102 """Called when the value of some entry widget has changed and the value
2103 should be converted to uppercase."""
2104 entry.set_text(entry.get_text().upper())
2105 self._valueChanged(entry, arg)
2106
2107 def _backClicked(self, button):
2108 """Called when the Back button is pressed."""
2109 self.goBack()
2110
2111 def _forwardClicked(self, button):
2112 """Called when the forward button is clicked."""
2113 self._wizard.gui.flight.aircraft.updateV1R2()
2114 self._wizard.nextPage()
2115
2116#-----------------------------------------------------------------------------
2117
2118class LandingPage(Page):
2119 """Page for entering landing data."""
2120 def __init__(self, wizard):
2121 """Construct the landing page."""
2122 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
2123 xstr("landing_help"),
2124 completedHelp = xstr("landing_chelp"))
2125
2126 self._flightEnded = False
2127
2128 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2129 xscale = 0.0, yscale = 0.0)
2130
2131 table = gtk.Table(5, 5)
2132 table.set_row_spacings(4)
2133 table.set_col_spacings(16)
2134 table.set_homogeneous(False)
2135 alignment.add(table)
2136 self.setMainWidget(alignment)
2137
2138 self._starButton = gtk.CheckButton()
2139 self._starButton.connect("clicked", self._starButtonClicked)
2140 table.attach(self._starButton, 0, 1, 0, 1)
2141
2142 label = gtk.Label(xstr("landing_star"))
2143 label.set_use_underline(True)
2144 label.set_alignment(0.0, 0.5)
2145 table.attach(label, 1, 2, 0, 1)
2146
2147 self._star = gtk.Entry()
2148 self._star.set_width_chars(10)
2149 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
2150 self._star.connect("changed", self._upperChanged)
2151 self._star.set_sensitive(False)
2152 table.attach(self._star, 2, 4, 0, 1)
2153 label.set_mnemonic_widget(self._starButton)
2154
2155 self._transitionButton = gtk.CheckButton()
2156 self._transitionButton.connect("clicked", self._transitionButtonClicked)
2157 table.attach(self._transitionButton, 0, 1, 1, 2)
2158
2159 label = gtk.Label(xstr("landing_transition"))
2160 label.set_use_underline(True)
2161 label.set_alignment(0.0, 0.5)
2162 table.attach(label, 1, 2, 1, 2)
2163
2164 self._transition = gtk.Entry()
2165 self._transition.set_width_chars(10)
2166 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
2167 self._transition.connect("changed", self._upperChanged)
2168 self._transition.set_sensitive(False)
2169 table.attach(self._transition, 2, 4, 1, 2)
2170 label.set_mnemonic_widget(self._transitionButton)
2171
2172 label = gtk.Label(xstr("landing_runway"))
2173 label.set_use_underline(True)
2174 label.set_alignment(0.0, 0.5)
2175 table.attach(label, 1, 2, 2, 3)
2176
2177 self._runway = gtk.Entry()
2178 self._runway.set_width_chars(10)
2179 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
2180 self._runway.connect("changed", self._upperChanged)
2181 table.attach(self._runway, 2, 4, 2, 3)
2182 label.set_mnemonic_widget(self._runway)
2183
2184 label = gtk.Label(xstr("landing_approach"))
2185 label.set_use_underline(True)
2186 label.set_alignment(0.0, 0.5)
2187 table.attach(label, 1, 2, 3, 4)
2188
2189 self._approachType = gtk.Entry()
2190 self._approachType.set_width_chars(10)
2191 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
2192 self._approachType.connect("changed", self._upperChanged)
2193 table.attach(self._approachType, 2, 4, 3, 4)
2194 label.set_mnemonic_widget(self._approachType)
2195
2196 label = gtk.Label(xstr("landing_vref"))
2197 label.set_use_markup(True)
2198 label.set_use_underline(True)
2199 label.set_alignment(0.0, 0.5)
2200 table.attach(label, 1, 2, 5, 6)
2201
2202 self._vref = IntegerEntry()
2203 self._vref.set_width_chars(5)
2204 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
2205 self._vref.connect("integer-changed", self._vrefChanged)
2206 table.attach(self._vref, 3, 4, 5, 6)
2207 label.set_mnemonic_widget(self._vref)
2208
2209 self._vrefUnit = gtk.Label(xstr("label_knots"))
2210 table.attach(self._vrefUnit, 4, 5, 5, 6)
2211
2212 self.addCancelFlightButton()
2213
2214 self.addPreviousButton(clicked = self._backClicked)
2215
2216 self._button = self.addNextButton(clicked = self._forwardClicked)
2217
2218 # These are needed for correct size calculations
2219 self._starButton.set_active(True)
2220 self._transitionButton.set_active(True)
2221
2222 @property
2223 def star(self):
2224 """Get the STAR or None if none entered."""
2225 return self._star.get_text() if self._starButton.get_active() else None
2226
2227 @property
2228 def transition(self):
2229 """Get the transition or None if none entered."""
2230 return self._transition.get_text() \
2231 if self._transitionButton.get_active() else None
2232
2233 @property
2234 def approachType(self):
2235 """Get the approach type."""
2236 return self._approachType.get_text()
2237
2238 @property
2239 def runway(self):
2240 """Get the runway."""
2241 return self._runway.get_text()
2242
2243 @property
2244 def vref(self):
2245 """Return the landing reference speed."""
2246 return self._vref.get_int()
2247
2248 def reset(self):
2249 """Reset the page if the wizard is reset."""
2250 super(LandingPage, self).reset()
2251 self._vref.reset()
2252 self._flightEnded = False
2253
2254 def activate(self):
2255 """Called when the page is activated."""
2256 self._starButton.set_sensitive(True)
2257 self._starButton.set_active(False)
2258 self._star.set_text("")
2259
2260 self._transitionButton.set_sensitive(True)
2261 self._transitionButton.set_active(False)
2262 self._transition.set_text("")
2263
2264 self._runway.set_text("")
2265 self._runway.set_sensitive(True)
2266
2267 self._approachType.set_text("")
2268 self._approachType.set_sensitive(True)
2269
2270 self._vref.set_int(None)
2271 self._vref.set_sensitive(True)
2272
2273 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2274 speedUnit = xstr("label" + i18nSpeedUnit)
2275 self._vrefUnit.set_text(speedUnit)
2276
2277 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
2278 i18nSpeedUnit))
2279
2280 self._updateForwardButton()
2281
2282 def flightEnded(self):
2283 """Called when the flight has ended."""
2284 super(LandingPage, self).flightEnded()
2285 self._flightEnded = True
2286 self._updateForwardButton()
2287
2288 def _starButtonClicked(self, button):
2289 """Called when the STAR button is clicked."""
2290 active = button.get_active()
2291 self._star.set_sensitive(active)
2292 if active:
2293 self._star.grab_focus()
2294 self._updateForwardButton()
2295
2296 def _transitionButtonClicked(self, button):
2297 """Called when the Transition button is clicked."""
2298 active = button.get_active()
2299 self._transition.set_sensitive(active)
2300 if active:
2301 self._transition.grab_focus()
2302 self._updateForwardButton()
2303
2304 def _updateForwardButton(self):
2305 """Update the sensitivity of the forward button."""
2306 sensitive = self._flightEnded and \
2307 (self._starButton.get_active() or \
2308 self._transitionButton.get_active()) and \
2309 (self._star.get_text()!="" or
2310 not self._starButton.get_active()) and \
2311 (self._transition.get_text()!="" or
2312 not self._transitionButton.get_active()) and \
2313 self._runway.get_text()!="" and \
2314 self._approachType.get_text()!="" and \
2315 self.vref is not None
2316 self._button.set_sensitive(sensitive)
2317
2318 def _upperChanged(self, entry):
2319 """Called for entry widgets that must be converted to uppercase."""
2320 entry.set_text(entry.get_text().upper())
2321 self._updateForwardButton()
2322
2323 def _vrefChanged(self, widget, value):
2324 """Called when the Vref has changed."""
2325 self._updateForwardButton()
2326
2327 def _backClicked(self, button):
2328 """Called when the Back button is pressed."""
2329 self.goBack()
2330
2331 def _forwardClicked(self, button):
2332 """Called when the forward button is clicked."""
2333 self._wizard.gui.flight.aircraft.updateVRef()
2334 if self._wizard.gui.config.onlineGateSystem and \
2335 self._wizard.loggedIn and not self._completed and \
2336 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2337 not self._wizard.entranceExam:
2338 self._wizard.getFleet(callback = self._fleetRetrieved,
2339 force = True)
2340 else:
2341 self._wizard.nextPage()
2342
2343 def _fleetRetrieved(self, fleet):
2344 """Callback for the fleet retrieval."""
2345 self._wizard.nextPage()
2346
2347#-----------------------------------------------------------------------------
2348
2349class FinishPage(Page):
2350 """Flight finish page."""
2351 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
2352 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
2353 ("flighttype_vip", const.FLIGHTTYPE_VIP),
2354 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
2355
2356 def __init__(self, wizard):
2357 """Construct the finish page."""
2358 super(FinishPage, self).__init__(wizard, xstr("finish_title"),
2359 xstr("finish_help"))
2360
2361 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2362 xscale = 0.0, yscale = 0.0)
2363
2364 table = gtk.Table(8, 2)
2365 table.set_row_spacings(4)
2366 table.set_col_spacings(16)
2367 table.set_homogeneous(False)
2368 alignment.add(table)
2369 self.setMainWidget(alignment)
2370
2371 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2372 label = gtk.Label(xstr("finish_rating"))
2373 labelAlignment.add(label)
2374 table.attach(labelAlignment, 0, 1, 0, 1)
2375
2376 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2377 self._flightRating = gtk.Label()
2378 self._flightRating.set_width_chars(8)
2379 self._flightRating.set_alignment(0.0, 0.5)
2380 self._flightRating.set_use_markup(True)
2381 labelAlignment.add(self._flightRating)
2382 table.attach(labelAlignment, 1, 2, 0, 1)
2383
2384 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2385 label = gtk.Label(xstr("finish_flight_time"))
2386 labelAlignment.add(label)
2387 table.attach(labelAlignment, 0, 1, 1, 2)
2388
2389 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2390 self._flightTime = gtk.Label()
2391 self._flightTime.set_width_chars(10)
2392 self._flightTime.set_alignment(0.0, 0.5)
2393 self._flightTime.set_use_markup(True)
2394 labelAlignment.add(self._flightTime)
2395 table.attach(labelAlignment, 1, 2, 1, 2)
2396
2397 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2398 label = gtk.Label(xstr("finish_block_time"))
2399 labelAlignment.add(label)
2400 table.attach(labelAlignment, 0, 1, 2, 3)
2401
2402 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2403 self._blockTime = gtk.Label()
2404 self._blockTime.set_width_chars(10)
2405 self._blockTime.set_alignment(0.0, 0.5)
2406 self._blockTime.set_use_markup(True)
2407 labelAlignment.add(self._blockTime)
2408 table.attach(labelAlignment, 1, 2, 2, 3)
2409
2410 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2411 label = gtk.Label(xstr("finish_distance"))
2412 labelAlignment.add(label)
2413 table.attach(labelAlignment, 0, 1, 3, 4)
2414
2415 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2416 self._distanceFlown = gtk.Label()
2417 self._distanceFlown.set_width_chars(10)
2418 self._distanceFlown.set_alignment(0.0, 0.5)
2419 self._distanceFlown.set_use_markup(True)
2420 labelAlignment.add(self._distanceFlown)
2421 table.attach(labelAlignment, 1, 2, 3, 4)
2422
2423 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2424 label = gtk.Label(xstr("finish_fuel"))
2425 labelAlignment.add(label)
2426 table.attach(labelAlignment, 0, 1, 4, 5)
2427
2428 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2429 self._fuelUsed = gtk.Label()
2430 self._fuelUsed.set_width_chars(10)
2431 self._fuelUsed.set_alignment(0.0, 0.5)
2432 self._fuelUsed.set_use_markup(True)
2433 labelAlignment.add(self._fuelUsed)
2434 table.attach(labelAlignment, 1, 2, 4, 5)
2435
2436 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2437 yalign = 0.5, yscale = 0.0)
2438 label = gtk.Label(xstr("finish_type"))
2439 label.set_use_underline(True)
2440 labelAlignment.add(label)
2441 table.attach(labelAlignment, 0, 1, 5, 6)
2442
2443 flightTypeModel = gtk.ListStore(str, int)
2444 for (name, type) in FinishPage._flightTypes:
2445 flightTypeModel.append([xstr(name), type])
2446
2447 self._flightType = gtk.ComboBox(model = flightTypeModel)
2448 renderer = gtk.CellRendererText()
2449 self._flightType.pack_start(renderer, True)
2450 self._flightType.add_attribute(renderer, "text", 0)
2451 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
2452 self._flightType.set_active(0)
2453 self._flightType.connect("changed", self._flightTypeChanged)
2454 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2455 flightTypeAlignment.add(self._flightType)
2456 table.attach(flightTypeAlignment, 1, 2, 5, 6)
2457 label.set_mnemonic_widget(self._flightType)
2458
2459 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
2460 self._onlineFlight.set_use_underline(True)
2461 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
2462 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2463 onlineFlightAlignment.add(self._onlineFlight)
2464 table.attach(onlineFlightAlignment, 1, 2, 6, 7)
2465
2466 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2467 yalign = 0.5, yscale = 0.0)
2468 self._gateLabel = gtk.Label(xstr("finish_gate"))
2469 self._gateLabel.set_use_underline(True)
2470 labelAlignment.add(self._gateLabel)
2471 table.attach(labelAlignment, 0, 1, 7, 8)
2472
2473 self._gatesModel = gtk.ListStore(str)
2474
2475 self._gate = gtk.ComboBox(model = self._gatesModel)
2476 renderer = gtk.CellRendererText()
2477 self._gate.pack_start(renderer, True)
2478 self._gate.add_attribute(renderer, "text", 0)
2479 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
2480 self._gate.connect("changed", self._gateChanged)
2481 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
2482 gateAlignment.add(self._gate)
2483 table.attach(gateAlignment, 1, 2, 7, 8)
2484 self._gateLabel.set_mnemonic_widget(self._gate)
2485
2486 self.addButton(xstr("finish_newFlight"),
2487 sensitive = True,
2488 clicked = self._newFlightClicked,
2489 tooltip = xstr("finish_newFlight_tooltip"),
2490 padding = 16)
2491
2492 self.addPreviousButton(clicked = self._backClicked)
2493
2494 self._saveButton = self.addButton(xstr("finish_save"),
2495 sensitive = False,
2496 clicked = self._saveClicked,
2497 tooltip = xstr("finish_save_tooltip"))
2498 self._savePIREPDialog = None
2499 self._lastSavePath = None
2500
2501 self._pirepSaved = False
2502 self._pirepSent = False
2503
2504 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
2505 sensitive = False,
2506 clicked = self._sendClicked,
2507 tooltip = xstr("sendPIREP_tooltip"))
2508
2509 @property
2510 def flightType(self):
2511 """Get the flight type."""
2512 index = self._flightType.get_active()
2513 return None if index<0 else self._flightType.get_model()[index][1]
2514
2515 @property
2516 def online(self):
2517 """Get whether the flight was an online flight or not."""
2518 return self._onlineFlight.get_active()
2519
2520 def activate(self):
2521 """Activate the page."""
2522 self._pirepSaved = False
2523 self._pirepSent = False
2524
2525 flight = self._wizard.gui._flight
2526 rating = flight.logger.getRating()
2527 if rating<0:
2528 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
2529 else:
2530 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
2531
2532 flightLength = flight.flightTimeEnd - flight.flightTimeStart
2533 self._flightTime.set_markup("<b>%s</b>" % \
2534 (util.getTimeIntervalString(flightLength),))
2535
2536 blockLength = flight.blockTimeEnd - flight.blockTimeStart
2537 self._blockTime.set_markup("<b>%s</b>" % \
2538 (util.getTimeIntervalString(blockLength),))
2539
2540 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
2541 (flight.flownDistance,))
2542
2543 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
2544 (flight.startFuel - flight.endFuel,))
2545
2546 self._flightType.set_active(-1)
2547 self._onlineFlight.set_active(self._wizard.loggedIn)
2548
2549 self._gatesModel.clear()
2550 if self._wizard.gui.config.onlineGateSystem and \
2551 self._wizard.loggedIn and \
2552 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2553 not self._wizard.entranceExam:
2554 occupiedGates = self._wizard._fleet.getOccupiedGateNumbers()
2555 for gateNumber in const.lhbpGateNumbers:
2556 if gateNumber not in occupiedGates:
2557 self._gatesModel.append([gateNumber])
2558 self._gateLabel.set_sensitive(True)
2559 self._gate.set_sensitive(True)
2560 self._gate.set_active(-1)
2561 else:
2562 self._gateLabel.set_sensitive(False)
2563 self._gate.set_sensitive(False)
2564
2565 def _backClicked(self, button):
2566 """Called when the Back button is pressed."""
2567 self.goBack()
2568
2569 def _updateButtons(self):
2570 """Update the sensitivity state of the buttons."""
2571 sensitive = self._flightType.get_active()>=0 and \
2572 (self._gatesModel.get_iter_first() is None or
2573 self._gate.get_active()>=0)
2574
2575 self._saveButton.set_sensitive(sensitive)
2576 self._sendButton.set_sensitive(sensitive and
2577 self._wizard.bookedFlight.id is not None)
2578
2579 def _flightTypeChanged(self, comboBox):
2580 """Called when the flight type has changed."""
2581 self._updateButtons()
2582
2583 def _gateChanged(self, comboBox):
2584 """Called when the arrival gate has changed."""
2585 self._updateButtons()
2586
2587 def _newFlightClicked(self, button):
2588 """Called when the new flight button is clicked."""
2589 gui = self._wizard.gui
2590 if not self._pirepSent and not self._pirepSaved:
2591 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2592 type = MESSAGETYPE_QUESTION,
2593 message_format = xstr("finish_newFlight_question"))
2594
2595 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
2596 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
2597
2598 dialog.set_title(WINDOW_TITLE_BASE)
2599 result = dialog.run()
2600 dialog.hide()
2601 if result!=RESPONSETYPE_YES:
2602 return
2603
2604 gui.reset()
2605
2606 def _saveClicked(self, button):
2607 """Called when the Save PIREP button is clicked."""
2608 gui = self._wizard.gui
2609
2610 bookedFlight = gui.bookedFlight
2611 tm = time.gmtime()
2612
2613 pilotID = self._wizard.pilotID
2614 if pilotID: pilotID += " "
2615 fileName = "%s%s %02d%02d %s-%s.pirep" % \
2616 (pilotID, str(bookedFlight.departureTime.date()),
2617 tm.tm_hour, tm.tm_min,
2618 bookedFlight.departureICAO,
2619 bookedFlight.arrivalICAO)
2620
2621 dialog = self._getSaveDialog()
2622
2623 if self._lastSavePath is None:
2624 pirepDirectory = gui.config.pirepDirectory
2625 if pirepDirectory is not None:
2626 dialog.set_current_folder(pirepDirectory)
2627 else:
2628 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
2629
2630 dialog.set_current_name(fileName)
2631 result = dialog.run()
2632 dialog.hide()
2633
2634 if result==RESPONSETYPE_OK:
2635 pirep = PIREP(gui.flight)
2636
2637 self._lastSavePath = text2unicode(dialog.get_filename())
2638
2639 if pirep.save(self._lastSavePath):
2640 type = MESSAGETYPE_INFO
2641 message = xstr("finish_save_done")
2642 secondary = None
2643 self._pirepSaved = True
2644 else:
2645 type = MESSAGETYPE_ERROR
2646 message = xstr("finish_save_failed")
2647 secondary = xstr("finish_save_failed_sec")
2648
2649 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2650 type = type, message_format = message)
2651 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2652 dialog.set_title(WINDOW_TITLE_BASE)
2653 if secondary is not None:
2654 dialog.format_secondary_markup(secondary)
2655
2656 dialog.run()
2657 dialog.hide()
2658
2659 def _getSaveDialog(self):
2660 """Get the PIREP saving dialog.
2661
2662 If it does not exist yet, create it."""
2663 if self._savePIREPDialog is None:
2664 gui = self._wizard.gui
2665 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
2666 xstr("finish_save_title"),
2667 action = FILE_CHOOSER_ACTION_SAVE,
2668 buttons = (gtk.STOCK_CANCEL,
2669 RESPONSETYPE_CANCEL,
2670 gtk.STOCK_OK, RESPONSETYPE_OK),
2671 parent = gui.mainWindow)
2672 dialog.set_modal(True)
2673 dialog.set_do_overwrite_confirmation(True)
2674
2675 filter = gtk.FileFilter()
2676 filter.set_name(xstr("file_filter_pireps"))
2677 filter.add_pattern("*.pirep")
2678 dialog.add_filter(filter)
2679
2680 filter = gtk.FileFilter()
2681 filter.set_name(xstr("file_filter_all"))
2682 filter.add_pattern("*.*")
2683 dialog.add_filter(filter)
2684
2685 self._savePIREPDialog = dialog
2686
2687 return self._savePIREPDialog
2688
2689
2690 def _sendClicked(self, button):
2691 """Called when the Send button is clicked."""
2692 pirep = PIREP(self._wizard.gui.flight)
2693 self._wizard.gui.sendPIREP(pirep,
2694 callback = self._handlePIREPSent)
2695
2696 def _handlePIREPSent(self, returned, result):
2697 """Callback for the PIREP sending result."""
2698 self._pirepSent = returned and result.success
2699 if self._wizard.gui.config.onlineGateSystem and \
2700 self._wizard.loggedIn and not self._wizard.entranceExam and \
2701 returned and result.success:
2702 bookedFlight = self._wizard.bookedFlight
2703 if bookedFlight.arrivalICAO=="LHBP":
2704 iter = self._gate.get_active_iter()
2705 gateNumber = None if iter is None \
2706 else self._gatesModel.get_value(iter, 0)
2707
2708 status = const.PLANE_PARKING if gateNumber is None \
2709 else const.PLANE_HOME
2710 else:
2711 gateNumber = None
2712 status = const.PLANE_AWAY
2713
2714 self._wizard.updatePlane(self._planeUpdated,
2715 bookedFlight.tailNumber,
2716 status, gateNumber = gateNumber)
2717
2718 def _planeUpdated(self, success):
2719 """Callback for the plane updating."""
2720 pass
2721
2722#-----------------------------------------------------------------------------
2723
2724class Wizard(gtk.VBox):
2725 """The flight wizard."""
2726 def __init__(self, gui):
2727 """Construct the wizard."""
2728 super(Wizard, self).__init__()
2729
2730 self.gui = gui
2731
2732 self._pages = []
2733 self._currentPage = None
2734
2735 self._loginPage = LoginPage(self)
2736 self._pages.append(self._loginPage)
2737 self._pages.append(FlightSelectionPage(self))
2738 self._pages.append(GateSelectionPage(self))
2739 self._pages.append(ConnectPage(self))
2740 self._payloadPage = PayloadPage(self)
2741 self._pages.append(self._payloadPage)
2742 self._payloadIndex = len(self._pages)
2743 self._pages.append(TimePage(self))
2744 self._pages.append(FuelPage(self))
2745 self._routePage = RoutePage(self)
2746 self._pages.append(self._routePage)
2747 self._departureBriefingPage = BriefingPage(self, True)
2748 self._pages.append(self._departureBriefingPage)
2749 self._arrivalBriefingPage = BriefingPage(self, False)
2750 self._pages.append(self._arrivalBriefingPage)
2751 self._arrivalBriefingIndex = len(self._pages)
2752 self._takeoffPage = TakeoffPage(self)
2753 self._pages.append(self._takeoffPage)
2754 self._landingPage = LandingPage(self)
2755 self._pages.append(self._landingPage)
2756 self._finishPage = FinishPage(self)
2757 self._pages.append(self._finishPage)
2758
2759 maxWidth = 0
2760 maxHeight = 0
2761 for page in self._pages:
2762 page.show_all()
2763 pageSizeRequest = page.size_request()
2764 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
2765 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
2766 maxWidth = max(maxWidth, width)
2767 maxHeight = max(maxHeight, height)
2768 page.setStyle()
2769 maxWidth += 16
2770 maxHeight += 32
2771 self.set_size_request(maxWidth, maxHeight)
2772
2773 self._initialize()
2774
2775 @property
2776 def pilotID(self):
2777 """Get the pilot ID, if given."""
2778 return self._loginPage.pilotID
2779
2780 @property
2781 def entranceExam(self):
2782 """Get whether an entrance exam is about to be taken."""
2783 return self._loginPage.entranceExam
2784
2785 @property
2786 def loggedIn(self):
2787 """Indicate if there was a successful login."""
2788 return self._loginResult is not None
2789
2790 @property
2791 def loginResult(self):
2792 """Get the login result."""
2793 return self._loginResult
2794
2795 def setCurrentPage(self, index, finalize = False):
2796 """Set the current page to the one with the given index."""
2797 assert index < len(self._pages)
2798
2799 fromPage = self._currentPage
2800 if fromPage is not None:
2801 page = self._pages[fromPage]
2802 if finalize and not page._completed:
2803 page.complete()
2804 self.remove(page)
2805
2806 self._currentPage = index
2807 page = self._pages[index]
2808 self.add(page)
2809 if page._fromPage is None:
2810 page._fromPage = fromPage
2811 page.initialize()
2812 self.show_all()
2813 if fromPage is not None:
2814 self.grabDefault()
2815
2816 @property
2817 def bookedFlight(self):
2818 """Get the booked flight selected."""
2819 return self._bookedFlight
2820
2821 @property
2822 def cargoWeight(self):
2823 """Get the calculated ZFW value."""
2824 return self._payloadPage.cargoWeight
2825
2826 @property
2827 def zfw(self):
2828 """Get the calculated ZFW value."""
2829 return 0 if self._bookedFlight is None \
2830 else self._payloadPage.calculateZFW()
2831
2832 @property
2833 def filedCruiseAltitude(self):
2834 """Get the filed cruise altitude."""
2835 return self._routePage.filedCruiseLevel * 100
2836
2837 @property
2838 def cruiseAltitude(self):
2839 """Get the cruise altitude."""
2840 return self._routePage.cruiseLevel * 100
2841
2842 @property
2843 def route(self):
2844 """Get the route."""
2845 return self._routePage.route
2846
2847 @property
2848 def departureMETAR(self):
2849 """Get the METAR of the departure airport."""
2850 return self._departureBriefingPage.metar
2851
2852 @property
2853 def arrivalMETAR(self):
2854 """Get the METAR of the arrival airport."""
2855 return self._arrivalBriefingPage.metar
2856
2857 @property
2858 def departureRunway(self):
2859 """Get the departure runway."""
2860 return self._takeoffPage.runway
2861
2862 @property
2863 def sid(self):
2864 """Get the SID."""
2865 return self._takeoffPage.sid
2866
2867 @property
2868 def v1(self):
2869 """Get the V1 speed."""
2870 return self._takeoffPage.v1
2871
2872 @property
2873 def vr(self):
2874 """Get the Vr speed."""
2875 return self._takeoffPage.vr
2876
2877 @property
2878 def v2(self):
2879 """Get the V2 speed."""
2880 return self._takeoffPage.v2
2881
2882 @property
2883 def arrivalRunway(self):
2884 """Get the arrival runway."""
2885 return self._landingPage.runway
2886
2887 @property
2888 def star(self):
2889 """Get the STAR."""
2890 return self._landingPage.star
2891
2892 @property
2893 def transition(self):
2894 """Get the transition."""
2895 return self._landingPage.transition
2896
2897 @property
2898 def approachType(self):
2899 """Get the approach type."""
2900 return self._landingPage.approachType
2901
2902 @property
2903 def vref(self):
2904 """Get the Vref speed."""
2905 return self._landingPage.vref
2906
2907 @property
2908 def flightType(self):
2909 """Get the flight type."""
2910 return self._finishPage.flightType
2911
2912 @property
2913 def online(self):
2914 """Get whether the flight was online or not."""
2915 return self._finishPage.online
2916
2917 def nextPage(self, finalize = True):
2918 """Go to the next page."""
2919 self.jumpPage(1, finalize)
2920
2921 def jumpPage(self, count, finalize = True):
2922 """Go to the page which is 'count' pages after the current one."""
2923 self.setCurrentPage(self._currentPage + count, finalize = finalize)
2924
2925 def grabDefault(self):
2926 """Make the default button of the current page the default."""
2927 self._pages[self._currentPage].grabDefault()
2928
2929 def connected(self, fsType, descriptor):
2930 """Called when the connection could be made to the simulator."""
2931 self.nextPage()
2932
2933 def reset(self, loginResult):
2934 """Resets the wizard to go back to the login page."""
2935 self._initialize(keepLoginResult = loginResult is None,
2936 loginResult = loginResult)
2937
2938 def setStage(self, stage):
2939 """Set the flight stage to the given one."""
2940 if stage==const.STAGE_TAKEOFF:
2941 self._takeoffPage.allowForward()
2942 elif stage==const.STAGE_LANDING:
2943 if not self._arrivalBriefingPage.metarEdited:
2944 print "Downloading arrival METAR again"
2945 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
2946 [self._bookedFlight.arrivalICAO])
2947
2948 elif stage==const.STAGE_END:
2949 for page in self._pages:
2950 page.flightEnded()
2951
2952 def _initialize(self, keepLoginResult = False, loginResult = None):
2953 """Initialize the wizard."""
2954 if not keepLoginResult:
2955 self._loginResult = loginResult
2956
2957 self._loginCallback = None
2958
2959 self._fleet = None
2960 self._fleetCallback = None
2961
2962 self._bookedFlight = None
2963 self._departureGate = "-"
2964 self._fuelData = None
2965 self._departureNOTAMs = None
2966 self._departureMETAR = None
2967 self._arrivalNOTAMs = None
2968 self._arrivalMETAR = None
2969
2970 firstPage = 0 if self._loginResult is None else 1
2971 for page in self._pages[firstPage:]:
2972 page.reset()
2973
2974 self.setCurrentPage(firstPage)
2975
2976 def login(self, callback, pilotID, password, entranceExam):
2977 """Called when the login button was clicked."""
2978 self._loginCallback = callback
2979 if pilotID is None:
2980 loginResult = self._loginResult
2981 assert loginResult is not None and loginResult.loggedIn
2982 pilotID = loginResult.pilotID
2983 password = loginResult.password
2984 entranceExam = loginResult.entranceExam
2985 busyMessage = xstr("reload_busy")
2986 else:
2987 self._loginResult = None
2988 busyMessage = xstr("login_busy")
2989
2990 self.gui.beginBusy(busyMessage)
2991
2992 self.gui.webHandler.login(self._loginResultCallback,
2993 pilotID, password,
2994 entranceExam = entranceExam)
2995
2996 def reloadFlights(self, callback):
2997 """Reload the flights from the MAVA server."""
2998 self.login(callback, None, None, None)
2999
3000 def _loginResultCallback(self, returned, result):
3001 """The login result callback, called in the web handler's thread."""
3002 gobject.idle_add(self._handleLoginResult, returned, result)
3003
3004 def _handleLoginResult(self, returned, result):
3005 """Handle the login result."""
3006 self.gui.endBusy()
3007 isReload = self._loginResult is not None
3008 if returned:
3009 if result.loggedIn:
3010 self._loginResult = result
3011 else:
3012 if isReload:
3013 message = xstr("reload_failed")
3014 else:
3015 message = xstr("login_entranceExam_invalid"
3016 if self.entranceExam else
3017 xstr("login_invalid"))
3018 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3019 type = MESSAGETYPE_ERROR,
3020 message_format = message)
3021 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3022 dialog.set_title(WINDOW_TITLE_BASE)
3023 if isReload:
3024 secondary = xstr("reload_failed_sec")
3025 else:
3026 secondary = xstr("login_entranceExam_invalid_sec"
3027 if self.entranceExam else
3028 xstr("login_invalid_sec"))
3029 dialog.format_secondary_markup(secondary)
3030 dialog.run()
3031 dialog.hide()
3032 else:
3033 message = xstr("reload_failconn") if isReload \
3034 else xstr("login_failconn")
3035 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3036 type = MESSAGETYPE_ERROR,
3037 message_format = message)
3038 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3039 dialog.set_title(WINDOW_TITLE_BASE)
3040 secondary = xstr("reload_failconn_sec") if isReload \
3041 else xstr("login_failconn_sec")
3042 dialog.format_secondary_markup(secondary)
3043
3044 dialog.run()
3045 dialog.hide()
3046
3047 callback = self._loginCallback
3048 self._loginCallback = None
3049 callback(returned, result)
3050
3051 def getFleet(self, callback, force = False):
3052 """Get the fleet via the GUI and call the given callback."""
3053 self._fleetCallback = callback
3054 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
3055
3056 def _fleetRetrieved(self, fleet):
3057 """Callback for the fleet retrieval."""
3058 self._fleet = fleet
3059 if self._fleetCallback is not None:
3060 self._fleetCallback(fleet)
3061 self._fleetCallback = None
3062
3063 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
3064 """Update the given plane's gate information."""
3065 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
3066 callback = callback)
3067
3068 def _connectSimulator(self):
3069 """Connect to the simulator."""
3070 self.gui.connectSimulator(self._bookedFlight.aircraftType)
3071
3072 def _arrivalMETARCallback(self, returned, result):
3073 """Called when the METAR of the arrival airport is retrieved."""
3074 gobject.idle_add(self._handleArrivalMETAR, returned, result)
3075
3076 def _handleArrivalMETAR(self, returned, result):
3077 """Called when the METAR of the arrival airport is retrieved."""
3078 icao = self._bookedFlight.arrivalICAO
3079 if returned and icao in result.metars:
3080 metar = result.metars[icao]
3081 if metar!="":
3082 self._arrivalBriefingPage.setMETAR(metar)
3083
3084#-----------------------------------------------------------------------------
3085
Note: See TracBrowser for help on using the repository browser.