Index: src/mlx/gui/acars.py
===================================================================
--- src/mlx/gui/acars.py	(revision 747)
+++ src/mlx/gui/acars.py	(revision 747)
@@ -0,0 +1,36 @@
+
+from common import *
+
+from mlx.i18n import xstr
+import mlx.const as const
+
+import cef
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.acars
+#
+# The ACARS tab.
+#
+# This module implements to \ref ACARS class, which displays the MAVA ACARS in a
+# browser window using CEF
+
+#------------------------------------------------------------------------------
+
+class ACARS(gtk.VBox):
+    """The flight info tab."""
+    # The URL of the ACARS map
+    URL = MAVA_BASE_URL + "/acars2/show.html"
+
+    def __init__(self, gui):
+        """Construct the flight info tab."""
+        super(ACARS, self).__init__()
+        self._gui = gui
+
+    def start(self):
+        """Start the browser."""
+        container = cef.getContainer()
+
+        self.pack_start(container, True, True, 0)
+
+        cef.startInContainer(container, ACARS.URL)
Index: src/mlx/gui/acars.py
===================================================================
--- src/mlx/gui/acars.py	(revision 747)
+++ 	(revision )
@@ -1,36 +1,0 @@
-
-from common import *
-
-from mlx.i18n import xstr
-import mlx.const as const
-
-import cef
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.acars
-#
-# The ACARS tab.
-#
-# This module implements to \ref ACARS class, which displays the MAVA ACARS in a
-# browser window using CEF
-
-#------------------------------------------------------------------------------
-
-class ACARS(gtk.VBox):
-    """The flight info tab."""
-    # The URL of the ACARS map
-    URL = MAVA_BASE_URL + "/acars2/show.html"
-
-    def __init__(self, gui):
-        """Construct the flight info tab."""
-        super(ACARS, self).__init__()
-        self._gui = gui
-
-    def start(self):
-        """Start the browser."""
-        container = cef.getContainer()
-
-        self.pack_start(container, True, True, 0)
-
-        cef.startInContainer(container, ACARS.URL)
Index: src/mlx/gui/bugreport.py
===================================================================
--- src/mlx/gui/bugreport.py	(revision 492)
+++ src/mlx/gui/bugreport.py	(revision 492)
@@ -0,0 +1,145 @@
+
+from common import *
+
+from mlx.i18n import xstr
+import mlx.const as const
+import mlx.config as config
+
+import os
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.bugreport
+#
+# The bug report dialog
+#
+# This module implements the bug report dialog.
+
+#------------------------------------------------------------------------------
+
+class BugReportDialog(gtk.Dialog):
+    """The dialog to report a bug."""
+    def __init__(self, gui):
+        super(BugReportDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
+                                              xstr("bugreport_title"),
+                                              gui.mainWindow,
+                                              DIALOG_MODAL)
+
+        self.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
+        self._sendButton = self.add_button(xstr("button_send"), RESPONSETYPE_ACCEPT)
+        self._sendButton.set_can_default(True)
+        self._gui = gui
+
+        contentArea = self.get_content_area()
+
+        contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                         xscale = 0.0, yscale = 0.0)
+        contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
+                                     padding_left = 8, padding_right = 8)
+
+        contentArea.pack_start(contentAlignment, False, False, 0)
+
+        contentVBox = gtk.VBox()
+        contentAlignment.add(contentVBox)
+
+        label = gtk.Label(xstr("bugreport_summary"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+
+        contentVBox.pack_start(label, False, False, 4)
+
+        self._summary = summary = gtk.Entry()
+        summary.connect("changed", self._summaryChanged)
+        summary.set_tooltip_text(xstr("bugreport_summary_tooltip"))
+        summary.set_width_chars(80)
+        label.set_mnemonic_widget(summary)
+        contentVBox.pack_start(summary, True, True, 4)
+
+        label = gtk.Label(xstr("bugreport_description"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+
+        contentVBox.pack_start(label, False, False, 4)
+
+        self._description = description = gtk.TextView()
+        description.set_tooltip_text(xstr("bugreport_description_tooltip"))
+        description.set_wrap_mode(WRAP_WORD)
+        label.set_mnemonic_widget(description)
+
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.add(description)
+        scrolledWindow.set_size_request(-1, 200)
+        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+        scrolledWindow.set_shadow_type(SHADOW_IN)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 1.0, yscale = 1.0)
+        alignment.add(scrolledWindow)
+
+        contentVBox.pack_start(alignment, True, True, 4)
+
+        emailBox = gtk.HBox()
+        contentVBox.pack_start(emailBox, False, False, 4)
+
+        label = gtk.Label(xstr("bugreport_email"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+
+        emailBox.pack_start(label, False, False, 0)
+
+        alignment = gtk.Alignment()
+        emailBox.pack_start(alignment, False, False, 8)
+
+        self._email = email = gtk.Entry()
+        email.set_tooltip_text(xstr("bugreport_email_tooltip"))
+        label.set_mnemonic_widget(email)
+        emailBox.pack_start(email, True, True, 0)
+
+
+    def run(self):
+        """Run the checklist editor dialog."""
+        self.set_sensitive(True)
+        self._description.set_sensitive(True)
+        self._updateButtons()
+        self._sendButton.grab_default()
+        self.show_all()
+        response = super(BugReportDialog, self).run()
+
+        print "response", response, RESPONSETYPE_ACCEPT
+        if response==RESPONSETYPE_ACCEPT:
+            self._send()
+        else:
+            self.hide()
+
+    def _summaryChanged(self, entry):
+        """Called when the summary has changed."""
+        self._updateButtons()
+
+    def _updateButtons(self):
+        """Update the sensitivity of the buttoms."""
+        self._sendButton.set_sensitive(self._summary.get_text()!="")
+
+    def _send(self):
+        """Send the bug report."""
+        descriptionBuffer = self._description.get_buffer()
+        description = \
+          descriptionBuffer.get_text(descriptionBuffer.get_start_iter(),
+                                     descriptionBuffer.get_end_iter(),
+                                     False)
+        description = text2unicode(description)
+        self.set_sensitive(False)
+        self._gui.sendBugReport(text2unicode(self._summary.get_text()),
+                                description,
+                                text2unicode(self._email.get_text()),
+                                self._bugReportSent)
+
+    def _bugReportSent(self, returned, result):
+        """Called when the bug report was sent."""
+        self.set_sensitive(True)
+        self._description.set_sensitive(True)
+        if returned and result.success:
+            self.hide()
+            self._summary.set_text("")
+            self._description.get_buffer().set_text("")
+            self._email.set_text("")
+        else:
+            self.run()
Index: src/mlx/gui/bugreport.py
===================================================================
--- src/mlx/gui/bugreport.py	(revision 492)
+++ 	(revision )
@@ -1,145 +1,0 @@
-
-from common import *
-
-from mlx.i18n import xstr
-import mlx.const as const
-import mlx.config as config
-
-import os
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.bugreport
-#
-# The bug report dialog
-#
-# This module implements the bug report dialog.
-
-#------------------------------------------------------------------------------
-
-class BugReportDialog(gtk.Dialog):
-    """The dialog to report a bug."""
-    def __init__(self, gui):
-        super(BugReportDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
-                                              xstr("bugreport_title"),
-                                              gui.mainWindow,
-                                              DIALOG_MODAL)
-
-        self.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
-        self._sendButton = self.add_button(xstr("button_send"), RESPONSETYPE_ACCEPT)
-        self._sendButton.set_can_default(True)
-        self._gui = gui
-
-        contentArea = self.get_content_area()
-
-        contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                         xscale = 0.0, yscale = 0.0)
-        contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
-                                     padding_left = 8, padding_right = 8)
-
-        contentArea.pack_start(contentAlignment, False, False, 0)
-
-        contentVBox = gtk.VBox()
-        contentAlignment.add(contentVBox)
-
-        label = gtk.Label(xstr("bugreport_summary"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-
-        contentVBox.pack_start(label, False, False, 4)
-
-        self._summary = summary = gtk.Entry()
-        summary.connect("changed", self._summaryChanged)
-        summary.set_tooltip_text(xstr("bugreport_summary_tooltip"))
-        summary.set_width_chars(80)
-        label.set_mnemonic_widget(summary)
-        contentVBox.pack_start(summary, True, True, 4)
-
-        label = gtk.Label(xstr("bugreport_description"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-
-        contentVBox.pack_start(label, False, False, 4)
-
-        self._description = description = gtk.TextView()
-        description.set_tooltip_text(xstr("bugreport_description_tooltip"))
-        description.set_wrap_mode(WRAP_WORD)
-        label.set_mnemonic_widget(description)
-
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.add(description)
-        scrolledWindow.set_size_request(-1, 200)
-        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-        scrolledWindow.set_shadow_type(SHADOW_IN)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 1.0, yscale = 1.0)
-        alignment.add(scrolledWindow)
-
-        contentVBox.pack_start(alignment, True, True, 4)
-
-        emailBox = gtk.HBox()
-        contentVBox.pack_start(emailBox, False, False, 4)
-
-        label = gtk.Label(xstr("bugreport_email"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-
-        emailBox.pack_start(label, False, False, 0)
-
-        alignment = gtk.Alignment()
-        emailBox.pack_start(alignment, False, False, 8)
-
-        self._email = email = gtk.Entry()
-        email.set_tooltip_text(xstr("bugreport_email_tooltip"))
-        label.set_mnemonic_widget(email)
-        emailBox.pack_start(email, True, True, 0)
-
-
-    def run(self):
-        """Run the checklist editor dialog."""
-        self.set_sensitive(True)
-        self._description.set_sensitive(True)
-        self._updateButtons()
-        self._sendButton.grab_default()
-        self.show_all()
-        response = super(BugReportDialog, self).run()
-
-        print "response", response, RESPONSETYPE_ACCEPT
-        if response==RESPONSETYPE_ACCEPT:
-            self._send()
-        else:
-            self.hide()
-
-    def _summaryChanged(self, entry):
-        """Called when the summary has changed."""
-        self._updateButtons()
-
-    def _updateButtons(self):
-        """Update the sensitivity of the buttoms."""
-        self._sendButton.set_sensitive(self._summary.get_text()!="")
-
-    def _send(self):
-        """Send the bug report."""
-        descriptionBuffer = self._description.get_buffer()
-        description = \
-          descriptionBuffer.get_text(descriptionBuffer.get_start_iter(),
-                                     descriptionBuffer.get_end_iter(),
-                                     False)
-        description = text2unicode(description)
-        self.set_sensitive(False)
-        self._gui.sendBugReport(text2unicode(self._summary.get_text()),
-                                description,
-                                text2unicode(self._email.get_text()),
-                                self._bugReportSent)
-
-    def _bugReportSent(self, returned, result):
-        """Called when the bug report was sent."""
-        self.set_sensitive(True)
-        self._description.set_sensitive(True)
-        if returned and result.success:
-            self.hide()
-            self._summary.set_text("")
-            self._description.get_buffer().set_text("")
-            self._email.set_text("")
-        else:
-            self.run()
Index: src/mlx/gui/callouts.py
===================================================================
--- src/mlx/gui/callouts.py	(revision 300)
+++ src/mlx/gui/callouts.py	(revision 300)
@@ -0,0 +1,503 @@
+
+from common import *
+
+from mlx.i18n import xstr
+import mlx.const as const
+import mlx.config as config
+
+import os
+import re
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.callouts
+#
+# Editor dialog for approach callouts.
+#
+# The dialog consists of an aircraft type selector box at the top, and a table
+# with two buttons below it. The table contains the callout files with the
+# corresponding altitudes, and is sorted according to the altitude. When a new
+# file is added, the program finds out a new altitude for it. If the file's
+# name contains numbers that are not used as altitudes yet, the most suitable
+# of those numbers will be used. Otherwise a 'usual' altitude is searched for,
+# in the direction according to the sort order, and if that fails too, the
+# altitudes are tried one-by-one. See the
+# \ref ApproachCalloutsEditor._getNewAltitude function for more details.
+
+#------------------------------------------------------------------------------
+
+class ApproachCalloutsEditor(gtk.Dialog):
+    """The dialog to edit the approach callouts."""
+    integerRE = re.compile("[0-9]+")
+
+    # A list of "usual" altitudes for callouts
+    _usualAltitudes = [10, 20, 30, 40, 50, 100, 200, 300, 400, 500, 1000,
+                       1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000]
+
+    @staticmethod
+    def _getNextUsualAltitude(altitude, descending):
+        """Get the next altitude coming after the given one in the
+        given direction."""
+        if descending:
+            previous = None
+            for alt in ApproachCalloutsEditor._usualAltitudes:
+                if alt>=altitude: return previous
+                previous = alt
+        else:
+            for alt in ApproachCalloutsEditor._usualAltitudes:
+                if alt>altitude: return alt                
+                    
+        return None
+
+    def __init__(self, gui):
+        super(ApproachCalloutsEditor, self).__init__(WINDOW_TITLE_BASE + " - " +
+                                                     xstr("callouts_title"),
+                                                     gui.mainWindow,
+                                                     DIALOG_MODAL)
+
+        self.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
+        self.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
+
+        self._gui = gui
+        self._approachCallouts = {}
+        self._currentAircraftType = const.aircraftTypes[0]
+        self._fileOpenDialog = None
+
+        contentArea = self.get_content_area()
+
+        # FIXME: common code with the checklist editor
+        typeBox = gtk.HBox()
+
+        label = gtk.Label(xstr("callouts_aircraftType"))
+        label.set_use_underline(True)
+
+        typeBox.pack_start(label, False, False, 4)
+
+        self._aircraftTypeModel = gtk.ListStore(str, int)
+        for type in const.aircraftTypes:
+            name = aircraftNames[type] if type in aircraftNames \
+                   else "Aircraft type #%d" % (type,)
+            self._aircraftTypeModel.append([name, type])
+        self._aircraftType = gtk.ComboBox(model = self._aircraftTypeModel)
+        renderer = gtk.CellRendererText()
+        self._aircraftType.pack_start(renderer, True)
+        self._aircraftType.add_attribute(renderer, "text", 0)
+        self._aircraftType.set_tooltip_text(xstr("callouts_aircraftType_tooltip"))
+        self._aircraftType.set_active(0)
+        self._aircraftType.connect("changed", self._aircraftTypeChanged)
+        label.set_mnemonic_widget(self._aircraftType)
+
+        typeBox.pack_start(self._aircraftType, True, True, 4)
+
+        typeBoxAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                         xscale = 0.0, yscale = 0.0)
+        typeBoxAlignment.set_size_request(400, -1)
+        typeBoxAlignment.add(typeBox)
+
+        contentArea.pack_start(typeBoxAlignment, False, False, 12)
+        # FIXME: common code until here, but note that some texts are different
+
+        contentBox = gtk.HBox()        
+
+        controlBox = gtk.VBox()
+        controlAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                         xscale = 0.0, yscale = 0.0)
+        controlAlignment.set_padding(padding_top = 0, padding_bottom = 0,
+                                     padding_left = 32, padding_right = 32)
+        controlAlignment.add(controlBox)
+        contentBox.pack_start(controlAlignment, False, False, 0)
+
+        self._addButton = gtk.Button(xstr("callouts_add"))
+        self._addButton.set_use_underline(True)
+        self._addButton.set_tooltip_text(xstr("callouts_add_tooltip"))
+        self._addButton.connect("clicked", self._addButtonClicked)
+        addAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
+                                     xscale = 0.0, yscale = 0.0)
+        addAlignment.set_padding(padding_top = 24, padding_bottom = 0,
+                                 padding_left = 0, padding_right = 0)
+        addAlignment.add(self._addButton)
+        controlBox.pack_start(addAlignment, False, False, 0)
+
+        self._removeButton = gtk.Button(xstr("callouts_remove"))
+        self._removeButton.set_use_underline(True)
+        self._removeButton.set_tooltip_text(xstr("callouts_remove_tooltip"))
+        self._removeButton.set_sensitive(False)
+        self._removeButton.connect("clicked", self._removeButtonClicked)
+
+        removeAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
+                                        xscale = 0.0, yscale = 0.0)
+        removeAlignment.set_padding(padding_top = 24, padding_bottom = 0,
+                                    padding_left = 0, padding_right = 0)
+        removeAlignment.add(self._removeButton)
+        controlBox.pack_start(removeAlignment, False, False, 0)
+
+        self._fileListModel = gtk.ListStore(int, str, str)
+        self._fileListModel.set_sort_column_id(0, SORT_DESCENDING)
+
+        self._addingFile = False
+        self._fileListModel.connect("row-inserted", self._fileAdded)
+        self._lastAddedAltitude = None
+        
+        self._fileList = gtk.TreeView(model = self._fileListModel)
+
+        renderer = gtk.CellRendererSpin()
+        renderer.set_property("editable", True)
+
+        adjustment = gtk.Adjustment(0, 0, 5000, 10, 100)
+        renderer.set_property("adjustment", adjustment);
+        renderer.connect("edited", self._altitudeEdited)
+        
+        column = gtk.TreeViewColumn(xstr("callouts_header_altitude"),
+                                    renderer, text = 0)
+        self._fileList.append_column(column)
+        column.set_expand(True)
+        column.set_clickable(True)
+        column.set_reorderable(False)
+        column.set_sort_indicator(True)
+        column.set_sort_column_id(0)
+        column.set_sort_order(SORT_DESCENDING)
+        column.set_expand(False)
+
+        column = gtk.TreeViewColumn(xstr("callouts_header_path"),
+                                    gtk.CellRendererText(), text = 1)
+        self._fileList.append_column(column)
+        column.set_expand(True)
+        column.set_clickable(False)
+        column.set_reorderable(False)
+        column.set_expand(True)
+        
+        self._fileList.set_tooltip_column(2)
+        self._fileList.set_size_request(300, -1)
+        self._fileList.set_reorderable(False)
+        self._fileList.connect("button-press-event",
+                               self._fileListButtonPressed)
+        selection = self._fileList.get_selection()
+        selection.set_mode(SELECTION_MULTIPLE)
+        selection.connect("changed", self._fileListSelectionChanged)
+
+        self._buildFileListPopupMenu()
+
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.add(self._fileList)
+        scrolledWindow.set_size_request(300, -1)
+        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+        scrolledWindow.set_shadow_type(SHADOW_IN)
+        
+        fileListAlignment = gtk.Alignment(xscale=1.0, yscale=1.0, 
+                                          xalign=0.5, yalign=0.5)
+        fileListAlignment.set_padding(padding_top = 0, padding_bottom = 16,
+                                      padding_left = 0, padding_right = 8)
+        fileListAlignment.add(scrolledWindow)
+        
+        contentBox.pack_start(fileListAlignment, False, False, 4)
+
+        contentArea.pack_start(contentBox, True, True, 4)
+
+        self.set_size_request(-1, 300)
+
+    def run(self):
+        """Run the approach callouts editor dialog."""
+        self._approachCallouts = {}
+        self._displayCurrentApproachCallouts()
+        self.show_all()
+        response = super(ApproachCalloutsEditor, self).run()
+        self.hide()
+
+        if response==RESPONSETYPE_ACCEPT:
+            self._saveApproachCallouts()
+            config = self._gui.config
+            for (aircraftType, approachCallouts) in \
+                    self._approachCallouts.iteritems():
+                config.setApproachCallouts(aircraftType, approachCallouts)
+            config.save()
+
+    def _aircraftTypeChanged(self, comboBox):
+        """Called when the aircraft's type has changed."""        
+        self._saveApproachCallouts()
+        self._displayCurrentApproachCallouts()
+        
+    def _addButtonClicked(self, button):
+        """Called when the Add button is clicked."""
+        dialog = self._getFileOpenDialog()
+        
+        dialog.show_all()
+        result = dialog.run()
+        dialog.hide()
+
+        if result==RESPONSETYPE_OK:
+            filePath = dialog.get_filename()
+            baseName = os.path.basename(filePath)
+            altitude = self._getNewAltitude(baseName)
+            self._addingFile = True
+            self._lastAddedAltitude = altitude
+            self._fileListModel.append([altitude, baseName, filePath])
+            self._addingFile = False
+
+    def _fileAdded(self, model, path, iter):
+        """Called when a file is added to the list of callouts.
+
+        Makes the treeview to edit the altitude in the given row."""
+        if self._addingFile:
+            gobject.idle_add(self._selectFile)
+            self._fileList.grab_focus()
+            self.grab_focus()
+
+    def _selectFile(self):
+        """Select the file with the last added altitude."""
+        if self._lastAddedAltitude is None: return
+
+        model = self._fileListModel
+        iter = model.get_iter_first()
+        while iter is not None:
+            if model.get_value(iter, 0)==self._lastAddedAltitude: break
+            iter = model.iter_next(iter)
+        if iter is not None:
+            self._fileList.set_cursor(model.get_path(iter),
+                                      self._fileList.get_column(0), True)
+        self._lastAddedAltitude = None
+                             
+    def _removeButtonClicked(self, button):
+        """Called when the Remove button is clicked."""
+        self._removeSelected()
+        
+    def _removeSelected(self):
+        """Remove the selected files."""
+        selection = self._fileList.get_selection()
+        (model, paths) = selection.get_selected_rows()
+
+        iters = [model.get_iter(path) for path in paths]
+
+        for i in iters:
+            if i is not None:
+                model.remove(i)
+        
+    def _fileListSelectionChanged(self, selection):
+        """Called when the selection in the file list changes."""
+        anySelected = selection.count_selected_rows()>0
+        self._removeButton.set_sensitive(anySelected)
+
+    def _getAircraftType(self):
+        """Get the currently selected aircraft type."""
+        # FIXME: the same code as in the checklist editor
+        index = self._aircraftType.get_active()
+        return self._aircraftTypeModel[index][1]        
+
+    def _altitudeEdited(self, widget, path, value):
+        """Called when an altitude is edited"""
+        newAltitude = int(value)
+
+        model = self._fileListModel
+        editedIter = model.get_iter_from_string(path)
+        editedPath = model.get_path(editedIter)
+        otherPath = self._hasAltitude(newAltitude, ignorePath = editedPath)
+        if otherPath is not None:
+            dialog = gtk.MessageDialog(parent = self,
+                                       type = MESSAGETYPE_QUESTION,
+                                       message_format =
+                                       xstr("callouts_altitude_clash"))
+            dialog.format_secondary_markup(xstr("callouts_altitude_clash_sec"))
+            dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
+            dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
+            dialog.set_title(WINDOW_TITLE_BASE)
+
+            result = dialog.run()
+            dialog.hide()
+
+            if result!=RESPONSETYPE_YES:
+                newAltitude = None
+                
+        if newAltitude is not None:
+            model[editedPath][0] = newAltitude
+            if otherPath is not None:
+                model.remove(model.get_iter(otherPath))
+
+    def _saveApproachCallouts(self):
+        """Save the currently displayed list of approach callouts for the
+        previously displayed aircraft type."""
+        mapping = {}
+        model = self._fileListModel
+        iter = model.get_iter_first()
+        while iter is not None:
+            altitude = int(model.get(iter, 0)[0])
+            path = model.get(iter, 2)[0]
+            mapping[altitude] = path
+            iter = model.iter_next(iter)
+
+        self._approachCallouts[self._currentAircraftType] = \
+            config.ApproachCallouts(mapping)
+        
+    def _displayCurrentApproachCallouts(self):
+        """Display the approach callouts for the currently selected aircraft
+        type."""
+        aircraftType = self._getAircraftType()
+        self._currentAircraftType = aircraftType
+        if aircraftType not in self._approachCallouts:
+            self._approachCallouts[aircraftType] = \
+                self._gui.config.getApproachCallouts(aircraftType).clone()
+        approachCallouts = self._approachCallouts[aircraftType]
+
+        self._fileListModel.clear()
+        for (altitude, path) in approachCallouts:
+            self._fileListModel.append([altitude, os.path.basename(path), path])        
+
+    def _getFileOpenDialog(self):
+        """Get the dialog to open a file.
+
+        If it does not exist yet, it will be created."""
+        if self._fileOpenDialog is None:
+            dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
+                                           xstr("callouts_open_title"),
+                                           action = FILE_CHOOSER_ACTION_OPEN,
+                                           buttons = (gtk.STOCK_CANCEL,
+                                                      RESPONSETYPE_CANCEL,
+                                                      gtk.STOCK_OK, RESPONSETYPE_OK),
+                                           parent = self)
+            dialog.set_modal(True)            
+            dialog.set_do_overwrite_confirmation(True)
+      
+            # FIXME: create the filters in one location and use them
+            # from there
+            filter = gtk.FileFilter()
+            filter.set_name(xstr("file_filter_audio"))
+            filter.add_pattern("*.wav")
+            filter.add_pattern("*.mp3")
+            dialog.add_filter(filter)
+
+            filter = gtk.FileFilter()
+            filter.set_name(xstr("file_filter_all"))
+            filter.add_pattern("*.*")
+            dialog.add_filter(filter)
+            
+            self._fileOpenDialog = dialog
+
+        return self._fileOpenDialog
+    
+    def _getNewAltitude(self, baseName):
+        """Get a new, unique altitude for the audio file with the given
+        base name.
+
+        First the given file name is searched for suitable
+        numbers. Otherwise the smallest altitude in the model is
+        considered, and, depending on the actual ordering of the
+        table, a suitable smaller or greater value is found. It is
+        ensured that the number is unique, unless all numbers are
+        taken.
+        
+        If there is no entry in the table yet, 2500 is returned if the
+        table is sorted descending, 10 otherwise."""
+        altitude = self._getNewAltitudeFromFileName(baseName)
+        if altitude is not None: return altitude
+
+        descending = self._fileList.get_column(0).get_sort_order()==SORT_DESCENDING
+        model = self._fileListModel
+        numEntries = model.iter_n_children(None)
+        if numEntries==0:
+            return 2500 if descending else 10
+        else:
+            selection = self._fileList.get_selection()
+            (_model, paths) = selection.get_selected_rows()
+
+            if paths:
+                startIter = model.get_iter(max(paths))
+            else:
+                startIter = model.iter_nth_child(None, numEntries-1)
+
+            startValue = model.get_value(startIter, 0)
+
+            altitude = self._getNextValidUsualAltitude(startValue, descending)
+            if altitude is None:
+                altitude = self._getNextValidUsualAltitude(startValue,
+                                                           not descending)
+
+            if altitude is None:
+                for altitude in range(0 if descending else 4999,
+                                      4999 if descending else 0,
+                                      1 if descending else -1):
+                    if not self._hasAltitude(altitude): break
+                                   
+            return altitude
+
+    def _getNewAltitudeFromFileName(self, baseName):
+        """Get a new altitude value from the given file name.
+
+        The name is traversed for numbers. If a number is less than
+        5000 and there is no such altitude yet in the table, it is
+        checked if it is divisible by 100 or 1000, and if so, it gets
+        a score of 2. If it is divisible by 10, the score will be 1,
+        otherwise 0. The first highest scoring number is returned, if
+        there are any at all, otherwise None."""
+        candidateAltitude = None
+        candidateScore = None
+        
+        (baseName, _) = os.path.splitext(baseName)
+        numbers = ApproachCalloutsEditor.integerRE.findall(baseName)
+        for number in numbers:
+            value = int(number)
+            if value<5000 and not self._hasAltitude(value):
+                score = 2 if (value%100)==0 or (value%1000)==0 \
+                    else 1 if (value%10)==0 else 0
+                if candidateAltitude is None or score>candidateScore:
+                    candidateAltitude = value
+                    candidateScore = score
+
+        return candidateAltitude        
+        
+    def _hasAltitude(self, altitude, ignorePath = None):
+        """Determine if the model already contains the given altitude
+        or not.
+        
+        ignorePath is a path in the model to ignore.
+
+        Returns the path of the element found, if any, or None, if the
+        altitude is not found."""        
+        model = self._fileListModel
+        iter = model.get_iter_first()
+        while iter is not None:
+            path = model.get_path(iter)
+            if path!=ignorePath and altitude==model[path][0]:
+                return path
+            iter = model.iter_next(iter)
+
+        return None
+
+    def _getNextValidUsualAltitude(self, startValue, descending):
+        """Get the next valid usual altitude."""
+        value = startValue
+        while value is not None and self._hasAltitude(value):
+            value = \
+                ApproachCalloutsEditor._getNextUsualAltitude(value, 
+                                                             descending)        
+
+        return value
+        
+    def _fileListButtonPressed(self, widget, event):
+        """Called when a mouse button is pressed on the file list."""
+        if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
+            return
+
+        menu = self._fileListPopupMenu
+        if pygobject:
+            menu.popup(None, None, None, None, event.button, event.time)
+        else:
+            menu.popup(None, None, None, event.button, event.time)
+
+    def _buildFileListPopupMenu(self):
+        """Build the file list popup menu."""
+        menu = gtk.Menu()
+
+        menuItem = gtk.MenuItem()
+        menuItem.set_label(xstr("callouts_remove"))
+        menuItem.set_use_underline(True)
+        menuItem.connect("activate", self._popupRemove)
+        menuItem.show()
+        self._popupRemoveItem = menuItem
+
+        menu.append(menuItem)
+
+        self._fileListPopupMenu = menu
+
+    def _popupRemove(self, menuItem):
+        """Remove the currently selected menu items."""
+        self._removeSelected()
+
+#------------------------------------------------------------------------------
Index: src/mlx/gui/callouts.py
===================================================================
--- src/mlx/gui/callouts.py	(revision 300)
+++ 	(revision )
@@ -1,503 +1,0 @@
-
-from common import *
-
-from mlx.i18n import xstr
-import mlx.const as const
-import mlx.config as config
-
-import os
-import re
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.callouts
-#
-# Editor dialog for approach callouts.
-#
-# The dialog consists of an aircraft type selector box at the top, and a table
-# with two buttons below it. The table contains the callout files with the
-# corresponding altitudes, and is sorted according to the altitude. When a new
-# file is added, the program finds out a new altitude for it. If the file's
-# name contains numbers that are not used as altitudes yet, the most suitable
-# of those numbers will be used. Otherwise a 'usual' altitude is searched for,
-# in the direction according to the sort order, and if that fails too, the
-# altitudes are tried one-by-one. See the
-# \ref ApproachCalloutsEditor._getNewAltitude function for more details.
-
-#------------------------------------------------------------------------------
-
-class ApproachCalloutsEditor(gtk.Dialog):
-    """The dialog to edit the approach callouts."""
-    integerRE = re.compile("[0-9]+")
-
-    # A list of "usual" altitudes for callouts
-    _usualAltitudes = [10, 20, 30, 40, 50, 100, 200, 300, 400, 500, 1000,
-                       1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000]
-
-    @staticmethod
-    def _getNextUsualAltitude(altitude, descending):
-        """Get the next altitude coming after the given one in the
-        given direction."""
-        if descending:
-            previous = None
-            for alt in ApproachCalloutsEditor._usualAltitudes:
-                if alt>=altitude: return previous
-                previous = alt
-        else:
-            for alt in ApproachCalloutsEditor._usualAltitudes:
-                if alt>altitude: return alt                
-                    
-        return None
-
-    def __init__(self, gui):
-        super(ApproachCalloutsEditor, self).__init__(WINDOW_TITLE_BASE + " - " +
-                                                     xstr("callouts_title"),
-                                                     gui.mainWindow,
-                                                     DIALOG_MODAL)
-
-        self.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
-        self.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
-
-        self._gui = gui
-        self._approachCallouts = {}
-        self._currentAircraftType = const.aircraftTypes[0]
-        self._fileOpenDialog = None
-
-        contentArea = self.get_content_area()
-
-        # FIXME: common code with the checklist editor
-        typeBox = gtk.HBox()
-
-        label = gtk.Label(xstr("callouts_aircraftType"))
-        label.set_use_underline(True)
-
-        typeBox.pack_start(label, False, False, 4)
-
-        self._aircraftTypeModel = gtk.ListStore(str, int)
-        for type in const.aircraftTypes:
-            name = aircraftNames[type] if type in aircraftNames \
-                   else "Aircraft type #%d" % (type,)
-            self._aircraftTypeModel.append([name, type])
-        self._aircraftType = gtk.ComboBox(model = self._aircraftTypeModel)
-        renderer = gtk.CellRendererText()
-        self._aircraftType.pack_start(renderer, True)
-        self._aircraftType.add_attribute(renderer, "text", 0)
-        self._aircraftType.set_tooltip_text(xstr("callouts_aircraftType_tooltip"))
-        self._aircraftType.set_active(0)
-        self._aircraftType.connect("changed", self._aircraftTypeChanged)
-        label.set_mnemonic_widget(self._aircraftType)
-
-        typeBox.pack_start(self._aircraftType, True, True, 4)
-
-        typeBoxAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                         xscale = 0.0, yscale = 0.0)
-        typeBoxAlignment.set_size_request(400, -1)
-        typeBoxAlignment.add(typeBox)
-
-        contentArea.pack_start(typeBoxAlignment, False, False, 12)
-        # FIXME: common code until here, but note that some texts are different
-
-        contentBox = gtk.HBox()        
-
-        controlBox = gtk.VBox()
-        controlAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                         xscale = 0.0, yscale = 0.0)
-        controlAlignment.set_padding(padding_top = 0, padding_bottom = 0,
-                                     padding_left = 32, padding_right = 32)
-        controlAlignment.add(controlBox)
-        contentBox.pack_start(controlAlignment, False, False, 0)
-
-        self._addButton = gtk.Button(xstr("callouts_add"))
-        self._addButton.set_use_underline(True)
-        self._addButton.set_tooltip_text(xstr("callouts_add_tooltip"))
-        self._addButton.connect("clicked", self._addButtonClicked)
-        addAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
-                                     xscale = 0.0, yscale = 0.0)
-        addAlignment.set_padding(padding_top = 24, padding_bottom = 0,
-                                 padding_left = 0, padding_right = 0)
-        addAlignment.add(self._addButton)
-        controlBox.pack_start(addAlignment, False, False, 0)
-
-        self._removeButton = gtk.Button(xstr("callouts_remove"))
-        self._removeButton.set_use_underline(True)
-        self._removeButton.set_tooltip_text(xstr("callouts_remove_tooltip"))
-        self._removeButton.set_sensitive(False)
-        self._removeButton.connect("clicked", self._removeButtonClicked)
-
-        removeAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
-                                        xscale = 0.0, yscale = 0.0)
-        removeAlignment.set_padding(padding_top = 24, padding_bottom = 0,
-                                    padding_left = 0, padding_right = 0)
-        removeAlignment.add(self._removeButton)
-        controlBox.pack_start(removeAlignment, False, False, 0)
-
-        self._fileListModel = gtk.ListStore(int, str, str)
-        self._fileListModel.set_sort_column_id(0, SORT_DESCENDING)
-
-        self._addingFile = False
-        self._fileListModel.connect("row-inserted", self._fileAdded)
-        self._lastAddedAltitude = None
-        
-        self._fileList = gtk.TreeView(model = self._fileListModel)
-
-        renderer = gtk.CellRendererSpin()
-        renderer.set_property("editable", True)
-
-        adjustment = gtk.Adjustment(0, 0, 5000, 10, 100)
-        renderer.set_property("adjustment", adjustment);
-        renderer.connect("edited", self._altitudeEdited)
-        
-        column = gtk.TreeViewColumn(xstr("callouts_header_altitude"),
-                                    renderer, text = 0)
-        self._fileList.append_column(column)
-        column.set_expand(True)
-        column.set_clickable(True)
-        column.set_reorderable(False)
-        column.set_sort_indicator(True)
-        column.set_sort_column_id(0)
-        column.set_sort_order(SORT_DESCENDING)
-        column.set_expand(False)
-
-        column = gtk.TreeViewColumn(xstr("callouts_header_path"),
-                                    gtk.CellRendererText(), text = 1)
-        self._fileList.append_column(column)
-        column.set_expand(True)
-        column.set_clickable(False)
-        column.set_reorderable(False)
-        column.set_expand(True)
-        
-        self._fileList.set_tooltip_column(2)
-        self._fileList.set_size_request(300, -1)
-        self._fileList.set_reorderable(False)
-        self._fileList.connect("button-press-event",
-                               self._fileListButtonPressed)
-        selection = self._fileList.get_selection()
-        selection.set_mode(SELECTION_MULTIPLE)
-        selection.connect("changed", self._fileListSelectionChanged)
-
-        self._buildFileListPopupMenu()
-
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.add(self._fileList)
-        scrolledWindow.set_size_request(300, -1)
-        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-        scrolledWindow.set_shadow_type(SHADOW_IN)
-        
-        fileListAlignment = gtk.Alignment(xscale=1.0, yscale=1.0, 
-                                          xalign=0.5, yalign=0.5)
-        fileListAlignment.set_padding(padding_top = 0, padding_bottom = 16,
-                                      padding_left = 0, padding_right = 8)
-        fileListAlignment.add(scrolledWindow)
-        
-        contentBox.pack_start(fileListAlignment, False, False, 4)
-
-        contentArea.pack_start(contentBox, True, True, 4)
-
-        self.set_size_request(-1, 300)
-
-    def run(self):
-        """Run the approach callouts editor dialog."""
-        self._approachCallouts = {}
-        self._displayCurrentApproachCallouts()
-        self.show_all()
-        response = super(ApproachCalloutsEditor, self).run()
-        self.hide()
-
-        if response==RESPONSETYPE_ACCEPT:
-            self._saveApproachCallouts()
-            config = self._gui.config
-            for (aircraftType, approachCallouts) in \
-                    self._approachCallouts.iteritems():
-                config.setApproachCallouts(aircraftType, approachCallouts)
-            config.save()
-
-    def _aircraftTypeChanged(self, comboBox):
-        """Called when the aircraft's type has changed."""        
-        self._saveApproachCallouts()
-        self._displayCurrentApproachCallouts()
-        
-    def _addButtonClicked(self, button):
-        """Called when the Add button is clicked."""
-        dialog = self._getFileOpenDialog()
-        
-        dialog.show_all()
-        result = dialog.run()
-        dialog.hide()
-
-        if result==RESPONSETYPE_OK:
-            filePath = dialog.get_filename()
-            baseName = os.path.basename(filePath)
-            altitude = self._getNewAltitude(baseName)
-            self._addingFile = True
-            self._lastAddedAltitude = altitude
-            self._fileListModel.append([altitude, baseName, filePath])
-            self._addingFile = False
-
-    def _fileAdded(self, model, path, iter):
-        """Called when a file is added to the list of callouts.
-
-        Makes the treeview to edit the altitude in the given row."""
-        if self._addingFile:
-            gobject.idle_add(self._selectFile)
-            self._fileList.grab_focus()
-            self.grab_focus()
-
-    def _selectFile(self):
-        """Select the file with the last added altitude."""
-        if self._lastAddedAltitude is None: return
-
-        model = self._fileListModel
-        iter = model.get_iter_first()
-        while iter is not None:
-            if model.get_value(iter, 0)==self._lastAddedAltitude: break
-            iter = model.iter_next(iter)
-        if iter is not None:
-            self._fileList.set_cursor(model.get_path(iter),
-                                      self._fileList.get_column(0), True)
-        self._lastAddedAltitude = None
-                             
-    def _removeButtonClicked(self, button):
-        """Called when the Remove button is clicked."""
-        self._removeSelected()
-        
-    def _removeSelected(self):
-        """Remove the selected files."""
-        selection = self._fileList.get_selection()
-        (model, paths) = selection.get_selected_rows()
-
-        iters = [model.get_iter(path) for path in paths]
-
-        for i in iters:
-            if i is not None:
-                model.remove(i)
-        
-    def _fileListSelectionChanged(self, selection):
-        """Called when the selection in the file list changes."""
-        anySelected = selection.count_selected_rows()>0
-        self._removeButton.set_sensitive(anySelected)
-
-    def _getAircraftType(self):
-        """Get the currently selected aircraft type."""
-        # FIXME: the same code as in the checklist editor
-        index = self._aircraftType.get_active()
-        return self._aircraftTypeModel[index][1]        
-
-    def _altitudeEdited(self, widget, path, value):
-        """Called when an altitude is edited"""
-        newAltitude = int(value)
-
-        model = self._fileListModel
-        editedIter = model.get_iter_from_string(path)
-        editedPath = model.get_path(editedIter)
-        otherPath = self._hasAltitude(newAltitude, ignorePath = editedPath)
-        if otherPath is not None:
-            dialog = gtk.MessageDialog(parent = self,
-                                       type = MESSAGETYPE_QUESTION,
-                                       message_format =
-                                       xstr("callouts_altitude_clash"))
-            dialog.format_secondary_markup(xstr("callouts_altitude_clash_sec"))
-            dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
-            dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
-            dialog.set_title(WINDOW_TITLE_BASE)
-
-            result = dialog.run()
-            dialog.hide()
-
-            if result!=RESPONSETYPE_YES:
-                newAltitude = None
-                
-        if newAltitude is not None:
-            model[editedPath][0] = newAltitude
-            if otherPath is not None:
-                model.remove(model.get_iter(otherPath))
-
-    def _saveApproachCallouts(self):
-        """Save the currently displayed list of approach callouts for the
-        previously displayed aircraft type."""
-        mapping = {}
-        model = self._fileListModel
-        iter = model.get_iter_first()
-        while iter is not None:
-            altitude = int(model.get(iter, 0)[0])
-            path = model.get(iter, 2)[0]
-            mapping[altitude] = path
-            iter = model.iter_next(iter)
-
-        self._approachCallouts[self._currentAircraftType] = \
-            config.ApproachCallouts(mapping)
-        
-    def _displayCurrentApproachCallouts(self):
-        """Display the approach callouts for the currently selected aircraft
-        type."""
-        aircraftType = self._getAircraftType()
-        self._currentAircraftType = aircraftType
-        if aircraftType not in self._approachCallouts:
-            self._approachCallouts[aircraftType] = \
-                self._gui.config.getApproachCallouts(aircraftType).clone()
-        approachCallouts = self._approachCallouts[aircraftType]
-
-        self._fileListModel.clear()
-        for (altitude, path) in approachCallouts:
-            self._fileListModel.append([altitude, os.path.basename(path), path])        
-
-    def _getFileOpenDialog(self):
-        """Get the dialog to open a file.
-
-        If it does not exist yet, it will be created."""
-        if self._fileOpenDialog is None:
-            dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
-                                           xstr("callouts_open_title"),
-                                           action = FILE_CHOOSER_ACTION_OPEN,
-                                           buttons = (gtk.STOCK_CANCEL,
-                                                      RESPONSETYPE_CANCEL,
-                                                      gtk.STOCK_OK, RESPONSETYPE_OK),
-                                           parent = self)
-            dialog.set_modal(True)            
-            dialog.set_do_overwrite_confirmation(True)
-      
-            # FIXME: create the filters in one location and use them
-            # from there
-            filter = gtk.FileFilter()
-            filter.set_name(xstr("file_filter_audio"))
-            filter.add_pattern("*.wav")
-            filter.add_pattern("*.mp3")
-            dialog.add_filter(filter)
-
-            filter = gtk.FileFilter()
-            filter.set_name(xstr("file_filter_all"))
-            filter.add_pattern("*.*")
-            dialog.add_filter(filter)
-            
-            self._fileOpenDialog = dialog
-
-        return self._fileOpenDialog
-    
-    def _getNewAltitude(self, baseName):
-        """Get a new, unique altitude for the audio file with the given
-        base name.
-
-        First the given file name is searched for suitable
-        numbers. Otherwise the smallest altitude in the model is
-        considered, and, depending on the actual ordering of the
-        table, a suitable smaller or greater value is found. It is
-        ensured that the number is unique, unless all numbers are
-        taken.
-        
-        If there is no entry in the table yet, 2500 is returned if the
-        table is sorted descending, 10 otherwise."""
-        altitude = self._getNewAltitudeFromFileName(baseName)
-        if altitude is not None: return altitude
-
-        descending = self._fileList.get_column(0).get_sort_order()==SORT_DESCENDING
-        model = self._fileListModel
-        numEntries = model.iter_n_children(None)
-        if numEntries==0:
-            return 2500 if descending else 10
-        else:
-            selection = self._fileList.get_selection()
-            (_model, paths) = selection.get_selected_rows()
-
-            if paths:
-                startIter = model.get_iter(max(paths))
-            else:
-                startIter = model.iter_nth_child(None, numEntries-1)
-
-            startValue = model.get_value(startIter, 0)
-
-            altitude = self._getNextValidUsualAltitude(startValue, descending)
-            if altitude is None:
-                altitude = self._getNextValidUsualAltitude(startValue,
-                                                           not descending)
-
-            if altitude is None:
-                for altitude in range(0 if descending else 4999,
-                                      4999 if descending else 0,
-                                      1 if descending else -1):
-                    if not self._hasAltitude(altitude): break
-                                   
-            return altitude
-
-    def _getNewAltitudeFromFileName(self, baseName):
-        """Get a new altitude value from the given file name.
-
-        The name is traversed for numbers. If a number is less than
-        5000 and there is no such altitude yet in the table, it is
-        checked if it is divisible by 100 or 1000, and if so, it gets
-        a score of 2. If it is divisible by 10, the score will be 1,
-        otherwise 0. The first highest scoring number is returned, if
-        there are any at all, otherwise None."""
-        candidateAltitude = None
-        candidateScore = None
-        
-        (baseName, _) = os.path.splitext(baseName)
-        numbers = ApproachCalloutsEditor.integerRE.findall(baseName)
-        for number in numbers:
-            value = int(number)
-            if value<5000 and not self._hasAltitude(value):
-                score = 2 if (value%100)==0 or (value%1000)==0 \
-                    else 1 if (value%10)==0 else 0
-                if candidateAltitude is None or score>candidateScore:
-                    candidateAltitude = value
-                    candidateScore = score
-
-        return candidateAltitude        
-        
-    def _hasAltitude(self, altitude, ignorePath = None):
-        """Determine if the model already contains the given altitude
-        or not.
-        
-        ignorePath is a path in the model to ignore.
-
-        Returns the path of the element found, if any, or None, if the
-        altitude is not found."""        
-        model = self._fileListModel
-        iter = model.get_iter_first()
-        while iter is not None:
-            path = model.get_path(iter)
-            if path!=ignorePath and altitude==model[path][0]:
-                return path
-            iter = model.iter_next(iter)
-
-        return None
-
-    def _getNextValidUsualAltitude(self, startValue, descending):
-        """Get the next valid usual altitude."""
-        value = startValue
-        while value is not None and self._hasAltitude(value):
-            value = \
-                ApproachCalloutsEditor._getNextUsualAltitude(value, 
-                                                             descending)        
-
-        return value
-        
-    def _fileListButtonPressed(self, widget, event):
-        """Called when a mouse button is pressed on the file list."""
-        if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
-            return
-
-        menu = self._fileListPopupMenu
-        if pygobject:
-            menu.popup(None, None, None, None, event.button, event.time)
-        else:
-            menu.popup(None, None, None, event.button, event.time)
-
-    def _buildFileListPopupMenu(self):
-        """Build the file list popup menu."""
-        menu = gtk.Menu()
-
-        menuItem = gtk.MenuItem()
-        menuItem.set_label(xstr("callouts_remove"))
-        menuItem.set_use_underline(True)
-        menuItem.connect("activate", self._popupRemove)
-        menuItem.show()
-        self._popupRemoveItem = menuItem
-
-        menu.append(menuItem)
-
-        self._fileListPopupMenu = menu
-
-    def _popupRemove(self, menuItem):
-        """Remove the currently selected menu items."""
-        self._removeSelected()
-
-#------------------------------------------------------------------------------
Index: src/mlx/gui/cef.py
===================================================================
--- src/mlx/gui/cef.py	(revision 805)
+++ src/mlx/gui/cef.py	(revision 805)
@@ -0,0 +1,431 @@
+from common import *
+
+from mlx.util import secondaryInstallation
+
+from cefpython3 import cefpython
+
+import platform
+import json
+import time
+import os
+import re
+import thread
+import threading
+import tempfile
+import traceback
+import urllib2
+from lxml import etree
+from StringIO import StringIO
+import lxml.html
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.cef
+#
+# Some helper stuff related to the Chrome Embedded Framework
+
+#------------------------------------------------------------------------------
+
+# Indicate if we should quit
+_toQuit = False
+
+# The SimBrief handler
+_simBriefHandler = None
+
+#------------------------------------------------------------------------------
+
+SIMBRIEF_PROGRESS_SEARCHING_BROWSER = 1
+SIMBRIEF_PROGRESS_LOADING_FORM = 2
+SIMBRIEF_PROGRESS_FILLING_FORM = 3
+SIMBRIEF_PROGRESS_WAITING_LOGIN = 4
+SIMBRIEF_PROGRESS_LOGGING_IN = 5
+SIMBRIEF_PROGRESS_WAITING_RESULT = 6
+
+SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING = 7
+SIMBRIEF_PROGRESS_DONE = 1000
+
+SIMBRIEF_RESULT_NONE = 0
+SIMBRIEF_RESULT_OK = 1
+SIMBRIEF_RESULT_ERROR_OTHER = 2
+SIMBRIEF_RESULT_ERROR_NO_FORM = 11
+SIMBRIEF_RESULT_ERROR_NO_POPUP = 12
+SIMBRIEF_RESULT_ERROR_LOGIN_FAILED = 13
+
+#------------------------------------------------------------------------------
+
+class SimBriefHandler(object):
+    """An object to store the state of a SimBrief query."""
+    _formURL = "http://flare.privatedns.org/mava_simbrief/simbrief_form.html"
+
+    _querySettings = {
+        'navlog': True,
+        'etops': True,
+        'stepclimbs': True,
+        'tlr': True,
+        'notams': True,
+        'firnot': True,
+        'maps': 'Simple',
+        };
+
+
+    def __init__(self):
+        """Construct the handler."""
+        self._browser = None
+        self._plan = None
+        self._getCredentials = None
+        self._getCredentialsCount = 0
+        self._updateProgressFn = None
+        self._htmlFilePath = None
+        self._lastProgress = SIMBRIEF_PROGRESS_SEARCHING_BROWSER
+        self._timeoutID = None
+
+    def initialize(self):
+        """Create and initialize the browser used for Simbrief."""
+        windowInfo = cefpython.WindowInfo()
+        windowInfo.SetAsOffscreen(int(0))
+
+        self._browser = \
+          cefpython.CreateBrowserSync(windowInfo, browserSettings = {},
+                                      navigateUrl = SimBriefHandler._formURL)
+        self._browser.SetClientHandler(OffscreenRenderHandler())
+        self._browser.SetFocus(True)
+        self._browser.SetClientCallback("OnLoadEnd", self._onLoadEnd)
+
+        bindings = cefpython.JavascriptBindings(bindToFrames=True,
+                                                bindToPopups=True)
+        bindings.SetFunction("briefingData", self._briefingDataAvailable)
+        bindings.SetFunction("formFilled", self._formFilled)
+        self._browser.SetJavascriptBindings(bindings)
+
+    def call(self, plan, getCredentials, updateProgress, htmlFilePath):
+        """Call SimBrief with the given plan."""
+        self._timeoutID = gobject.timeout_add(120*1000, self._timedOut)
+
+        self._plan = plan
+        self._getCredentials = getCredentials
+        self._getCredentialsCount = 0
+        self._updateProgressFn = updateProgress
+        self._htmlFilePath = htmlFilePath
+
+        self._browser.LoadUrl(SimBriefHandler._formURL)
+        self._updateProgress(SIMBRIEF_PROGRESS_LOADING_FORM,
+                             SIMBRIEF_RESULT_NONE, None)
+
+    def _onLoadEnd(self, browser, frame, httpCode):
+        """Called when a page has been loaded in the SimBrief browser."""
+        url = frame.GetUrl()
+        print "gui.cef.SimBriefHandler._onLoadEnd", httpCode, url
+        if httpCode>=300:
+            self._updateProgress(self._lastProgress,
+                                 SIMBRIEF_RESULT_ERROR_OTHER, None)
+        elif url.startswith("http://flare.privatedns.org/mava_simbrief/simbrief_form.html"):
+            if self._plan is None:
+                return
+
+            self._updateProgress(SIMBRIEF_PROGRESS_FILLING_FORM,
+                                 SIMBRIEF_RESULT_NONE, None)
+
+            js = "form=document.getElementById(\"sbapiform\");"
+            for (name, value) in self._plan.iteritems():
+                js += "form." + name + ".value=\"" + value + "\";"
+            for (name, value) in SimBriefHandler._querySettings.iteritems():
+                if isinstance(value, bool):
+                    js += "form." + name + ".checked=" + \
+                      ("true" if value else "false") + ";"
+                elif isinstance(value, str):
+                    js += "form." + name + ".value=\"" + value + "\";"
+
+            js += "form.submitform.click();"
+            js += "window.formFilled();"
+
+            frame.ExecuteJavascript(js)
+        elif url.startswith("http://www.simbrief.com/system/login.api.php"):
+            (user, password) = self._getCredentials(self._getCredentialsCount)
+            if user is None or password is None:
+                self._updateProgress(SIMBRIEF_PROGRESS_WAITING_LOGIN,
+                                     SIMBRIEF_RESULT_ERROR_LOGIN_FAILED, None)
+                return
+
+            self._getCredentialsCount += 1
+            js = "form=document.forms[0];"
+            js += "form.user.value=\"" + user + "\";"
+            js += "form.pass.value=\"" + password + "\";"
+            js += "form.submit();"
+            frame.ExecuteJavascript(js)
+        elif url.startswith("http://www.simbrief.com/ofp/ofp.loader.api.php"):
+            self._updateProgress(SIMBRIEF_PROGRESS_WAITING_RESULT,
+                                 SIMBRIEF_RESULT_NONE, None)
+        elif url.startswith("http://flare.privatedns.org/mava_simbrief/simbrief_briefing.php"):
+            js = "form=document.getElementById(\"hiddenform\");"
+            js += "window.briefingData(form.hidden_is_briefing_available.value, form.hidden_link.value);";
+            frame.ExecuteJavascript(js)
+
+    def _formFilled(self):
+        """Called when the form has been filled and submitted."""
+        self._updateProgress(SIMBRIEF_PROGRESS_WAITING_LOGIN,
+                             SIMBRIEF_RESULT_NONE, None)
+
+    def _briefingDataAvailable(self, available, link):
+        """Called when the briefing data is available."""
+        if available:
+            link ="http://www.simbrief.com/ofp/flightplans/xml/" + link + ".xml"
+
+            self._updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
+                                 SIMBRIEF_RESULT_NONE, None)
+
+            thread = threading.Thread(target = self._getResults, args = (link,))
+            thread.daemon = True
+            thread.start()
+        else:
+            self._updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
+                                 SIMBRIEF_RESULT_ERROR_OTHER, None)
+
+    def _resultsAvailable(self, flightInfo):
+        """Called from the result retrieval thread when the result is
+        available.
+
+        It checks for the plan being not None, as we may time out."""
+        if self._plan is not None:
+            self._updateProgress(SIMBRIEF_PROGRESS_DONE,
+                                 SIMBRIEF_RESULT_OK, flightInfo)
+
+    def _updateProgress(self, progress, results, flightInfo):
+        """Update the progress."""
+        self._lastProgress = progress
+        if results!=SIMBRIEF_RESULT_NONE:
+            gobject.source_remove(self._timeoutID)
+            self._plan = None
+
+        self._updateProgressFn(progress, results, flightInfo)
+
+    def _timedOut(self):
+        """Called when the timeout occurs."""
+        if self._lastProgress==SIMBRIEF_PROGRESS_LOADING_FORM:
+            result = SIMBRIEF_RESULT_ERROR_NO_FORM
+        elif self._lastProgress==SIMBRIEF_PROGRESS_WAITING_LOGIN:
+            result = SIMBRIEF_RESULT_ERROR_NO_POPUP
+        else:
+            result = SIMBRIEF_RESULT_ERROR_OTHER
+
+        self._updateProgress(self._lastProgress, result, None)
+
+        return False
+
+    def _getResults(self, link):
+        """Get the result from the given link."""
+        availableInfo = {}
+        ## Holds analysis data to be used
+        flightInfo = {}
+
+        # Obtaining the xml
+        response = urllib2.urlopen(link)
+        xmlContent = response.read()
+        # Processing xml
+        content = etree.iterparse(StringIO(xmlContent))
+
+        for (action, element) in content:
+            # Processing tags that occur multiple times
+            if element.tag == "weather":
+                weatherElementList = list(element)
+                for weatherElement in weatherElementList:
+                    flightInfo[weatherElement.tag] = weatherElement.text
+            else:
+                availableInfo[element.tag] = element.text
+
+        # Processing plan_html
+        ## Obtaining chart links
+        imageLinks = []
+        for imageLinkElement in lxml.html.find_class(availableInfo["plan_html"],
+                                                     "ofpmaplink"):
+            for imageLink in imageLinkElement.iterlinks():
+                if imageLink[1] == 'src':
+                    imageLinks.append(imageLink[2])
+        flightInfo["image_links"] = imageLinks
+        print(sorted(availableInfo.keys()))
+        htmlFilePath = "simbrief_plan.html" if self._htmlFilePath is None \
+          else self._htmlFilePath
+        with open(htmlFilePath, 'w') as f:
+            f.write(availableInfo["plan_html"])
+
+        gobject.idle_add(self._resultsAvailable, flightInfo)
+
+#------------------------------------------------------------------------------
+
+def initialize(initializedCallback):
+    """Initialize the Chrome Embedded Framework."""
+    global _toQuit, _simBriefHandler
+    _toQuit = False
+
+    gobject.threads_init()
+
+    _simBriefHandler = SimBriefHandler()
+    _initializeCEF([], initializedCallback)
+
+#------------------------------------------------------------------------------
+
+def _initializeCEF(args, initializedCallback):
+    """Perform the actual initialization of CEF using the given arguments."""
+    print "Initializing CEF with args:", args
+
+    settings = {
+        "debug": True, # cefpython debug messages in console and in log_file
+        "log_severity": cefpython.LOGSEVERITY_VERBOSE, # LOGSEVERITY_VERBOSE
+        "log_file": "", # Set to "" to disable
+        "release_dcheck_enabled": True, # Enable only when debugging
+        # This directories must be set on Linux
+        "locales_dir_path": os.path.join(cefpython.GetModuleDirectory(), "locales"),
+        "resources_dir_path": cefpython.GetModuleDirectory(),
+        "browser_subprocess_path": "%s/%s" % \
+            (cefpython.GetModuleDirectory(), "subprocess"),
+    }
+
+    switches={}
+    for arg in args:
+        if arg.startswith("--"):
+            if arg != "--enable-logging":
+                assignIndex = arg.find("=")
+                if assignIndex<0:
+                    switches[arg[2:]] = ""
+                else:
+                    switches[arg[2:assignIndex]] = arg[assignIndex+1:]
+        else:
+            print "Unhandled switch", arg
+
+    cefpython.Initialize(settings, switches)
+
+    gobject.timeout_add(10, _handleTimeout)
+
+    print "Initialized, executing callback..."
+    initializedCallback()
+
+#------------------------------------------------------------------------------
+
+def getContainer():
+    """Get a container object suitable for running a browser instance
+    within."""
+    if os.name=="nt":
+        container = gtk.DrawingArea()
+        container.set_property("can-focus", True)
+        container.connect("size-allocate", _handleSizeAllocate)
+    else:
+        container = gtk.VBox(True, 0)
+
+    container.show()
+
+    return container
+
+#------------------------------------------------------------------------------
+
+def startInContainer(container, url, browserSettings = {}):
+    """Start a browser instance in the given container with the given URL."""
+    if os.name=="nt":
+        window = container.get_window()
+        if window is None:
+            print "mlx.gui.cef.startInContainer: no window found!"
+            windowID = None
+        else:
+            windowID = window.handle
+    else:
+        m = re.search("GtkVBox at 0x(\w+)", str(container))
+        hexID = m.group(1)
+        windowID = int(hexID, 16)
+
+    windowInfo = cefpython.WindowInfo()
+    if windowID is not None:
+        windowInfo.SetAsChild(windowID)
+
+    return cefpython.CreateBrowserSync(windowInfo,
+                                       browserSettings = browserSettings,
+                                       navigateUrl = url)
+
+#------------------------------------------------------------------------------
+
+class OffscreenRenderHandler(object):
+    def GetRootScreenRect(self, browser, rect):
+        #print "GetRootScreenRect"
+        rect += [0, 0, 800, 600]
+        return True
+
+    def GetViewRect(self, browser, rect):
+        #print "GetViewRect"
+        rect += [0, 0, 800, 600]
+        return True
+
+    def GetScreenPoint(self, browser, viewX, viewY, screenCoordinates):
+        #print "GetScreenPoint", viewX, viewY
+        rect += [viewX, viewY]
+        return True
+
+    def GetScreenInfo(self, browser, screenInfo):
+        #print "GetScreenInfo"
+        pass
+
+    def OnPopupShow(self, browser, show):
+        #print "OnPopupShow", show
+        pass
+
+    def OnPopupSize(self, browser, rect):
+        #print "OnPopupSize", rect
+        pass
+
+    def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height):
+        #print "OnPaint", paintElementType, dirtyRects, buffer, width, height
+        pass
+
+    def OnCursorChange(self, browser, cursor):
+        #print "OnCursorChange", cursor
+        pass
+
+    def OnScrollOffsetChanged(self, browser):
+        #print "OnScrollOffsetChange"
+        pass
+
+    def OnBeforePopup(self, browser, frame, targetURL, targetFrameName,
+                      popupFeatures, windowInfo, client, browserSettings,
+                      noJavascriptAccess):
+        wInfo = cefpython.WindowInfo()
+        wInfo.SetAsOffscreen(int(0))
+
+        windowInfo.append(wInfo)
+
+        return False
+
+#------------------------------------------------------------------------------
+
+def initializeSimBrief():
+    """Initialize the (hidden) browser window for SimBrief."""
+    _simBriefHandler.initialize()
+
+#------------------------------------------------------------------------------
+
+def callSimBrief(plan, getCredentials, updateProgress, htmlFilePath):
+    """Call SimBrief with the given plan.
+
+    The callbacks will be called in the main GUI thread."""
+    _simBriefHandler.call(plan, getCredentials, updateProgress, htmlFilePath)
+
+#------------------------------------------------------------------------------
+
+def finalize():
+    """Finalize the Chrome Embedded Framework."""
+    global _toQuit
+    _toQuit = True
+    cefpython.Shutdown()
+
+#------------------------------------------------------------------------------
+
+def _handleTimeout():
+    """Handle the timeout by running the CEF message loop."""
+    if _toQuit:
+        return False
+    else:
+        cefpython.MessageLoopWork()
+        return True
+
+#------------------------------------------------------------------------------
+
+def _handleSizeAllocate(widget, sizeAlloc):
+    """Handle the size-allocate event."""
+    window = widget.get_window()
+    if widget is not None:
+        cefpython.WindowUtils.OnSize(window.handle, 0, 0, 0)
Index: src/mlx/gui/cef.py
===================================================================
--- src/mlx/gui/cef.py	(revision 805)
+++ 	(revision )
@@ -1,431 +1,0 @@
-from common import *
-
-from mlx.util import secondaryInstallation
-
-from cefpython3 import cefpython
-
-import platform
-import json
-import time
-import os
-import re
-import thread
-import threading
-import tempfile
-import traceback
-import urllib2
-from lxml import etree
-from StringIO import StringIO
-import lxml.html
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.cef
-#
-# Some helper stuff related to the Chrome Embedded Framework
-
-#------------------------------------------------------------------------------
-
-# Indicate if we should quit
-_toQuit = False
-
-# The SimBrief handler
-_simBriefHandler = None
-
-#------------------------------------------------------------------------------
-
-SIMBRIEF_PROGRESS_SEARCHING_BROWSER = 1
-SIMBRIEF_PROGRESS_LOADING_FORM = 2
-SIMBRIEF_PROGRESS_FILLING_FORM = 3
-SIMBRIEF_PROGRESS_WAITING_LOGIN = 4
-SIMBRIEF_PROGRESS_LOGGING_IN = 5
-SIMBRIEF_PROGRESS_WAITING_RESULT = 6
-
-SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING = 7
-SIMBRIEF_PROGRESS_DONE = 1000
-
-SIMBRIEF_RESULT_NONE = 0
-SIMBRIEF_RESULT_OK = 1
-SIMBRIEF_RESULT_ERROR_OTHER = 2
-SIMBRIEF_RESULT_ERROR_NO_FORM = 11
-SIMBRIEF_RESULT_ERROR_NO_POPUP = 12
-SIMBRIEF_RESULT_ERROR_LOGIN_FAILED = 13
-
-#------------------------------------------------------------------------------
-
-class SimBriefHandler(object):
-    """An object to store the state of a SimBrief query."""
-    _formURL = "http://flare.privatedns.org/mava_simbrief/simbrief_form.html"
-
-    _querySettings = {
-        'navlog': True,
-        'etops': True,
-        'stepclimbs': True,
-        'tlr': True,
-        'notams': True,
-        'firnot': True,
-        'maps': 'Simple',
-        };
-
-
-    def __init__(self):
-        """Construct the handler."""
-        self._browser = None
-        self._plan = None
-        self._getCredentials = None
-        self._getCredentialsCount = 0
-        self._updateProgressFn = None
-        self._htmlFilePath = None
-        self._lastProgress = SIMBRIEF_PROGRESS_SEARCHING_BROWSER
-        self._timeoutID = None
-
-    def initialize(self):
-        """Create and initialize the browser used for Simbrief."""
-        windowInfo = cefpython.WindowInfo()
-        windowInfo.SetAsOffscreen(int(0))
-
-        self._browser = \
-          cefpython.CreateBrowserSync(windowInfo, browserSettings = {},
-                                      navigateUrl = SimBriefHandler._formURL)
-        self._browser.SetClientHandler(OffscreenRenderHandler())
-        self._browser.SetFocus(True)
-        self._browser.SetClientCallback("OnLoadEnd", self._onLoadEnd)
-
-        bindings = cefpython.JavascriptBindings(bindToFrames=True,
-                                                bindToPopups=True)
-        bindings.SetFunction("briefingData", self._briefingDataAvailable)
-        bindings.SetFunction("formFilled", self._formFilled)
-        self._browser.SetJavascriptBindings(bindings)
-
-    def call(self, plan, getCredentials, updateProgress, htmlFilePath):
-        """Call SimBrief with the given plan."""
-        self._timeoutID = gobject.timeout_add(120*1000, self._timedOut)
-
-        self._plan = plan
-        self._getCredentials = getCredentials
-        self._getCredentialsCount = 0
-        self._updateProgressFn = updateProgress
-        self._htmlFilePath = htmlFilePath
-
-        self._browser.LoadUrl(SimBriefHandler._formURL)
-        self._updateProgress(SIMBRIEF_PROGRESS_LOADING_FORM,
-                             SIMBRIEF_RESULT_NONE, None)
-
-    def _onLoadEnd(self, browser, frame, httpCode):
-        """Called when a page has been loaded in the SimBrief browser."""
-        url = frame.GetUrl()
-        print "gui.cef.SimBriefHandler._onLoadEnd", httpCode, url
-        if httpCode>=300:
-            self._updateProgress(self._lastProgress,
-                                 SIMBRIEF_RESULT_ERROR_OTHER, None)
-        elif url.startswith("http://flare.privatedns.org/mava_simbrief/simbrief_form.html"):
-            if self._plan is None:
-                return
-
-            self._updateProgress(SIMBRIEF_PROGRESS_FILLING_FORM,
-                                 SIMBRIEF_RESULT_NONE, None)
-
-            js = "form=document.getElementById(\"sbapiform\");"
-            for (name, value) in self._plan.iteritems():
-                js += "form." + name + ".value=\"" + value + "\";"
-            for (name, value) in SimBriefHandler._querySettings.iteritems():
-                if isinstance(value, bool):
-                    js += "form." + name + ".checked=" + \
-                      ("true" if value else "false") + ";"
-                elif isinstance(value, str):
-                    js += "form." + name + ".value=\"" + value + "\";"
-
-            js += "form.submitform.click();"
-            js += "window.formFilled();"
-
-            frame.ExecuteJavascript(js)
-        elif url.startswith("http://www.simbrief.com/system/login.api.php"):
-            (user, password) = self._getCredentials(self._getCredentialsCount)
-            if user is None or password is None:
-                self._updateProgress(SIMBRIEF_PROGRESS_WAITING_LOGIN,
-                                     SIMBRIEF_RESULT_ERROR_LOGIN_FAILED, None)
-                return
-
-            self._getCredentialsCount += 1
-            js = "form=document.forms[0];"
-            js += "form.user.value=\"" + user + "\";"
-            js += "form.pass.value=\"" + password + "\";"
-            js += "form.submit();"
-            frame.ExecuteJavascript(js)
-        elif url.startswith("http://www.simbrief.com/ofp/ofp.loader.api.php"):
-            self._updateProgress(SIMBRIEF_PROGRESS_WAITING_RESULT,
-                                 SIMBRIEF_RESULT_NONE, None)
-        elif url.startswith("http://flare.privatedns.org/mava_simbrief/simbrief_briefing.php"):
-            js = "form=document.getElementById(\"hiddenform\");"
-            js += "window.briefingData(form.hidden_is_briefing_available.value, form.hidden_link.value);";
-            frame.ExecuteJavascript(js)
-
-    def _formFilled(self):
-        """Called when the form has been filled and submitted."""
-        self._updateProgress(SIMBRIEF_PROGRESS_WAITING_LOGIN,
-                             SIMBRIEF_RESULT_NONE, None)
-
-    def _briefingDataAvailable(self, available, link):
-        """Called when the briefing data is available."""
-        if available:
-            link ="http://www.simbrief.com/ofp/flightplans/xml/" + link + ".xml"
-
-            self._updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
-                                 SIMBRIEF_RESULT_NONE, None)
-
-            thread = threading.Thread(target = self._getResults, args = (link,))
-            thread.daemon = True
-            thread.start()
-        else:
-            self._updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
-                                 SIMBRIEF_RESULT_ERROR_OTHER, None)
-
-    def _resultsAvailable(self, flightInfo):
-        """Called from the result retrieval thread when the result is
-        available.
-
-        It checks for the plan being not None, as we may time out."""
-        if self._plan is not None:
-            self._updateProgress(SIMBRIEF_PROGRESS_DONE,
-                                 SIMBRIEF_RESULT_OK, flightInfo)
-
-    def _updateProgress(self, progress, results, flightInfo):
-        """Update the progress."""
-        self._lastProgress = progress
-        if results!=SIMBRIEF_RESULT_NONE:
-            gobject.source_remove(self._timeoutID)
-            self._plan = None
-
-        self._updateProgressFn(progress, results, flightInfo)
-
-    def _timedOut(self):
-        """Called when the timeout occurs."""
-        if self._lastProgress==SIMBRIEF_PROGRESS_LOADING_FORM:
-            result = SIMBRIEF_RESULT_ERROR_NO_FORM
-        elif self._lastProgress==SIMBRIEF_PROGRESS_WAITING_LOGIN:
-            result = SIMBRIEF_RESULT_ERROR_NO_POPUP
-        else:
-            result = SIMBRIEF_RESULT_ERROR_OTHER
-
-        self._updateProgress(self._lastProgress, result, None)
-
-        return False
-
-    def _getResults(self, link):
-        """Get the result from the given link."""
-        availableInfo = {}
-        ## Holds analysis data to be used
-        flightInfo = {}
-
-        # Obtaining the xml
-        response = urllib2.urlopen(link)
-        xmlContent = response.read()
-        # Processing xml
-        content = etree.iterparse(StringIO(xmlContent))
-
-        for (action, element) in content:
-            # Processing tags that occur multiple times
-            if element.tag == "weather":
-                weatherElementList = list(element)
-                for weatherElement in weatherElementList:
-                    flightInfo[weatherElement.tag] = weatherElement.text
-            else:
-                availableInfo[element.tag] = element.text
-
-        # Processing plan_html
-        ## Obtaining chart links
-        imageLinks = []
-        for imageLinkElement in lxml.html.find_class(availableInfo["plan_html"],
-                                                     "ofpmaplink"):
-            for imageLink in imageLinkElement.iterlinks():
-                if imageLink[1] == 'src':
-                    imageLinks.append(imageLink[2])
-        flightInfo["image_links"] = imageLinks
-        print(sorted(availableInfo.keys()))
-        htmlFilePath = "simbrief_plan.html" if self._htmlFilePath is None \
-          else self._htmlFilePath
-        with open(htmlFilePath, 'w') as f:
-            f.write(availableInfo["plan_html"])
-
-        gobject.idle_add(self._resultsAvailable, flightInfo)
-
-#------------------------------------------------------------------------------
-
-def initialize(initializedCallback):
-    """Initialize the Chrome Embedded Framework."""
-    global _toQuit, _simBriefHandler
-    _toQuit = False
-
-    gobject.threads_init()
-
-    _simBriefHandler = SimBriefHandler()
-    _initializeCEF([], initializedCallback)
-
-#------------------------------------------------------------------------------
-
-def _initializeCEF(args, initializedCallback):
-    """Perform the actual initialization of CEF using the given arguments."""
-    print "Initializing CEF with args:", args
-
-    settings = {
-        "debug": True, # cefpython debug messages in console and in log_file
-        "log_severity": cefpython.LOGSEVERITY_VERBOSE, # LOGSEVERITY_VERBOSE
-        "log_file": "", # Set to "" to disable
-        "release_dcheck_enabled": True, # Enable only when debugging
-        # This directories must be set on Linux
-        "locales_dir_path": os.path.join(cefpython.GetModuleDirectory(), "locales"),
-        "resources_dir_path": cefpython.GetModuleDirectory(),
-        "browser_subprocess_path": "%s/%s" % \
-            (cefpython.GetModuleDirectory(), "subprocess"),
-    }
-
-    switches={}
-    for arg in args:
-        if arg.startswith("--"):
-            if arg != "--enable-logging":
-                assignIndex = arg.find("=")
-                if assignIndex<0:
-                    switches[arg[2:]] = ""
-                else:
-                    switches[arg[2:assignIndex]] = arg[assignIndex+1:]
-        else:
-            print "Unhandled switch", arg
-
-    cefpython.Initialize(settings, switches)
-
-    gobject.timeout_add(10, _handleTimeout)
-
-    print "Initialized, executing callback..."
-    initializedCallback()
-
-#------------------------------------------------------------------------------
-
-def getContainer():
-    """Get a container object suitable for running a browser instance
-    within."""
-    if os.name=="nt":
-        container = gtk.DrawingArea()
-        container.set_property("can-focus", True)
-        container.connect("size-allocate", _handleSizeAllocate)
-    else:
-        container = gtk.VBox(True, 0)
-
-    container.show()
-
-    return container
-
-#------------------------------------------------------------------------------
-
-def startInContainer(container, url, browserSettings = {}):
-    """Start a browser instance in the given container with the given URL."""
-    if os.name=="nt":
-        window = container.get_window()
-        if window is None:
-            print "mlx.gui.cef.startInContainer: no window found!"
-            windowID = None
-        else:
-            windowID = window.handle
-    else:
-        m = re.search("GtkVBox at 0x(\w+)", str(container))
-        hexID = m.group(1)
-        windowID = int(hexID, 16)
-
-    windowInfo = cefpython.WindowInfo()
-    if windowID is not None:
-        windowInfo.SetAsChild(windowID)
-
-    return cefpython.CreateBrowserSync(windowInfo,
-                                       browserSettings = browserSettings,
-                                       navigateUrl = url)
-
-#------------------------------------------------------------------------------
-
-class OffscreenRenderHandler(object):
-    def GetRootScreenRect(self, browser, rect):
-        #print "GetRootScreenRect"
-        rect += [0, 0, 800, 600]
-        return True
-
-    def GetViewRect(self, browser, rect):
-        #print "GetViewRect"
-        rect += [0, 0, 800, 600]
-        return True
-
-    def GetScreenPoint(self, browser, viewX, viewY, screenCoordinates):
-        #print "GetScreenPoint", viewX, viewY
-        rect += [viewX, viewY]
-        return True
-
-    def GetScreenInfo(self, browser, screenInfo):
-        #print "GetScreenInfo"
-        pass
-
-    def OnPopupShow(self, browser, show):
-        #print "OnPopupShow", show
-        pass
-
-    def OnPopupSize(self, browser, rect):
-        #print "OnPopupSize", rect
-        pass
-
-    def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height):
-        #print "OnPaint", paintElementType, dirtyRects, buffer, width, height
-        pass
-
-    def OnCursorChange(self, browser, cursor):
-        #print "OnCursorChange", cursor
-        pass
-
-    def OnScrollOffsetChanged(self, browser):
-        #print "OnScrollOffsetChange"
-        pass
-
-    def OnBeforePopup(self, browser, frame, targetURL, targetFrameName,
-                      popupFeatures, windowInfo, client, browserSettings,
-                      noJavascriptAccess):
-        wInfo = cefpython.WindowInfo()
-        wInfo.SetAsOffscreen(int(0))
-
-        windowInfo.append(wInfo)
-
-        return False
-
-#------------------------------------------------------------------------------
-
-def initializeSimBrief():
-    """Initialize the (hidden) browser window for SimBrief."""
-    _simBriefHandler.initialize()
-
-#------------------------------------------------------------------------------
-
-def callSimBrief(plan, getCredentials, updateProgress, htmlFilePath):
-    """Call SimBrief with the given plan.
-
-    The callbacks will be called in the main GUI thread."""
-    _simBriefHandler.call(plan, getCredentials, updateProgress, htmlFilePath)
-
-#------------------------------------------------------------------------------
-
-def finalize():
-    """Finalize the Chrome Embedded Framework."""
-    global _toQuit
-    _toQuit = True
-    cefpython.Shutdown()
-
-#------------------------------------------------------------------------------
-
-def _handleTimeout():
-    """Handle the timeout by running the CEF message loop."""
-    if _toQuit:
-        return False
-    else:
-        cefpython.MessageLoopWork()
-        return True
-
-#------------------------------------------------------------------------------
-
-def _handleSizeAllocate(widget, sizeAlloc):
-    """Handle the size-allocate event."""
-    window = widget.get_window()
-    if widget is not None:
-        cefpython.WindowUtils.OnSize(window.handle, 0, 0, 0)
Index: src/mlx/gui/checklist.py
===================================================================
--- src/mlx/gui/checklist.py	(revision 300)
+++ src/mlx/gui/checklist.py	(revision 300)
@@ -0,0 +1,380 @@
+
+from common import *
+
+from mlx.i18n import xstr
+import mlx.const as const
+import mlx.config as config
+
+import os
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.checklist
+#
+# The checklist editor dialog.
+#
+# This module implements the checklist editor dialog. A checklist is a list of
+# sound files that are played one-by-one when requested by the user. The top
+# part of the dialog contains an aircraft type selector. Below it, on the left
+# there is a file selector, and the list of the files in the checklist to the
+# right. There are some buttons in the middle to manipulate the checklist.
+
+#------------------------------------------------------------------------------
+
+class ChecklistEditor(gtk.Dialog):
+    """The dialog to edit the checklists."""
+    def __init__(self, gui):
+        super(ChecklistEditor, self).__init__(WINDOW_TITLE_BASE + " - " +
+                                              xstr("chklst_title"),
+                                              gui.mainWindow,
+                                              DIALOG_MODAL)
+
+        self.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
+        self.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
+
+        self._gui = gui
+        self._checklists = {}
+        self._currentAircraftType = const.aircraftTypes[0]
+
+        contentArea = self.get_content_area()
+
+        typeBox = gtk.HBox()
+
+        label = gtk.Label(xstr("chklst_aircraftType"))
+        label.set_use_underline(True)
+
+        typeBox.pack_start(label, False, False, 4)
+
+        self._aircraftTypeModel = gtk.ListStore(str, int)
+        for type in const.aircraftTypes:
+            name = aircraftNames[type] if type in aircraftNames \
+                   else "Aircraft type #%d" % (type,)
+            self._aircraftTypeModel.append([name, type])
+        self._aircraftType = gtk.ComboBox(model = self._aircraftTypeModel)
+        renderer = gtk.CellRendererText()
+        self._aircraftType.pack_start(renderer, True)
+        self._aircraftType.add_attribute(renderer, "text", 0)
+        self._aircraftType.set_tooltip_text(xstr("chklst_aircraftType_tooltip"))
+        self._aircraftType.set_active(0)
+        self._aircraftType.connect("changed", self._aircraftTypeChanged)
+        label.set_mnemonic_widget(self._aircraftType)
+
+        typeBox.pack_start(self._aircraftType, True, True, 4)
+
+        typeBoxAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                         xscale = 0.0, yscale = 0.0)
+        typeBoxAlignment.set_size_request(400, -1)
+        typeBoxAlignment.add(typeBox)
+
+        contentArea.pack_start(typeBoxAlignment, False, False, 12)
+
+        fileBox = gtk.HBox()        
+
+        self._fileChooser = gtk.FileChooserWidget()
+        self._fileChooser.set_select_multiple(True)
+        
+        filter = gtk.FileFilter()
+        filter.set_name(xstr("file_filter_audio"))
+        filter.add_pattern("*.wav")
+        filter.add_pattern("*.mp3")
+        self._fileChooser.add_filter(filter)
+            
+        filter = gtk.FileFilter()
+        filter.set_name(xstr("file_filter_all"))
+        filter.add_pattern("*.*")
+        self._fileChooser.add_filter(filter)
+
+        self._fileChooser.connect("selection-changed",
+                                  self._fileChooserSelectionChanged)
+
+        fileBox.pack_start(self._fileChooser, True, True, 4)
+
+        controlBox = gtk.VBox()
+        controlAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                         xscale = 0.0, yscale = 0.0)
+        controlAlignment.set_padding(padding_top = 0, padding_bottom = 0,
+                                     padding_left = 32, padding_right = 32)
+        controlAlignment.add(controlBox)
+        fileBox.pack_start(controlAlignment, False, False, 0)
+
+        self._addButton = gtk.Button(xstr("chklst_add"))
+        self._addButton.set_use_underline(True)
+        self._addButton.set_tooltip_text(xstr("chklst_add_tooltip"))
+        self._addButton.connect("clicked", self._addButtonClicked)
+        addAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
+                                     xscale = 0.0, yscale = 0.0)
+        addAlignment.set_padding(padding_top = 64, padding_bottom = 0,
+                                 padding_left = 0, padding_right = 0)
+        addAlignment.add(self._addButton)
+        controlBox.pack_start(addAlignment, False, False, 0)
+
+        self._removeButton = gtk.Button(xstr("chklst_remove"))
+        self._removeButton.set_use_underline(True)
+        self._removeButton.set_tooltip_text(xstr("chklst_remove_tooltip"))
+        self._removeButton.set_sensitive(False)
+        self._removeButton.connect("clicked", self._removeButtonClicked)
+
+        removeAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
+                                        xscale = 0.0, yscale = 0.0)
+        removeAlignment.set_padding(padding_top = 64, padding_bottom = 0,
+                                    padding_left = 0, padding_right = 0)
+        removeAlignment.add(self._removeButton)
+        controlBox.pack_start(removeAlignment, False, False, 0)
+
+        self._moveUpButton = gtk.Button(xstr("chklst_moveUp"))
+        self._moveUpButton.set_use_underline(True)
+        self._moveUpButton.set_tooltip_text(xstr("chklst_moveUp_tooltip"))
+        self._moveUpButton.set_sensitive(False)
+        self._moveUpButton.connect("clicked", self._moveUpButtonClicked)
+
+        moveUpAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
+                                        xscale = 0.0, yscale = 0.0)
+        moveUpAlignment.set_padding(padding_top = 16, padding_bottom = 0,
+                                    padding_left = 0, padding_right = 0)
+        moveUpAlignment.add(self._moveUpButton)
+        controlBox.pack_start(moveUpAlignment, False, False, 0)
+
+        self._moveDownButton = gtk.Button(xstr("chklst_moveDown"))
+        self._moveDownButton.set_use_underline(True)
+        self._moveDownButton.set_tooltip_text(xstr("chklst_moveDown_tooltip"))
+        self._moveDownButton.set_sensitive(False)
+        self._moveDownButton.connect("clicked", self._moveDownButtonClicked)
+
+        moveDownAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
+                                        xscale = 0.0, yscale = 0.0)
+        moveDownAlignment.set_padding(padding_top = 4, padding_bottom = 0,
+                                    padding_left = 0, padding_right = 0)
+        moveDownAlignment.add(self._moveDownButton)
+        controlBox.pack_start(moveDownAlignment, False, False, 0)
+
+        self._fileListModel = gtk.ListStore(str, str)
+        self._fileList = gtk.TreeView(model = self._fileListModel)
+        self._fileList.connect("button-press-event",
+                               self._fileListButtonPressed)
+        column = gtk.TreeViewColumn(xstr("chklst_header"),
+                                    gtk.CellRendererText(), text = 0)
+        column.set_expand(True)
+        column.set_clickable(False)
+        column.set_reorderable(False)
+        self._fileList.append_column(column)
+        self._fileList.set_tooltip_column(1)
+        self._fileList.set_reorderable(True)
+        self._fileListPopupMenu = None
+        selection = self._fileList.get_selection()
+        selection.set_mode(SELECTION_MULTIPLE)
+        selection.connect("changed", self._fileListSelectionChanged)
+
+        self._buildFileListPopupMenu()
+
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.add(self._fileList)
+        scrolledWindow.set_size_request(200, -1)
+        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+        scrolledWindow.set_shadow_type(SHADOW_IN)
+
+        fileBox.pack_start(scrolledWindow, False, False, 4)
+
+        contentArea.pack_start(fileBox, True, True, 4)
+
+        self.set_size_request(900, 500)
+
+    def run(self):
+        """Run the checklist editor dialog."""
+        self._checklists = {}
+        self._displayCurrentChecklist()
+        self.show_all()
+        response = super(ChecklistEditor, self).run()
+        self.hide()
+
+        if response==RESPONSETYPE_ACCEPT:
+            self._saveChecklist()
+            config = self._gui.config
+            for (aircraftType, checklist) in self._checklists.iteritems():
+                config.setChecklist(aircraftType, checklist)
+            config.save()
+
+    def _aircraftTypeChanged(self, comboBox):
+        """Called when the aircraft's type has changed."""        
+        self._saveChecklist()
+        self._displayCurrentChecklist()
+        
+    def _fileChooserSelectionChanged(self, fileChooser):
+        """Called when the selection of the given file chooser is changed."""
+        numFiles = 0
+        numSelected = 0
+        for path in fileChooser.get_filenames():
+            path = text2unicode(path)
+            numSelected += 1
+            if os.path.isfile(path): numFiles += 1
+
+        self._addButton.set_sensitive(numFiles>0 and numFiles==numSelected)
+
+    def _addButtonClicked(self, button):
+        """Called when the Add button is clicked."""
+        for path in self._fileChooser.get_filenames():
+            path = text2unicode(path)
+            self._fileListModel.append([os.path.basename(path),
+                                        path])
+        self._fileChooser.unselect_all()
+        
+    def _removeButtonClicked(self, button):
+        """Called when the Remove button is clicked."""
+        self._removeSelected()
+
+    def _removeSelected(self):
+        """Remove the currently selected files."""
+        selection = self._fileList.get_selection()
+        (model, paths) = selection.get_selected_rows()
+
+        iters = [model.get_iter(path) for path in paths]
+
+        for i in iters:
+            if i is not None:
+                model.remove(i)
+        
+    def _moveUpButtonClicked(self, button):
+        """Called when the move up button is clicked."""
+        self._moveSelected(True)
+        
+    def _moveDownButtonClicked(self, button):
+        """Called when the move down button is clicked."""
+        self._moveSelected(False)
+
+    def _moveSelected(self, up):
+        """Move the selected files up or down."""
+        selection = self._fileList.get_selection()
+        (model, paths) = selection.get_selected_rows()
+        indexes = [(path.get_indices() if pygobject else path)[0]
+                   for path in paths]        
+        indexes.sort()
+        if not up:
+            indexes.reverse()
+
+        for index in indexes:
+            fromIter = model.iter_nth_child(None, index)
+            toIter = model.iter_nth_child(None, index-1 if up else index + 1)
+            if up:
+                model.move_before(fromIter, toIter)
+            else:
+                model.move_after(fromIter, toIter)
+        
+        self._moveUpButton.set_sensitive(indexes[0]>1 if up else True)
+        numRows = model.iter_n_children(None)
+        self._moveDownButton.set_sensitive(True if up else
+                                           indexes[0]<(numRows-2))
+
+    def _fileListSelectionChanged(self, selection):
+        """Called when the selection in the file list changes."""
+        anySelected = selection.count_selected_rows()>0
+        self._removeButton.set_sensitive(anySelected)
+        self._popupRemoveItem.set_sensitive(anySelected)
+
+        if anySelected:
+            (model, paths) = selection.get_selected_rows()
+            minIndex = None
+            maxIndex = None
+            for path in paths:
+                [index] = path.get_indices() if pygobject else path
+                if minIndex is None or index<minIndex: minIndex = index
+                if maxIndex is None or index>maxIndex: maxIndex = index
+                        
+            self._moveUpButton.set_sensitive(minIndex>0)
+            self._popupMoveUpItem.set_sensitive(minIndex>0)
+
+            numRows = model.iter_n_children(None)
+            self._moveDownButton.set_sensitive(maxIndex<(numRows-1))
+            self._popupMoveDownItem.set_sensitive(maxIndex<(numRows-1))
+        else:
+            self._moveUpButton.set_sensitive(False)
+            self._popupMoveUpItem.set_sensitive(False)
+            self._moveDownButton.set_sensitive(False)
+            self._popupMoveDownItem.set_sensitive(False)
+
+    def _getAircraftType(self):
+        """Get the currently selected aircraft type."""
+        index = self._aircraftType.get_active()
+        return self._aircraftTypeModel[index][1]        
+
+    def _saveChecklist(self):
+        """Save the currently displayed checklist for the previously displayed
+        aircraft type."""
+        fileList = []
+        model = self._fileListModel
+        iter = model.get_iter_first()
+        while iter is not None:
+            path = model.get(iter, 1)[0]
+            fileList.append(path)
+            iter = model.iter_next(iter)
+
+        self._checklists[self._currentAircraftType] = config.Checklist(fileList)
+        
+    def _displayCurrentChecklist(self):
+        """Display the checklist for the currently selected aircraft type."""
+        aircraftType = self._getAircraftType()
+        self._currentAircraftType = aircraftType
+        if aircraftType not in self._checklists:
+            self._checklists[aircraftType] = \
+                self._gui.config.getChecklist(aircraftType).clone()
+        checklist = self._checklists[aircraftType]
+
+        self._fileListModel.clear()
+        for path in checklist:
+            self._fileListModel.append([os.path.basename(path), path])
+        
+    def _fileListButtonPressed(self, widget, event):
+        """Called when a mouse button is pressed on the file list."""
+        if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
+            return
+
+        menu = self._fileListPopupMenu
+        if pygobject:
+            menu.popup(None, None, None, None, event.button, event.time)
+        else:
+            menu.popup(None, None, None, event.button, event.time)
+
+    def _buildFileListPopupMenu(self):
+        """Build the file list popup menu."""
+        menu = gtk.Menu()
+
+        menuItem = gtk.MenuItem()
+        menuItem.set_label(xstr("chklst_remove"))
+        menuItem.set_use_underline(True)
+        menuItem.connect("activate", self._popupRemove)
+        menuItem.show()
+        self._popupRemoveItem = menuItem
+
+        menu.append(menuItem)
+
+        menuItem = gtk.MenuItem()
+        menuItem.set_label(xstr("chklst_moveUp"))
+        menuItem.set_use_underline(True)
+        menuItem.connect("activate", self._popupMoveUp)
+        menuItem.show()
+        self._popupMoveUpItem = menuItem
+
+        menu.append(menuItem)
+
+        menuItem = gtk.MenuItem()
+        menuItem.set_label(xstr("chklst_moveDown"))
+        menuItem.set_use_underline(True)
+        menuItem.connect("activate", self._popupMoveDown)
+        menuItem.show()
+        self._popupMoveDownItem = menuItem
+
+        menu.append(menuItem)
+
+        self._fileListPopupMenu = menu
+
+    def _popupRemove(self, menuItem):
+        """Remove the currently selected menu items."""
+        self._removeSelected()
+
+    def _popupMoveUp(self, menuItem):
+        """Move up the currently selected menu items."""
+        self._moveSelected(True)
+
+    def _popupMoveDown(self, menuItem):
+        """Move down the currently selected menu items."""
+        self._moveSelected(False)
+        
+#------------------------------------------------------------------------------
Index: src/mlx/gui/checklist.py
===================================================================
--- src/mlx/gui/checklist.py	(revision 300)
+++ 	(revision )
@@ -1,380 +1,0 @@
-
-from common import *
-
-from mlx.i18n import xstr
-import mlx.const as const
-import mlx.config as config
-
-import os
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.checklist
-#
-# The checklist editor dialog.
-#
-# This module implements the checklist editor dialog. A checklist is a list of
-# sound files that are played one-by-one when requested by the user. The top
-# part of the dialog contains an aircraft type selector. Below it, on the left
-# there is a file selector, and the list of the files in the checklist to the
-# right. There are some buttons in the middle to manipulate the checklist.
-
-#------------------------------------------------------------------------------
-
-class ChecklistEditor(gtk.Dialog):
-    """The dialog to edit the checklists."""
-    def __init__(self, gui):
-        super(ChecklistEditor, self).__init__(WINDOW_TITLE_BASE + " - " +
-                                              xstr("chklst_title"),
-                                              gui.mainWindow,
-                                              DIALOG_MODAL)
-
-        self.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
-        self.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
-
-        self._gui = gui
-        self._checklists = {}
-        self._currentAircraftType = const.aircraftTypes[0]
-
-        contentArea = self.get_content_area()
-
-        typeBox = gtk.HBox()
-
-        label = gtk.Label(xstr("chklst_aircraftType"))
-        label.set_use_underline(True)
-
-        typeBox.pack_start(label, False, False, 4)
-
-        self._aircraftTypeModel = gtk.ListStore(str, int)
-        for type in const.aircraftTypes:
-            name = aircraftNames[type] if type in aircraftNames \
-                   else "Aircraft type #%d" % (type,)
-            self._aircraftTypeModel.append([name, type])
-        self._aircraftType = gtk.ComboBox(model = self._aircraftTypeModel)
-        renderer = gtk.CellRendererText()
-        self._aircraftType.pack_start(renderer, True)
-        self._aircraftType.add_attribute(renderer, "text", 0)
-        self._aircraftType.set_tooltip_text(xstr("chklst_aircraftType_tooltip"))
-        self._aircraftType.set_active(0)
-        self._aircraftType.connect("changed", self._aircraftTypeChanged)
-        label.set_mnemonic_widget(self._aircraftType)
-
-        typeBox.pack_start(self._aircraftType, True, True, 4)
-
-        typeBoxAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                         xscale = 0.0, yscale = 0.0)
-        typeBoxAlignment.set_size_request(400, -1)
-        typeBoxAlignment.add(typeBox)
-
-        contentArea.pack_start(typeBoxAlignment, False, False, 12)
-
-        fileBox = gtk.HBox()        
-
-        self._fileChooser = gtk.FileChooserWidget()
-        self._fileChooser.set_select_multiple(True)
-        
-        filter = gtk.FileFilter()
-        filter.set_name(xstr("file_filter_audio"))
-        filter.add_pattern("*.wav")
-        filter.add_pattern("*.mp3")
-        self._fileChooser.add_filter(filter)
-            
-        filter = gtk.FileFilter()
-        filter.set_name(xstr("file_filter_all"))
-        filter.add_pattern("*.*")
-        self._fileChooser.add_filter(filter)
-
-        self._fileChooser.connect("selection-changed",
-                                  self._fileChooserSelectionChanged)
-
-        fileBox.pack_start(self._fileChooser, True, True, 4)
-
-        controlBox = gtk.VBox()
-        controlAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                         xscale = 0.0, yscale = 0.0)
-        controlAlignment.set_padding(padding_top = 0, padding_bottom = 0,
-                                     padding_left = 32, padding_right = 32)
-        controlAlignment.add(controlBox)
-        fileBox.pack_start(controlAlignment, False, False, 0)
-
-        self._addButton = gtk.Button(xstr("chklst_add"))
-        self._addButton.set_use_underline(True)
-        self._addButton.set_tooltip_text(xstr("chklst_add_tooltip"))
-        self._addButton.connect("clicked", self._addButtonClicked)
-        addAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
-                                     xscale = 0.0, yscale = 0.0)
-        addAlignment.set_padding(padding_top = 64, padding_bottom = 0,
-                                 padding_left = 0, padding_right = 0)
-        addAlignment.add(self._addButton)
-        controlBox.pack_start(addAlignment, False, False, 0)
-
-        self._removeButton = gtk.Button(xstr("chklst_remove"))
-        self._removeButton.set_use_underline(True)
-        self._removeButton.set_tooltip_text(xstr("chklst_remove_tooltip"))
-        self._removeButton.set_sensitive(False)
-        self._removeButton.connect("clicked", self._removeButtonClicked)
-
-        removeAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
-                                        xscale = 0.0, yscale = 0.0)
-        removeAlignment.set_padding(padding_top = 64, padding_bottom = 0,
-                                    padding_left = 0, padding_right = 0)
-        removeAlignment.add(self._removeButton)
-        controlBox.pack_start(removeAlignment, False, False, 0)
-
-        self._moveUpButton = gtk.Button(xstr("chklst_moveUp"))
-        self._moveUpButton.set_use_underline(True)
-        self._moveUpButton.set_tooltip_text(xstr("chklst_moveUp_tooltip"))
-        self._moveUpButton.set_sensitive(False)
-        self._moveUpButton.connect("clicked", self._moveUpButtonClicked)
-
-        moveUpAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
-                                        xscale = 0.0, yscale = 0.0)
-        moveUpAlignment.set_padding(padding_top = 16, padding_bottom = 0,
-                                    padding_left = 0, padding_right = 0)
-        moveUpAlignment.add(self._moveUpButton)
-        controlBox.pack_start(moveUpAlignment, False, False, 0)
-
-        self._moveDownButton = gtk.Button(xstr("chklst_moveDown"))
-        self._moveDownButton.set_use_underline(True)
-        self._moveDownButton.set_tooltip_text(xstr("chklst_moveDown_tooltip"))
-        self._moveDownButton.set_sensitive(False)
-        self._moveDownButton.connect("clicked", self._moveDownButtonClicked)
-
-        moveDownAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
-                                        xscale = 0.0, yscale = 0.0)
-        moveDownAlignment.set_padding(padding_top = 4, padding_bottom = 0,
-                                    padding_left = 0, padding_right = 0)
-        moveDownAlignment.add(self._moveDownButton)
-        controlBox.pack_start(moveDownAlignment, False, False, 0)
-
-        self._fileListModel = gtk.ListStore(str, str)
-        self._fileList = gtk.TreeView(model = self._fileListModel)
-        self._fileList.connect("button-press-event",
-                               self._fileListButtonPressed)
-        column = gtk.TreeViewColumn(xstr("chklst_header"),
-                                    gtk.CellRendererText(), text = 0)
-        column.set_expand(True)
-        column.set_clickable(False)
-        column.set_reorderable(False)
-        self._fileList.append_column(column)
-        self._fileList.set_tooltip_column(1)
-        self._fileList.set_reorderable(True)
-        self._fileListPopupMenu = None
-        selection = self._fileList.get_selection()
-        selection.set_mode(SELECTION_MULTIPLE)
-        selection.connect("changed", self._fileListSelectionChanged)
-
-        self._buildFileListPopupMenu()
-
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.add(self._fileList)
-        scrolledWindow.set_size_request(200, -1)
-        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-        scrolledWindow.set_shadow_type(SHADOW_IN)
-
-        fileBox.pack_start(scrolledWindow, False, False, 4)
-
-        contentArea.pack_start(fileBox, True, True, 4)
-
-        self.set_size_request(900, 500)
-
-    def run(self):
-        """Run the checklist editor dialog."""
-        self._checklists = {}
-        self._displayCurrentChecklist()
-        self.show_all()
-        response = super(ChecklistEditor, self).run()
-        self.hide()
-
-        if response==RESPONSETYPE_ACCEPT:
-            self._saveChecklist()
-            config = self._gui.config
-            for (aircraftType, checklist) in self._checklists.iteritems():
-                config.setChecklist(aircraftType, checklist)
-            config.save()
-
-    def _aircraftTypeChanged(self, comboBox):
-        """Called when the aircraft's type has changed."""        
-        self._saveChecklist()
-        self._displayCurrentChecklist()
-        
-    def _fileChooserSelectionChanged(self, fileChooser):
-        """Called when the selection of the given file chooser is changed."""
-        numFiles = 0
-        numSelected = 0
-        for path in fileChooser.get_filenames():
-            path = text2unicode(path)
-            numSelected += 1
-            if os.path.isfile(path): numFiles += 1
-
-        self._addButton.set_sensitive(numFiles>0 and numFiles==numSelected)
-
-    def _addButtonClicked(self, button):
-        """Called when the Add button is clicked."""
-        for path in self._fileChooser.get_filenames():
-            path = text2unicode(path)
-            self._fileListModel.append([os.path.basename(path),
-                                        path])
-        self._fileChooser.unselect_all()
-        
-    def _removeButtonClicked(self, button):
-        """Called when the Remove button is clicked."""
-        self._removeSelected()
-
-    def _removeSelected(self):
-        """Remove the currently selected files."""
-        selection = self._fileList.get_selection()
-        (model, paths) = selection.get_selected_rows()
-
-        iters = [model.get_iter(path) for path in paths]
-
-        for i in iters:
-            if i is not None:
-                model.remove(i)
-        
-    def _moveUpButtonClicked(self, button):
-        """Called when the move up button is clicked."""
-        self._moveSelected(True)
-        
-    def _moveDownButtonClicked(self, button):
-        """Called when the move down button is clicked."""
-        self._moveSelected(False)
-
-    def _moveSelected(self, up):
-        """Move the selected files up or down."""
-        selection = self._fileList.get_selection()
-        (model, paths) = selection.get_selected_rows()
-        indexes = [(path.get_indices() if pygobject else path)[0]
-                   for path in paths]        
-        indexes.sort()
-        if not up:
-            indexes.reverse()
-
-        for index in indexes:
-            fromIter = model.iter_nth_child(None, index)
-            toIter = model.iter_nth_child(None, index-1 if up else index + 1)
-            if up:
-                model.move_before(fromIter, toIter)
-            else:
-                model.move_after(fromIter, toIter)
-        
-        self._moveUpButton.set_sensitive(indexes[0]>1 if up else True)
-        numRows = model.iter_n_children(None)
-        self._moveDownButton.set_sensitive(True if up else
-                                           indexes[0]<(numRows-2))
-
-    def _fileListSelectionChanged(self, selection):
-        """Called when the selection in the file list changes."""
-        anySelected = selection.count_selected_rows()>0
-        self._removeButton.set_sensitive(anySelected)
-        self._popupRemoveItem.set_sensitive(anySelected)
-
-        if anySelected:
-            (model, paths) = selection.get_selected_rows()
-            minIndex = None
-            maxIndex = None
-            for path in paths:
-                [index] = path.get_indices() if pygobject else path
-                if minIndex is None or index<minIndex: minIndex = index
-                if maxIndex is None or index>maxIndex: maxIndex = index
-                        
-            self._moveUpButton.set_sensitive(minIndex>0)
-            self._popupMoveUpItem.set_sensitive(minIndex>0)
-
-            numRows = model.iter_n_children(None)
-            self._moveDownButton.set_sensitive(maxIndex<(numRows-1))
-            self._popupMoveDownItem.set_sensitive(maxIndex<(numRows-1))
-        else:
-            self._moveUpButton.set_sensitive(False)
-            self._popupMoveUpItem.set_sensitive(False)
-            self._moveDownButton.set_sensitive(False)
-            self._popupMoveDownItem.set_sensitive(False)
-
-    def _getAircraftType(self):
-        """Get the currently selected aircraft type."""
-        index = self._aircraftType.get_active()
-        return self._aircraftTypeModel[index][1]        
-
-    def _saveChecklist(self):
-        """Save the currently displayed checklist for the previously displayed
-        aircraft type."""
-        fileList = []
-        model = self._fileListModel
-        iter = model.get_iter_first()
-        while iter is not None:
-            path = model.get(iter, 1)[0]
-            fileList.append(path)
-            iter = model.iter_next(iter)
-
-        self._checklists[self._currentAircraftType] = config.Checklist(fileList)
-        
-    def _displayCurrentChecklist(self):
-        """Display the checklist for the currently selected aircraft type."""
-        aircraftType = self._getAircraftType()
-        self._currentAircraftType = aircraftType
-        if aircraftType not in self._checklists:
-            self._checklists[aircraftType] = \
-                self._gui.config.getChecklist(aircraftType).clone()
-        checklist = self._checklists[aircraftType]
-
-        self._fileListModel.clear()
-        for path in checklist:
-            self._fileListModel.append([os.path.basename(path), path])
-        
-    def _fileListButtonPressed(self, widget, event):
-        """Called when a mouse button is pressed on the file list."""
-        if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
-            return
-
-        menu = self._fileListPopupMenu
-        if pygobject:
-            menu.popup(None, None, None, None, event.button, event.time)
-        else:
-            menu.popup(None, None, None, event.button, event.time)
-
-    def _buildFileListPopupMenu(self):
-        """Build the file list popup menu."""
-        menu = gtk.Menu()
-
-        menuItem = gtk.MenuItem()
-        menuItem.set_label(xstr("chklst_remove"))
-        menuItem.set_use_underline(True)
-        menuItem.connect("activate", self._popupRemove)
-        menuItem.show()
-        self._popupRemoveItem = menuItem
-
-        menu.append(menuItem)
-
-        menuItem = gtk.MenuItem()
-        menuItem.set_label(xstr("chklst_moveUp"))
-        menuItem.set_use_underline(True)
-        menuItem.connect("activate", self._popupMoveUp)
-        menuItem.show()
-        self._popupMoveUpItem = menuItem
-
-        menu.append(menuItem)
-
-        menuItem = gtk.MenuItem()
-        menuItem.set_label(xstr("chklst_moveDown"))
-        menuItem.set_use_underline(True)
-        menuItem.connect("activate", self._popupMoveDown)
-        menuItem.show()
-        self._popupMoveDownItem = menuItem
-
-        menu.append(menuItem)
-
-        self._fileListPopupMenu = menu
-
-    def _popupRemove(self, menuItem):
-        """Remove the currently selected menu items."""
-        self._removeSelected()
-
-    def _popupMoveUp(self, menuItem):
-        """Move up the currently selected menu items."""
-        self._moveSelected(True)
-
-    def _popupMoveDown(self, menuItem):
-        """Move down the currently selected menu items."""
-        self._moveSelected(False)
-        
-#------------------------------------------------------------------------------
Index: src/mlx/gui/common.py
===================================================================
--- src/mlx/gui/common.py	(revision 863)
+++ src/mlx/gui/common.py	(revision 863)
@@ -0,0 +1,683 @@
+
+from mlx.common import *
+
+import mlx.const as _const
+from mlx.i18n import xstr
+
+from mlx.util import secondaryInstallation
+
+import os
+import time
+import calendar
+
+#-----------------------------------------------------------------------------
+
+## @package mlx.gui.common
+#
+# Common definitions and utilities for the GUI
+#
+# The main purpose of this module is to provide common definitions for things
+# that are named differently in Gtk+ 2 and 3. This way the other parts of the
+# GUI have to check the version in use very rarely. The variable \c pygobject
+# tells which version is being used. If it is \c True, Gtk+ 3 is used via the
+# PyGObject interface. Otherwise Gtk+ 2 is used, which is the default on
+# Windows or when the \c FORCE_PYGTK environment variable is set.
+#
+# Besides this there are some common utility classes and functions.
+
+#-----------------------------------------------------------------------------
+
+appIndicator = False
+
+if not pygobject:
+    print "Using PyGTK"
+    pygobject = False
+    import pygtk
+    pygtk.require("2.0")
+    import gtk.gdk as gdk
+    import gtk
+    import pango
+
+    try:
+        import appindicator
+        appIndicator = True
+    except Exception, e:
+        pass
+
+    MESSAGETYPE_ERROR = gtk.MESSAGE_ERROR
+    MESSAGETYPE_QUESTION = gtk.MESSAGE_QUESTION
+    MESSAGETYPE_INFO = gtk.MESSAGE_INFO
+
+    RESPONSETYPE_NONE = gtk.RESPONSE_NONE
+    RESPONSETYPE_OK = gtk.RESPONSE_OK
+    RESPONSETYPE_YES = gtk.RESPONSE_YES
+    RESPONSETYPE_NO = gtk.RESPONSE_NO
+    RESPONSETYPE_ACCEPT = gtk.RESPONSE_ACCEPT
+    RESPONSETYPE_REJECT = gtk.RESPONSE_REJECT
+    RESPONSETYPE_CANCEL = gtk.RESPONSE_CANCEL
+
+    ACCEL_VISIBLE = gtk.ACCEL_VISIBLE
+    CONTROL_MASK = gdk.CONTROL_MASK
+    DIALOG_MODAL = gtk.DIALOG_MODAL
+    WRAP_WORD = gtk.WRAP_WORD
+
+    JUSTIFY_CENTER = gtk.JUSTIFY_CENTER
+    JUSTIFY_LEFT = gtk.JUSTIFY_LEFT
+
+    CONTROL_MASK = gdk.CONTROL_MASK
+    SHIFT_MASK = gdk.SHIFT_MASK
+    BUTTON1_MASK = gdk.BUTTON1_MASK
+
+    SCROLL_UP = gdk.SCROLL_UP
+    SCROLL_DOWN = gdk.SCROLL_DOWN
+
+    SPIN_USER_DEFINED = gtk.SPIN_USER_DEFINED
+
+    FILE_CHOOSER_ACTION_SELECT_FOLDER = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
+    FILE_CHOOSER_ACTION_OPEN = gtk.FILE_CHOOSER_ACTION_OPEN
+    FILE_CHOOSER_ACTION_SAVE = gtk.FILE_CHOOSER_ACTION_SAVE
+
+    SELECTION_MULTIPLE = gtk.SELECTION_MULTIPLE
+
+    SHADOW_IN = gtk.SHADOW_IN
+    SHADOW_NONE = gtk.SHADOW_NONE
+
+    POLICY_AUTOMATIC = gtk.POLICY_AUTOMATIC
+    POLICY_NEVER = gtk.POLICY_NEVER
+    POLICY_ALWAYS = gtk.POLICY_ALWAYS
+
+    WEIGHT_NORMAL = pango.WEIGHT_NORMAL
+    WEIGHT_BOLD = pango.WEIGHT_BOLD
+
+    WINDOW_STATE_ICONIFIED = gdk.WINDOW_STATE_ICONIFIED
+    WINDOW_STATE_WITHDRAWN = gdk.WINDOW_STATE_WITHDRAWN
+
+    SORT_ASCENDING = gtk.SORT_ASCENDING
+    SORT_DESCENDING = gtk.SORT_DESCENDING
+
+    EVENT_BUTTON_PRESS = gdk.BUTTON_PRESS
+
+    TREE_VIEW_COLUMN_FIXED = gtk.TREE_VIEW_COLUMN_FIXED
+
+    FILL = gtk.FILL
+    EXPAND = gtk.EXPAND
+
+    UPDATE_IF_VALID = gtk.UPDATE_IF_VALID
+
+    SELECTION_MULTIPLE = gtk.SELECTION_MULTIPLE
+
+    WINDOW_POPUP = gtk.WINDOW_POPUP
+
+    pixbuf_new_from_file = gdk.pixbuf_new_from_file
+
+    def text2unicode(text):
+        """Convert the given text, returned by a Gtk widget, to Unicode."""
+        return unicode(text)
+
+    def text2str(text):
+        """Convert the given text, returned by xstr to a string."""
+        return str(text)
+
+else: # pygobject
+    from gi.repository import Gdk as gdk
+    from gi.repository import GdkPixbuf as gdkPixbuf
+    from gi.repository import Gtk as gtk
+    from gi.repository import AppIndicator3 as appindicator
+    from gi.repository import Pango as pango
+
+    appIndicator = True
+
+
+    MESSAGETYPE_ERROR = gtk.MessageType.ERROR
+    MESSAGETYPE_QUESTION = gtk.MessageType.QUESTION
+    MESSAGETYPE_INFO = gtk.MessageType.INFO
+    RESPONSETYPE_NONE = gtk.ResponseType.NONE
+    RESPONSETYPE_OK = gtk.ResponseType.OK
+    RESPONSETYPE_YES = gtk.ResponseType.YES
+    RESPONSETYPE_NO = gtk.ResponseType.NO
+    RESPONSETYPE_ACCEPT = gtk.ResponseType.ACCEPT
+    RESPONSETYPE_REJECT = gtk.ResponseType.REJECT
+    RESPONSETYPE_CANCEL = gtk.ResponseType.CANCEL
+    ACCEL_VISIBLE = gtk.AccelFlags.VISIBLE
+    CONTROL_MASK = gdk.ModifierType.CONTROL_MASK
+    DIALOG_MODAL = gtk.DialogFlags.MODAL
+    WRAP_WORD = gtk.WrapMode.WORD
+    JUSTIFY_CENTER = gtk.Justification.CENTER
+    JUSTIFY_LEFT = gtk.Justification.LEFT
+
+    CONTROL_MASK = gdk.ModifierType.CONTROL_MASK
+    SHIFT_MASK = gdk.ModifierType.SHIFT_MASK
+    BUTTON1_MASK = gdk.ModifierType.BUTTON1_MASK
+
+    SCROLL_UP = gdk.ScrollDirection.UP
+    SCROLL_DOWN = gdk.ScrollDirection.DOWN
+
+    SPIN_USER_DEFINED = gtk.SpinType.USER_DEFINED
+
+    FILE_CHOOSER_ACTION_SELECT_FOLDER = gtk.FileChooserAction.SELECT_FOLDER
+    FILE_CHOOSER_ACTION_OPEN = gtk.FileChooserAction.OPEN
+    FILE_CHOOSER_ACTION_SAVE = gtk.FileChooserAction.SAVE
+
+    SELECTION_MULTIPLE = gtk.SelectionMode.MULTIPLE
+
+    SHADOW_IN = gtk.ShadowType.IN
+    SHADOW_NONE = gtk.ShadowType.NONE
+
+    POLICY_AUTOMATIC = gtk.PolicyType.AUTOMATIC
+    POLICY_NEVER = gtk.PolicyType.NEVER
+    POLICY_ALWAYS = gtk.PolicyType.ALWAYS
+
+    WEIGHT_NORMAL = pango.Weight.NORMAL
+    WEIGHT_BOLD = pango.Weight.BOLD
+
+    WINDOW_STATE_ICONIFIED = gdk.WindowState.ICONIFIED
+    WINDOW_STATE_WITHDRAWN = gdk.WindowState.WITHDRAWN
+
+    SORT_ASCENDING = gtk.SortType.ASCENDING
+    SORT_DESCENDING = gtk.SortType.DESCENDING
+
+    EVENT_BUTTON_PRESS = gdk.EventType.BUTTON_PRESS
+
+    TREE_VIEW_COLUMN_FIXED = gtk.TreeViewColumnSizing.FIXED
+
+    FILL = gtk.AttachOptions.FILL
+    EXPAND = gtk.AttachOptions.EXPAND
+
+    UPDATE_IF_VALID = gtk.SpinButtonUpdatePolicy.IF_VALID
+
+    SELECTION_MULTIPLE = gtk.SelectionMode.MULTIPLE
+
+    pixbuf_new_from_file = gdkPixbuf.Pixbuf.new_from_file
+
+    import codecs
+    _utf8Decoder = codecs.getdecoder("utf-8")
+
+    def text2unicode(str):
+        """Convert the given text, returned by a Gtk widget, to Unicode."""
+        return _utf8Decoder(str)[0]
+
+    def text2str(text):
+        """Convert the given text, returned by xstr to a string."""
+        return _utf8Decoder(text)[0]
+
+import cairo
+
+#------------------------------------------------------------------------------
+
+class FlightStatusHandler(object):
+    """Base class for objects that handle the flight status in some way."""
+    def __init__(self):
+        self._stage = None
+        self._rating = 100
+        self._noGoReason = None
+
+    def resetFlightStatus(self):
+        """Reset the flight status."""
+        self._stage = None
+        self._rating = 100
+        self._noGoReason = None
+        self._updateFlightStatus()
+
+    def setStage(self, stage):
+        """Set the stage of the flight."""
+        if stage!=self._stage:
+            self._stage = stage
+            self._updateFlightStatus()
+
+    def setRating(self, rating):
+        """Set the rating to the given value."""
+        if rating!=self._rating:
+            self._rating = rating
+            if self._noGoReason is None:
+                self._updateFlightStatus()
+
+    def setNoGo(self, reason):
+        """Set a No-Go condition with the given reason."""
+        if self._noGoReason is None:
+            self._noGoReason = reason
+            self._updateFlightStatus()
+
+#------------------------------------------------------------------------------
+
+class IntegerEntry(gtk.Entry):
+    """An entry that allows only either an empty value, or an integer."""
+    def __init__(self, defaultValue = None):
+        """Construct the entry."""
+        gtk.Entry.__init__(self)
+
+        self.set_alignment(1.0)
+
+        self._defaultValue = defaultValue
+        self._currentInteger = defaultValue
+        self._selfSetting = False
+        self._set_text()
+
+        self.connect("changed", self._handle_changed)
+
+    def get_int(self):
+        """Get the integer."""
+        return self._currentInteger
+
+    def reset(self):
+        """Reset the integer."""
+        self.set_int(None)
+
+    def set_int(self, value):
+        """Set the integer."""
+        if value!=self._currentInteger:
+            self._currentInteger = value
+            self.emit("integer-changed", self._currentInteger)
+        self._set_text()
+
+    def _handle_changed(self, widget):
+        """Handle the changed signal."""
+        if self._selfSetting:
+            return
+        text = self.get_text()
+        if text=="":
+            self.set_int(self._defaultValue)
+        else:
+            try:
+                self.set_int(int(text))
+            except:
+                self._set_text()
+
+    def _set_text(self):
+        """Set the text value from the current integer."""
+        self._selfSetting = True
+        self.set_text("" if self._currentInteger is None
+                      else str(self._currentInteger))
+        self._selfSetting = False
+
+#------------------------------------------------------------------------------
+
+class TimeEntry(gtk.Entry):
+    """Widget to display and edit a time value in HH:MM format."""
+    def __init__(self):
+        """Construct the entry"""
+        super(TimeEntry, self).__init__(max = 5)
+
+        self.connect("insert-text", self._insertText)
+        self.connect("delete-text", self._deleteText)
+        self.connect("focus-out-event", self._focusOutEvent)
+
+    @property
+    def hour(self):
+        """Get the hour from the current text"""
+        text = self.get_text()
+        if not text or text==":":
+            return 0
+
+        words = text.split(":")
+        if len(words)==1:
+            return 0
+        elif len(words)>=2:
+            return 0 if len(words[0])==0 else int(words[0])
+        else:
+            return 0
+
+    @property
+    def minute(self):
+        """Get the hour from the current text"""
+        text = self.get_text()
+        if not text or text==":":
+            return 0
+
+        words = text.split(":")
+        if len(words)==1:
+            return 0 if len(words[0])==0 else int(words[0])
+        elif len(words)>=2:
+            return 0 if len(words[1])==0 else int(words[1])
+        else:
+            return 0
+
+    @property
+    def minutes(self):
+        """Get the time in minutes, i.e. hour*60+minute."""
+        return self.hour * 60 + self.minute
+
+    def setTimestamp(self, timestamp):
+        """Set the hour and minute from the given timestamp in UTC."""
+        tm = time.gmtime(timestamp)
+        self.set_text("%02d:%02d" % (tm.tm_hour, tm.tm_min))
+
+    def getTimestampFrom(self, timestamp):
+        """Get the timestamp by replacing the hour and minute from the given
+        timestamp with what is set in this widget."""
+        tm = time.gmtime(timestamp)
+        ts = calendar.timegm((tm.tm_year, tm.tm_mon, tm.tm_mday,
+                              self.hour, self.minute, 0,
+                              tm.tm_wday, tm.tm_yday, tm.tm_isdst))
+
+        if ts > (timestamp + (16*60*60)):
+            ts -= 24*60*60
+        elif (ts + 16*60*60) < timestamp:
+            ts += 24*60*60
+
+        return ts
+
+    def _focusOutEvent(self, widget, event):
+        """Reformat the text to match pattern HH:MM"""
+        text = "%02d:%02d" % (self.hour, self.minute)
+        if text!=self.get_text():
+            self.set_text(text)
+
+    def _insertText(self, entry, text, length, position):
+        """Called when some text is inserted into the entry."""
+        text=text[:length]
+        currentText = self.get_text()
+        position = self.get_position()
+        newText = currentText[:position] + text + currentText[position:]
+        self._checkText(newText, "insert-text")
+
+    def _deleteText(self, entry, start, end):
+        """Called when some text is erased from the entry."""
+        currentText = self.get_text()
+        newText = currentText[:start] + currentText[end:]
+        self._checkText(newText, "delete-text")
+
+    def _checkText(self, newText, signal):
+        """Check the given text.
+
+        If it is not suitable, stop the emission of the signal to prevent the
+        change from appearing."""
+        if not newText or newText==":":
+            return
+
+        words = newText.split(":")
+        if (len(words)==1 and
+            len(words[0])<=2 and (len(words[0])==0 or
+                                  (words[0].isdigit() and int(words[0])<60))) or \
+           (len(words)==2 and
+            len(words[0])<=2 and (len(words[0])==0 or
+                                  (words[0].isdigit() and int(words[0])<24)) and
+            len(words[1])<=2 and (len(words[1])==0 or
+                                  (words[1].isdigit() and int(words[1])<60))):
+            pass
+        else:
+            gtk.gdk.display_get_default().beep()
+            self.stop_emission(signal)
+
+#------------------------------------------------------------------------------
+
+class CredentialsDialog(gtk.Dialog):
+    """A dialog window to ask for a user name and a password."""
+    def __init__(self, gui, userName, password,
+                 titleLabel, cancelButtonLabel, okButtonLabel,
+                 userNameLabel, userNameTooltip,
+                 passwordLabel, passwordTooltip,
+                 infoText = None,
+                 rememberPassword = None,
+                 rememberLabel = None, rememberTooltip = None):
+        """Construct the dialog."""
+        super(CredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
+                                                titleLabel,
+                                                gui.mainWindow,
+                                                DIALOG_MODAL)
+        self.add_button(cancelButtonLabel, RESPONSETYPE_CANCEL)
+        self.add_button(okButtonLabel, RESPONSETYPE_OK)
+
+        contentArea = self.get_content_area()
+
+        contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                         xscale = 0.0, yscale = 0.0)
+        contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
+                                     padding_left = 8, padding_right = 8)
+
+        contentArea.pack_start(contentAlignment, False, False, 0)
+
+        contentVBox = gtk.VBox()
+        contentAlignment.add(contentVBox)
+
+        if infoText is not None:
+            label = gtk.Label(infoText)
+            label.set_alignment(0.0, 0.0)
+
+            contentVBox.pack_start(label, False, False, 0)
+
+        tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
+                                   padding_left = 0, padding_right = 0)
+
+        table = gtk.Table(3, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+
+        tableAlignment.add(table)
+        contentVBox.pack_start(tableAlignment, True, True, 0)
+
+        label = gtk.Label(userNameLabel)
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 0, 1)
+
+        self._userName = gtk.Entry()
+        self._userName.set_width_chars(16)
+        # FIXME: enabled the OK button only when there is something in thr
+        # user name and password fields
+        #self._userName.connect("changed",
+        #                       lambda button: self._updateForwardButton())
+        self._userName.set_tooltip_text(userNameTooltip)
+        self._userName.set_text(userName)
+        table.attach(self._userName, 1, 2, 0, 1)
+        label.set_mnemonic_widget(self._userName)
+
+        label = gtk.Label(passwordLabel)
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 1, 2)
+
+        self._password = gtk.Entry()
+        self._password.set_visibility(False)
+        #self._password.connect("changed",
+        #                       lambda button: self._updateForwardButton())
+        self._password.set_tooltip_text(passwordTooltip)
+        self._password.set_text(password)
+        table.attach(self._password, 1, 2, 1, 2)
+        label.set_mnemonic_widget(self._password)
+
+        if rememberPassword is not None:
+            self._rememberButton = gtk.CheckButton(rememberLabel)
+            self._rememberButton.set_use_underline(True)
+            self._rememberButton.set_tooltip_text(rememberTooltip)
+            self._rememberButton.set_active(rememberPassword)
+            table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
+        else:
+            self._rememberButton = None
+
+    @property
+    def userName(self):
+        """Get the user name entered."""
+        return self._userName.get_text()
+
+    @property
+    def password(self):
+        """Get the password entered."""
+        return self._password.get_text()
+
+    @property
+    def rememberPassword(self):
+        """Get whether the password is to be remembered."""
+        return None if self._rememberButton is None \
+               else self._rememberButton.get_active()
+
+    def run(self):
+        """Run the dialog."""
+        self.show_all()
+
+        response = super(CredentialsDialog, self).run()
+
+        self.hide()
+
+        return response
+
+#------------------------------------------------------------------------------
+
+gobject.signal_new("integer-changed", IntegerEntry, gobject.SIGNAL_RUN_FIRST,
+                   None, (object,))
+
+#------------------------------------------------------------------------------
+
+PROGRAM_NAME = "MAVA Logger X"
+
+WINDOW_TITLE_BASE = PROGRAM_NAME + " " + _const.VERSION
+if secondaryInstallation:
+    WINDOW_TITLE_BASE += " (" + xstr("secondary") + ")"
+
+#------------------------------------------------------------------------------
+
+# A mapping of aircraft types to their screen names
+aircraftNames = { _const.AIRCRAFT_B736  : xstr("aircraft_b736"),
+                  _const.AIRCRAFT_B737  : xstr("aircraft_b737"),
+                  _const.AIRCRAFT_B738  : xstr("aircraft_b738"),
+                  _const.AIRCRAFT_B738C : xstr("aircraft_b738c"),
+                  _const.AIRCRAFT_B732  : xstr("aircraft_b732"),
+                  _const.AIRCRAFT_B733  : xstr("aircraft_b733"),
+                  _const.AIRCRAFT_B734  : xstr("aircraft_b734"),
+                  _const.AIRCRAFT_B735  : xstr("aircraft_b735"),
+                  _const.AIRCRAFT_DH8D  : xstr("aircraft_dh8d"),
+                  _const.AIRCRAFT_B762  : xstr("aircraft_b762"),
+                  _const.AIRCRAFT_B763  : xstr("aircraft_b763"),
+                  _const.AIRCRAFT_CRJ2  : xstr("aircraft_crj2"),
+                  _const.AIRCRAFT_F70   : xstr("aircraft_f70"),
+                  _const.AIRCRAFT_DC3   : xstr("aircraft_dc3"),
+                  _const.AIRCRAFT_T134  : xstr("aircraft_t134"),
+                  _const.AIRCRAFT_T154  : xstr("aircraft_t154"),
+                  _const.AIRCRAFT_YK40  : xstr("aircraft_yk40"),
+                  _const.AIRCRAFT_B462  : xstr("aircraft_b462") }
+
+#------------------------------------------------------------------------------
+
+aircraftFamilyNames = {
+    _const.AIRCRAFT_FAMILY_B737NG: xstr("aircraft_family_b737ng"),
+
+    _const.AIRCRAFT_FAMILY_B737CL: xstr("aircraft_family_b737cl"),
+
+    _const.AIRCRAFT_FAMILY_DH8D: xstr("aircraft_family_dh8d"),
+
+    _const.AIRCRAFT_FAMILY_B767: xstr("aircraft_family_b767"),
+
+    _const.AIRCRAFT_FAMILY_CRJ2: xstr("aircraft_family_crj2"),
+
+    _const.AIRCRAFT_FAMILY_F70: xstr("aircraft_family_f70"),
+
+    _const.AIRCRAFT_FAMILY_DC3: xstr("aircraft_family_dc3"),
+
+    _const.AIRCRAFT_FAMILY_T134: xstr("aircraft_family_t134"),
+
+    _const.AIRCRAFT_FAMILY_T154: xstr("aircraft_family_t154"),
+
+    _const.AIRCRAFT_FAMILY_YK40: xstr("aircraft_family_yk40"),
+
+    _const.AIRCRAFT_FAMILY_B462: xstr("aircraft_family_b462")
+}
+
+#------------------------------------------------------------------------------
+
+def formatFlightLogLine(timeStr, line):
+    """Format the given flight log line."""
+    """Format the given line for flight logging."""
+    if timeStr is not None:
+        line = timeStr + ": " + line
+    return line + "\n"
+
+#------------------------------------------------------------------------------
+
+def addFaultTag(buffer):
+    """Add a tag named 'fault' to the given buffer."""
+    buffer.create_tag("fault", foreground="red", weight=WEIGHT_BOLD)
+
+#------------------------------------------------------------------------------
+
+def appendTextBuffer(buffer, text, isFault = False):
+    """Append the given line at the end of the given text buffer.
+
+    If isFault is set, use the tag named 'fault'."""
+    insertTextBuffer(buffer, buffer.get_end_iter(), text, isFault)
+
+#------------------------------------------------------------------------------
+
+def insertTextBuffer(buffer, iter, text, isFault = False):
+    """Insert the given line into the given text buffer at the given iterator.
+
+    If isFault is set, use the tag named 'fault' else use the tag named
+    'normal'."""
+    line = iter.get_line()
+
+    buffer.insert(iter, text)
+
+    iter0 = buffer.get_iter_at_line(line)
+    iter1 = buffer.get_iter_at_line(line+1)
+    if isFault:
+        buffer.apply_tag_by_name("fault", iter0, iter1)
+    else:
+        buffer.remove_all_tags(iter0, iter1)
+
+#------------------------------------------------------------------------------
+
+def askYesNo(question, parent = None, title = WINDOW_TITLE_BASE):
+    """Ask a Yes/No question.
+
+    Return a boolean indicating the answer."""
+    dialog = gtk.MessageDialog(parent = parent,
+                               type = MESSAGETYPE_QUESTION,
+                               message_format = question)
+
+    dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
+    dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
+
+    dialog.set_title(title)
+    result = dialog.run()
+    dialog.hide()
+
+    return result==RESPONSETYPE_YES
+
+#------------------------------------------------------------------------------
+
+def errorDialog(message, parent = None, secondary = None,
+                title = WINDOW_TITLE_BASE):
+    """Display an error dialog box with the given message."""
+    dialog = gtk.MessageDialog(parent = parent,
+                               type = MESSAGETYPE_ERROR,
+                               message_format = message)
+    dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+    dialog.set_title(title)
+    if secondary is not None:
+        dialog.format_secondary_markup(secondary)
+
+    dialog.run()
+    dialog.hide()
+
+#------------------------------------------------------------------------------
+
+def communicationErrorDialog(parent = None, title = WINDOW_TITLE_BASE):
+    """Display a communication error dialog."""
+    errorDialog(xstr("error_communication"), parent = parent,
+                secondary = xstr("error_communication_secondary"),
+                title = title)
+
+#------------------------------------------------------------------------------
+
+def createFlightTypeComboBox():
+        flightTypeModel = gtk.ListStore(str, int)
+        for type in _const.flightTypes:
+            name = "flighttype_" + _const.flightType2string(type)
+            flightTypeModel.append([xstr(name), type])
+
+        flightType = gtk.ComboBox(model = flightTypeModel)
+        renderer = gtk.CellRendererText()
+        flightType.pack_start(renderer, True)
+        flightType.add_attribute(renderer, "text", 0)
+
+        return flightType
+
+#------------------------------------------------------------------------------
+
+def getTextViewText(textView):
+    """Get the text from the given text view."""
+    buffer = textView.get_buffer()
+    return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
+
+#------------------------------------------------------------------------------
Index: src/mlx/gui/common.py
===================================================================
--- src/mlx/gui/common.py	(revision 863)
+++ 	(revision )
@@ -1,683 +1,0 @@
-
-from mlx.common import *
-
-import mlx.const as _const
-from mlx.i18n import xstr
-
-from mlx.util import secondaryInstallation
-
-import os
-import time
-import calendar
-
-#-----------------------------------------------------------------------------
-
-## @package mlx.gui.common
-#
-# Common definitions and utilities for the GUI
-#
-# The main purpose of this module is to provide common definitions for things
-# that are named differently in Gtk+ 2 and 3. This way the other parts of the
-# GUI have to check the version in use very rarely. The variable \c pygobject
-# tells which version is being used. If it is \c True, Gtk+ 3 is used via the
-# PyGObject interface. Otherwise Gtk+ 2 is used, which is the default on
-# Windows or when the \c FORCE_PYGTK environment variable is set.
-#
-# Besides this there are some common utility classes and functions.
-
-#-----------------------------------------------------------------------------
-
-appIndicator = False
-
-if not pygobject:
-    print "Using PyGTK"
-    pygobject = False
-    import pygtk
-    pygtk.require("2.0")
-    import gtk.gdk as gdk
-    import gtk
-    import pango
-
-    try:
-        import appindicator
-        appIndicator = True
-    except Exception, e:
-        pass
-
-    MESSAGETYPE_ERROR = gtk.MESSAGE_ERROR
-    MESSAGETYPE_QUESTION = gtk.MESSAGE_QUESTION
-    MESSAGETYPE_INFO = gtk.MESSAGE_INFO
-
-    RESPONSETYPE_NONE = gtk.RESPONSE_NONE
-    RESPONSETYPE_OK = gtk.RESPONSE_OK
-    RESPONSETYPE_YES = gtk.RESPONSE_YES
-    RESPONSETYPE_NO = gtk.RESPONSE_NO
-    RESPONSETYPE_ACCEPT = gtk.RESPONSE_ACCEPT
-    RESPONSETYPE_REJECT = gtk.RESPONSE_REJECT
-    RESPONSETYPE_CANCEL = gtk.RESPONSE_CANCEL
-
-    ACCEL_VISIBLE = gtk.ACCEL_VISIBLE
-    CONTROL_MASK = gdk.CONTROL_MASK
-    DIALOG_MODAL = gtk.DIALOG_MODAL
-    WRAP_WORD = gtk.WRAP_WORD
-
-    JUSTIFY_CENTER = gtk.JUSTIFY_CENTER
-    JUSTIFY_LEFT = gtk.JUSTIFY_LEFT
-
-    CONTROL_MASK = gdk.CONTROL_MASK
-    SHIFT_MASK = gdk.SHIFT_MASK
-    BUTTON1_MASK = gdk.BUTTON1_MASK
-
-    SCROLL_UP = gdk.SCROLL_UP
-    SCROLL_DOWN = gdk.SCROLL_DOWN
-
-    SPIN_USER_DEFINED = gtk.SPIN_USER_DEFINED
-
-    FILE_CHOOSER_ACTION_SELECT_FOLDER = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
-    FILE_CHOOSER_ACTION_OPEN = gtk.FILE_CHOOSER_ACTION_OPEN
-    FILE_CHOOSER_ACTION_SAVE = gtk.FILE_CHOOSER_ACTION_SAVE
-
-    SELECTION_MULTIPLE = gtk.SELECTION_MULTIPLE
-
-    SHADOW_IN = gtk.SHADOW_IN
-    SHADOW_NONE = gtk.SHADOW_NONE
-
-    POLICY_AUTOMATIC = gtk.POLICY_AUTOMATIC
-    POLICY_NEVER = gtk.POLICY_NEVER
-    POLICY_ALWAYS = gtk.POLICY_ALWAYS
-
-    WEIGHT_NORMAL = pango.WEIGHT_NORMAL
-    WEIGHT_BOLD = pango.WEIGHT_BOLD
-
-    WINDOW_STATE_ICONIFIED = gdk.WINDOW_STATE_ICONIFIED
-    WINDOW_STATE_WITHDRAWN = gdk.WINDOW_STATE_WITHDRAWN
-
-    SORT_ASCENDING = gtk.SORT_ASCENDING
-    SORT_DESCENDING = gtk.SORT_DESCENDING
-
-    EVENT_BUTTON_PRESS = gdk.BUTTON_PRESS
-
-    TREE_VIEW_COLUMN_FIXED = gtk.TREE_VIEW_COLUMN_FIXED
-
-    FILL = gtk.FILL
-    EXPAND = gtk.EXPAND
-
-    UPDATE_IF_VALID = gtk.UPDATE_IF_VALID
-
-    SELECTION_MULTIPLE = gtk.SELECTION_MULTIPLE
-
-    WINDOW_POPUP = gtk.WINDOW_POPUP
-
-    pixbuf_new_from_file = gdk.pixbuf_new_from_file
-
-    def text2unicode(text):
-        """Convert the given text, returned by a Gtk widget, to Unicode."""
-        return unicode(text)
-
-    def text2str(text):
-        """Convert the given text, returned by xstr to a string."""
-        return str(text)
-
-else: # pygobject
-    from gi.repository import Gdk as gdk
-    from gi.repository import GdkPixbuf as gdkPixbuf
-    from gi.repository import Gtk as gtk
-    from gi.repository import AppIndicator3 as appindicator
-    from gi.repository import Pango as pango
-
-    appIndicator = True
-
-
-    MESSAGETYPE_ERROR = gtk.MessageType.ERROR
-    MESSAGETYPE_QUESTION = gtk.MessageType.QUESTION
-    MESSAGETYPE_INFO = gtk.MessageType.INFO
-    RESPONSETYPE_NONE = gtk.ResponseType.NONE
-    RESPONSETYPE_OK = gtk.ResponseType.OK
-    RESPONSETYPE_YES = gtk.ResponseType.YES
-    RESPONSETYPE_NO = gtk.ResponseType.NO
-    RESPONSETYPE_ACCEPT = gtk.ResponseType.ACCEPT
-    RESPONSETYPE_REJECT = gtk.ResponseType.REJECT
-    RESPONSETYPE_CANCEL = gtk.ResponseType.CANCEL
-    ACCEL_VISIBLE = gtk.AccelFlags.VISIBLE
-    CONTROL_MASK = gdk.ModifierType.CONTROL_MASK
-    DIALOG_MODAL = gtk.DialogFlags.MODAL
-    WRAP_WORD = gtk.WrapMode.WORD
-    JUSTIFY_CENTER = gtk.Justification.CENTER
-    JUSTIFY_LEFT = gtk.Justification.LEFT
-
-    CONTROL_MASK = gdk.ModifierType.CONTROL_MASK
-    SHIFT_MASK = gdk.ModifierType.SHIFT_MASK
-    BUTTON1_MASK = gdk.ModifierType.BUTTON1_MASK
-
-    SCROLL_UP = gdk.ScrollDirection.UP
-    SCROLL_DOWN = gdk.ScrollDirection.DOWN
-
-    SPIN_USER_DEFINED = gtk.SpinType.USER_DEFINED
-
-    FILE_CHOOSER_ACTION_SELECT_FOLDER = gtk.FileChooserAction.SELECT_FOLDER
-    FILE_CHOOSER_ACTION_OPEN = gtk.FileChooserAction.OPEN
-    FILE_CHOOSER_ACTION_SAVE = gtk.FileChooserAction.SAVE
-
-    SELECTION_MULTIPLE = gtk.SelectionMode.MULTIPLE
-
-    SHADOW_IN = gtk.ShadowType.IN
-    SHADOW_NONE = gtk.ShadowType.NONE
-
-    POLICY_AUTOMATIC = gtk.PolicyType.AUTOMATIC
-    POLICY_NEVER = gtk.PolicyType.NEVER
-    POLICY_ALWAYS = gtk.PolicyType.ALWAYS
-
-    WEIGHT_NORMAL = pango.Weight.NORMAL
-    WEIGHT_BOLD = pango.Weight.BOLD
-
-    WINDOW_STATE_ICONIFIED = gdk.WindowState.ICONIFIED
-    WINDOW_STATE_WITHDRAWN = gdk.WindowState.WITHDRAWN
-
-    SORT_ASCENDING = gtk.SortType.ASCENDING
-    SORT_DESCENDING = gtk.SortType.DESCENDING
-
-    EVENT_BUTTON_PRESS = gdk.EventType.BUTTON_PRESS
-
-    TREE_VIEW_COLUMN_FIXED = gtk.TreeViewColumnSizing.FIXED
-
-    FILL = gtk.AttachOptions.FILL
-    EXPAND = gtk.AttachOptions.EXPAND
-
-    UPDATE_IF_VALID = gtk.SpinButtonUpdatePolicy.IF_VALID
-
-    SELECTION_MULTIPLE = gtk.SelectionMode.MULTIPLE
-
-    pixbuf_new_from_file = gdkPixbuf.Pixbuf.new_from_file
-
-    import codecs
-    _utf8Decoder = codecs.getdecoder("utf-8")
-
-    def text2unicode(str):
-        """Convert the given text, returned by a Gtk widget, to Unicode."""
-        return _utf8Decoder(str)[0]
-
-    def text2str(text):
-        """Convert the given text, returned by xstr to a string."""
-        return _utf8Decoder(text)[0]
-
-import cairo
-
-#------------------------------------------------------------------------------
-
-class FlightStatusHandler(object):
-    """Base class for objects that handle the flight status in some way."""
-    def __init__(self):
-        self._stage = None
-        self._rating = 100
-        self._noGoReason = None
-
-    def resetFlightStatus(self):
-        """Reset the flight status."""
-        self._stage = None
-        self._rating = 100
-        self._noGoReason = None
-        self._updateFlightStatus()
-
-    def setStage(self, stage):
-        """Set the stage of the flight."""
-        if stage!=self._stage:
-            self._stage = stage
-            self._updateFlightStatus()
-
-    def setRating(self, rating):
-        """Set the rating to the given value."""
-        if rating!=self._rating:
-            self._rating = rating
-            if self._noGoReason is None:
-                self._updateFlightStatus()
-
-    def setNoGo(self, reason):
-        """Set a No-Go condition with the given reason."""
-        if self._noGoReason is None:
-            self._noGoReason = reason
-            self._updateFlightStatus()
-
-#------------------------------------------------------------------------------
-
-class IntegerEntry(gtk.Entry):
-    """An entry that allows only either an empty value, or an integer."""
-    def __init__(self, defaultValue = None):
-        """Construct the entry."""
-        gtk.Entry.__init__(self)
-
-        self.set_alignment(1.0)
-
-        self._defaultValue = defaultValue
-        self._currentInteger = defaultValue
-        self._selfSetting = False
-        self._set_text()
-
-        self.connect("changed", self._handle_changed)
-
-    def get_int(self):
-        """Get the integer."""
-        return self._currentInteger
-
-    def reset(self):
-        """Reset the integer."""
-        self.set_int(None)
-
-    def set_int(self, value):
-        """Set the integer."""
-        if value!=self._currentInteger:
-            self._currentInteger = value
-            self.emit("integer-changed", self._currentInteger)
-        self._set_text()
-
-    def _handle_changed(self, widget):
-        """Handle the changed signal."""
-        if self._selfSetting:
-            return
-        text = self.get_text()
-        if text=="":
-            self.set_int(self._defaultValue)
-        else:
-            try:
-                self.set_int(int(text))
-            except:
-                self._set_text()
-
-    def _set_text(self):
-        """Set the text value from the current integer."""
-        self._selfSetting = True
-        self.set_text("" if self._currentInteger is None
-                      else str(self._currentInteger))
-        self._selfSetting = False
-
-#------------------------------------------------------------------------------
-
-class TimeEntry(gtk.Entry):
-    """Widget to display and edit a time value in HH:MM format."""
-    def __init__(self):
-        """Construct the entry"""
-        super(TimeEntry, self).__init__(max = 5)
-
-        self.connect("insert-text", self._insertText)
-        self.connect("delete-text", self._deleteText)
-        self.connect("focus-out-event", self._focusOutEvent)
-
-    @property
-    def hour(self):
-        """Get the hour from the current text"""
-        text = self.get_text()
-        if not text or text==":":
-            return 0
-
-        words = text.split(":")
-        if len(words)==1:
-            return 0
-        elif len(words)>=2:
-            return 0 if len(words[0])==0 else int(words[0])
-        else:
-            return 0
-
-    @property
-    def minute(self):
-        """Get the hour from the current text"""
-        text = self.get_text()
-        if not text or text==":":
-            return 0
-
-        words = text.split(":")
-        if len(words)==1:
-            return 0 if len(words[0])==0 else int(words[0])
-        elif len(words)>=2:
-            return 0 if len(words[1])==0 else int(words[1])
-        else:
-            return 0
-
-    @property
-    def minutes(self):
-        """Get the time in minutes, i.e. hour*60+minute."""
-        return self.hour * 60 + self.minute
-
-    def setTimestamp(self, timestamp):
-        """Set the hour and minute from the given timestamp in UTC."""
-        tm = time.gmtime(timestamp)
-        self.set_text("%02d:%02d" % (tm.tm_hour, tm.tm_min))
-
-    def getTimestampFrom(self, timestamp):
-        """Get the timestamp by replacing the hour and minute from the given
-        timestamp with what is set in this widget."""
-        tm = time.gmtime(timestamp)
-        ts = calendar.timegm((tm.tm_year, tm.tm_mon, tm.tm_mday,
-                              self.hour, self.minute, 0,
-                              tm.tm_wday, tm.tm_yday, tm.tm_isdst))
-
-        if ts > (timestamp + (16*60*60)):
-            ts -= 24*60*60
-        elif (ts + 16*60*60) < timestamp:
-            ts += 24*60*60
-
-        return ts
-
-    def _focusOutEvent(self, widget, event):
-        """Reformat the text to match pattern HH:MM"""
-        text = "%02d:%02d" % (self.hour, self.minute)
-        if text!=self.get_text():
-            self.set_text(text)
-
-    def _insertText(self, entry, text, length, position):
-        """Called when some text is inserted into the entry."""
-        text=text[:length]
-        currentText = self.get_text()
-        position = self.get_position()
-        newText = currentText[:position] + text + currentText[position:]
-        self._checkText(newText, "insert-text")
-
-    def _deleteText(self, entry, start, end):
-        """Called when some text is erased from the entry."""
-        currentText = self.get_text()
-        newText = currentText[:start] + currentText[end:]
-        self._checkText(newText, "delete-text")
-
-    def _checkText(self, newText, signal):
-        """Check the given text.
-
-        If it is not suitable, stop the emission of the signal to prevent the
-        change from appearing."""
-        if not newText or newText==":":
-            return
-
-        words = newText.split(":")
-        if (len(words)==1 and
-            len(words[0])<=2 and (len(words[0])==0 or
-                                  (words[0].isdigit() and int(words[0])<60))) or \
-           (len(words)==2 and
-            len(words[0])<=2 and (len(words[0])==0 or
-                                  (words[0].isdigit() and int(words[0])<24)) and
-            len(words[1])<=2 and (len(words[1])==0 or
-                                  (words[1].isdigit() and int(words[1])<60))):
-            pass
-        else:
-            gtk.gdk.display_get_default().beep()
-            self.stop_emission(signal)
-
-#------------------------------------------------------------------------------
-
-class CredentialsDialog(gtk.Dialog):
-    """A dialog window to ask for a user name and a password."""
-    def __init__(self, gui, userName, password,
-                 titleLabel, cancelButtonLabel, okButtonLabel,
-                 userNameLabel, userNameTooltip,
-                 passwordLabel, passwordTooltip,
-                 infoText = None,
-                 rememberPassword = None,
-                 rememberLabel = None, rememberTooltip = None):
-        """Construct the dialog."""
-        super(CredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
-                                                titleLabel,
-                                                gui.mainWindow,
-                                                DIALOG_MODAL)
-        self.add_button(cancelButtonLabel, RESPONSETYPE_CANCEL)
-        self.add_button(okButtonLabel, RESPONSETYPE_OK)
-
-        contentArea = self.get_content_area()
-
-        contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                         xscale = 0.0, yscale = 0.0)
-        contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
-                                     padding_left = 8, padding_right = 8)
-
-        contentArea.pack_start(contentAlignment, False, False, 0)
-
-        contentVBox = gtk.VBox()
-        contentAlignment.add(contentVBox)
-
-        if infoText is not None:
-            label = gtk.Label(infoText)
-            label.set_alignment(0.0, 0.0)
-
-            contentVBox.pack_start(label, False, False, 0)
-
-        tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
-                                   padding_left = 0, padding_right = 0)
-
-        table = gtk.Table(3, 2)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-
-        tableAlignment.add(table)
-        contentVBox.pack_start(tableAlignment, True, True, 0)
-
-        label = gtk.Label(userNameLabel)
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 0, 1)
-
-        self._userName = gtk.Entry()
-        self._userName.set_width_chars(16)
-        # FIXME: enabled the OK button only when there is something in thr
-        # user name and password fields
-        #self._userName.connect("changed",
-        #                       lambda button: self._updateForwardButton())
-        self._userName.set_tooltip_text(userNameTooltip)
-        self._userName.set_text(userName)
-        table.attach(self._userName, 1, 2, 0, 1)
-        label.set_mnemonic_widget(self._userName)
-
-        label = gtk.Label(passwordLabel)
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 1, 2)
-
-        self._password = gtk.Entry()
-        self._password.set_visibility(False)
-        #self._password.connect("changed",
-        #                       lambda button: self._updateForwardButton())
-        self._password.set_tooltip_text(passwordTooltip)
-        self._password.set_text(password)
-        table.attach(self._password, 1, 2, 1, 2)
-        label.set_mnemonic_widget(self._password)
-
-        if rememberPassword is not None:
-            self._rememberButton = gtk.CheckButton(rememberLabel)
-            self._rememberButton.set_use_underline(True)
-            self._rememberButton.set_tooltip_text(rememberTooltip)
-            self._rememberButton.set_active(rememberPassword)
-            table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
-        else:
-            self._rememberButton = None
-
-    @property
-    def userName(self):
-        """Get the user name entered."""
-        return self._userName.get_text()
-
-    @property
-    def password(self):
-        """Get the password entered."""
-        return self._password.get_text()
-
-    @property
-    def rememberPassword(self):
-        """Get whether the password is to be remembered."""
-        return None if self._rememberButton is None \
-               else self._rememberButton.get_active()
-
-    def run(self):
-        """Run the dialog."""
-        self.show_all()
-
-        response = super(CredentialsDialog, self).run()
-
-        self.hide()
-
-        return response
-
-#------------------------------------------------------------------------------
-
-gobject.signal_new("integer-changed", IntegerEntry, gobject.SIGNAL_RUN_FIRST,
-                   None, (object,))
-
-#------------------------------------------------------------------------------
-
-PROGRAM_NAME = "MAVA Logger X"
-
-WINDOW_TITLE_BASE = PROGRAM_NAME + " " + _const.VERSION
-if secondaryInstallation:
-    WINDOW_TITLE_BASE += " (" + xstr("secondary") + ")"
-
-#------------------------------------------------------------------------------
-
-# A mapping of aircraft types to their screen names
-aircraftNames = { _const.AIRCRAFT_B736  : xstr("aircraft_b736"),
-                  _const.AIRCRAFT_B737  : xstr("aircraft_b737"),
-                  _const.AIRCRAFT_B738  : xstr("aircraft_b738"),
-                  _const.AIRCRAFT_B738C : xstr("aircraft_b738c"),
-                  _const.AIRCRAFT_B732  : xstr("aircraft_b732"),
-                  _const.AIRCRAFT_B733  : xstr("aircraft_b733"),
-                  _const.AIRCRAFT_B734  : xstr("aircraft_b734"),
-                  _const.AIRCRAFT_B735  : xstr("aircraft_b735"),
-                  _const.AIRCRAFT_DH8D  : xstr("aircraft_dh8d"),
-                  _const.AIRCRAFT_B762  : xstr("aircraft_b762"),
-                  _const.AIRCRAFT_B763  : xstr("aircraft_b763"),
-                  _const.AIRCRAFT_CRJ2  : xstr("aircraft_crj2"),
-                  _const.AIRCRAFT_F70   : xstr("aircraft_f70"),
-                  _const.AIRCRAFT_DC3   : xstr("aircraft_dc3"),
-                  _const.AIRCRAFT_T134  : xstr("aircraft_t134"),
-                  _const.AIRCRAFT_T154  : xstr("aircraft_t154"),
-                  _const.AIRCRAFT_YK40  : xstr("aircraft_yk40"),
-                  _const.AIRCRAFT_B462  : xstr("aircraft_b462") }
-
-#------------------------------------------------------------------------------
-
-aircraftFamilyNames = {
-    _const.AIRCRAFT_FAMILY_B737NG: xstr("aircraft_family_b737ng"),
-
-    _const.AIRCRAFT_FAMILY_B737CL: xstr("aircraft_family_b737cl"),
-
-    _const.AIRCRAFT_FAMILY_DH8D: xstr("aircraft_family_dh8d"),
-
-    _const.AIRCRAFT_FAMILY_B767: xstr("aircraft_family_b767"),
-
-    _const.AIRCRAFT_FAMILY_CRJ2: xstr("aircraft_family_crj2"),
-
-    _const.AIRCRAFT_FAMILY_F70: xstr("aircraft_family_f70"),
-
-    _const.AIRCRAFT_FAMILY_DC3: xstr("aircraft_family_dc3"),
-
-    _const.AIRCRAFT_FAMILY_T134: xstr("aircraft_family_t134"),
-
-    _const.AIRCRAFT_FAMILY_T154: xstr("aircraft_family_t154"),
-
-    _const.AIRCRAFT_FAMILY_YK40: xstr("aircraft_family_yk40"),
-
-    _const.AIRCRAFT_FAMILY_B462: xstr("aircraft_family_b462")
-}
-
-#------------------------------------------------------------------------------
-
-def formatFlightLogLine(timeStr, line):
-    """Format the given flight log line."""
-    """Format the given line for flight logging."""
-    if timeStr is not None:
-        line = timeStr + ": " + line
-    return line + "\n"
-
-#------------------------------------------------------------------------------
-
-def addFaultTag(buffer):
-    """Add a tag named 'fault' to the given buffer."""
-    buffer.create_tag("fault", foreground="red", weight=WEIGHT_BOLD)
-
-#------------------------------------------------------------------------------
-
-def appendTextBuffer(buffer, text, isFault = False):
-    """Append the given line at the end of the given text buffer.
-
-    If isFault is set, use the tag named 'fault'."""
-    insertTextBuffer(buffer, buffer.get_end_iter(), text, isFault)
-
-#------------------------------------------------------------------------------
-
-def insertTextBuffer(buffer, iter, text, isFault = False):
-    """Insert the given line into the given text buffer at the given iterator.
-
-    If isFault is set, use the tag named 'fault' else use the tag named
-    'normal'."""
-    line = iter.get_line()
-
-    buffer.insert(iter, text)
-
-    iter0 = buffer.get_iter_at_line(line)
-    iter1 = buffer.get_iter_at_line(line+1)
-    if isFault:
-        buffer.apply_tag_by_name("fault", iter0, iter1)
-    else:
-        buffer.remove_all_tags(iter0, iter1)
-
-#------------------------------------------------------------------------------
-
-def askYesNo(question, parent = None, title = WINDOW_TITLE_BASE):
-    """Ask a Yes/No question.
-
-    Return a boolean indicating the answer."""
-    dialog = gtk.MessageDialog(parent = parent,
-                               type = MESSAGETYPE_QUESTION,
-                               message_format = question)
-
-    dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
-    dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
-
-    dialog.set_title(title)
-    result = dialog.run()
-    dialog.hide()
-
-    return result==RESPONSETYPE_YES
-
-#------------------------------------------------------------------------------
-
-def errorDialog(message, parent = None, secondary = None,
-                title = WINDOW_TITLE_BASE):
-    """Display an error dialog box with the given message."""
-    dialog = gtk.MessageDialog(parent = parent,
-                               type = MESSAGETYPE_ERROR,
-                               message_format = message)
-    dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-    dialog.set_title(title)
-    if secondary is not None:
-        dialog.format_secondary_markup(secondary)
-
-    dialog.run()
-    dialog.hide()
-
-#------------------------------------------------------------------------------
-
-def communicationErrorDialog(parent = None, title = WINDOW_TITLE_BASE):
-    """Display a communication error dialog."""
-    errorDialog(xstr("error_communication"), parent = parent,
-                secondary = xstr("error_communication_secondary"),
-                title = title)
-
-#------------------------------------------------------------------------------
-
-def createFlightTypeComboBox():
-        flightTypeModel = gtk.ListStore(str, int)
-        for type in _const.flightTypes:
-            name = "flighttype_" + _const.flightType2string(type)
-            flightTypeModel.append([xstr(name), type])
-
-        flightType = gtk.ComboBox(model = flightTypeModel)
-        renderer = gtk.CellRendererText()
-        flightType.pack_start(renderer, True)
-        flightType.add_attribute(renderer, "text", 0)
-
-        return flightType
-
-#------------------------------------------------------------------------------
-
-def getTextViewText(textView):
-    """Get the text from the given text view."""
-    buffer = textView.get_buffer()
-    return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
-
-#------------------------------------------------------------------------------
Index: src/mlx/gui/delaycodes.py
===================================================================
--- src/mlx/gui/delaycodes.py	(revision 840)
+++ src/mlx/gui/delaycodes.py	(revision 840)
@@ -0,0 +1,396 @@
+# Module to handle the GUI aspects of the table of the delay codes
+
+#------------------------------------------------------------------------------
+
+from dcdata import CAPTION, DELAYCODE, getTable
+
+from mlx.gui.common import *
+
+import mlx.const as const
+
+#------------------------------------------------------------------------------
+
+if pygobject:
+
+#------------------------------------------------------------------------------
+
+    class Viewport(gtk.Viewport):
+        """Viewport implementation that alleviates the problem with improper
+        resizing by the VBox."""
+        def __init__(self):
+            """Construct the viewport."""
+            gtk.Viewport.__init__(self)
+            self._recursive = False
+            self._vboxHeight = None
+
+        def setVBOXHeight(self, vboxHeight):
+            """Set the height of the VBox which will be used to calculate the
+            viewport's height."""
+            self._vboxHeight = vboxHeight
+
+        def do_size_allocate(self, allocation):
+            """Called when the viewport's size is allocated.
+
+            The height in the allocation object is modified so that it is only
+            so high to fit into the VBox."""
+            if self._vboxHeight is not None:
+                allocation.height = self._vboxHeight - allocation.y
+                self._vboxHeight = None
+            gtk.Viewport.do_size_allocate(self, allocation)
+
+    class DelayCodeTableBase(gtk.VBox, gtk.Scrollable):
+        """PyGObject-specific base class for the delay code table."""
+        __gproperties__ = {
+            "vscroll-policy" : ( gtk.ScrollablePolicy,
+                                 "vscroll-policy",
+                                 "The vertical scrolling policy",
+                                 gtk.ScrollablePolicy.MINIMUM,
+                                 gobject.PARAM_READWRITE ),
+            "vadjustment" : ( gtk.Adjustment,
+                              "vadjustment",
+                              "The vertical adjustment",
+                              gobject.PARAM_READWRITE ),
+            "hscroll-policy" : ( gtk.ScrollablePolicy,
+                                 "hscroll-policy",
+                                 "The horizontal scrolling policy",
+                                 gtk.ScrollablePolicy.MINIMUM,
+                                 gobject.PARAM_READWRITE ),
+            "hadjustment" : ( gtk.Adjustment,
+                              "hadjustment",
+                              "The horizontal adjustment",
+                              gobject.PARAM_READWRITE )  }
+
+
+        @staticmethod
+        def _createViewport():
+            """Create an instance of the viewport class used by this base class."""
+            return Viewport()
+
+        def __init__(self):
+            """Construct the delay code table."""
+            super(DelayCodeTableBase, self).__init__()
+
+        def do_size_allocate(self, allocation):
+            """Allocate the size for the table and its children.
+
+            This sets up the VBox height in the viewport and then calls the
+            do_size_allocate() function of VBox()."""
+            self._viewport.setVBOXHeight(allocation.height)
+            gtk.VBox.do_size_allocate(self, allocation)
+            self.allocate_column_sizes(allocation)
+
+        def do_get_property(self, prop):
+            """Get the value of one of the properties defined above.
+
+            The request is forwarded to the viewport."""
+            if prop.name=="vscroll-policy":
+                return self._viewport.get_vscroll_policy()
+            elif prop.name=="hscroll-policy":
+                return self._viewport.get_hscroll_policy()
+            elif prop.name=="vadjustment":
+                return self._viewport.get_vadjustment()
+            elif prop.name=="hadjustment":
+                return self._viewport.get_hadjustment()
+            else:
+                raise AttributeError("mlx.gui.delaycodes.DelayCodeTableBase: property %s is not handled in do_get_property" %
+                                     (prop.name,))
+
+        def do_set_property(self, prop, value):
+            """Set the value of the adjustment properties defined above.
+
+            The adjustments are forwarded to the viewport."""
+            if prop.name=="vadjustment":
+                self._viewport.set_vadjustment(value)
+            elif prop.name=="hadjustment":
+                self._viewport.set_hadjustment(value)
+                self._treeView.set_hadjustment(value)
+            else:
+                raise AttributeError("mlx.gui.delaycodes.DelayCodeTableBase: property %s is not handled in do_set_property" %
+                                     (prop.name,))
+
+        def setStyle(self):
+            """Set the style of the event box from the treeview."""
+
+    class Alignment(gtk.Alignment):
+        """An alignment that remembers the width it was given."""
+        def __init__(self, xalign = 0.0, yalign=0.0,
+                     xscale = 0.0, yscale = 0.0 ):
+            """Construct the alignment."""
+            super(Alignment, self).__init__(xalign = xalign, yalign = yalign,
+                                            xscale = xscale, yscale = yscale)
+            self.allocatedWidth = 0
+
+        def do_size_allocate(self, allocation):
+            """Called with the new size allocation."""
+            self.allocatedWidth = allocation.width
+            gtk.Alignment.do_size_allocate(self, allocation)
+
+#------------------------------------------------------------------------------
+
+else: # pygobject
+
+#------------------------------------------------------------------------------
+
+    class DelayCodeTableBase (gtk.VBox):
+        """Base class of the delay code table for PyGtk."""
+
+        __gsignals__ = {
+            "set-scroll-adjustments": (
+                gobject.SIGNAL_RUN_LAST,
+                gobject.TYPE_NONE, (gtk.Adjustment, gtk.Adjustment))
+                }
+
+        @staticmethod
+        def _createViewport():
+            """Create an instance of the viewport class used by this base class."""
+            return gtk.Viewport()
+
+        def __init__(self):
+            """Construct the base class."""
+            super(DelayCodeTableBase, self).__init__()
+            self.set_set_scroll_adjustments_signal("set-scroll-adjustments")
+            self.connect("size-allocate", self._do_size_allocate)
+
+        def do_set_scroll_adjustments(self, hAdjustment, vAdjustment):
+            """Set the adjustments on the viewport."""
+            self._viewport.set_hadjustment(hAdjustment)
+            self._viewport.set_vadjustment(vAdjustment)
+            self._treeView.set_hadjustment(hAdjustment)
+
+        def _do_size_allocate(self, widget, allocation):
+            """Handler of the size-allocate signal.
+
+            Calls allocate_column_sizes()."""
+            self.allocate_column_sizes(allocation)
+
+        def setStyle(self):
+            """Set the style of the event box from the treeview."""
+            if self._treeView is not None:
+                style = self._treeView.rc_get_style()
+                self._eventBox.modify_bg(0, style.bg[2])
+                self._eventBox.modify_fg(0, style.fg[2])
+
+    class Alignment(gtk.Alignment):
+        """An alignment that remembers the width it was given."""
+        def __init__(self, xalign = 0.0, yalign=0.0,
+                     xscale = 0.0, yscale = 0.0 ):
+            """Construct the alignment."""
+            super(Alignment, self).__init__(xalign = xalign, yalign = yalign,
+                                            xscale = xscale, yscale = yscale)
+            self.allocatedWidth = 0
+            self.connect("size-allocate", self._do_size_allocate)
+
+        def _do_size_allocate(self, widget, allocation):
+            """Called with the new size allocation."""
+            self.allocatedWidth = allocation.width
+
+#------------------------------------------------------------------------------
+
+class CheckButton(gtk.CheckButton):
+    """A check button that contains a reference to a row in the delay code
+    data table."""
+    def __init__(self, delayCodeRow):
+        """Construct the check button."""
+        super(CheckButton, self).__init__()
+        self.delayCodeRow = delayCodeRow
+
+#------------------------------------------------------------------------------
+
+# CAPTION = 1
+
+# DELAYCODE = 2
+
+# _data1 = ( lambda row: row[0].strip(),
+#            ["Num", "Code", "Title", "Description"],
+#            [ (CAPTION, "Others"),
+#              (DELAYCODE, ("        6", "OA     ", "NO GATES/STAND AVAILABLE",
+#                           "Due to own airline activity")),
+#              (DELAYCODE, ("9", "SG", "SCHEDULED GROUND TIME",
+#                           "Planned turnaround time less than declared minimum")),
+#              (CAPTION, "Passenger and baggage"),
+#              (DELAYCODE, ("11", "PD", "LATE CHECK-IN",
+#                           "Check-in reopened for late passengers")),
+#              (DELAYCODE, ("12", "PL", "LATE CHECK-IN",
+#                           "Check-in not completed by flight closure time")),
+#              (DELAYCODE, ("13", "PE", "CHECK-IN ERROR",
+#                           "Error with passenger or baggage details")) ] )
+
+# _data2 = ( lambda row: row[0].strip(),
+#            ["MA", "IATA", "Description"],
+#            [ (CAPTION, "Passenger and baggage"),
+#              (DELAYCODE, (" 012", "01   ",
+#                           "Late shipping of parts and/or materials")),
+#              (DELAYCODE, (" 111", "11",
+#                           "Check-in reopened for late passengers")),
+#              (DELAYCODE, (" 121", "12",
+#                           "Check-in not completed by flight closure time")),
+#              (DELAYCODE, (" 132", "13",
+#                           "Error with passenger or baggage details"))
+#             ])
+
+#------------------------------------------------------------------------------
+
+class DelayCodeTable(DelayCodeTableBase):
+    """The delay code table."""
+    def __init__(self, info):
+        """Construct the delay code table."""
+        super(DelayCodeTable, self).__init__()
+
+        self._info = info
+
+        self._delayCodeData = None
+
+        self._treeView = gtk.TreeView(gtk.ListStore(str, str))
+        self._treeView.set_rules_hint(True)
+
+        self.pack_start(self._treeView, False, False, 0)
+
+        self._alignments = []
+        self._checkButtons = []
+
+        self._eventBox = gtk.EventBox()
+
+        self._table = None
+
+        self._viewport = self._createViewport()
+        self._viewport.add(self._eventBox)
+        self._viewport.set_shadow_type(SHADOW_NONE)
+
+        self.pack_start(self._viewport, True, True, 0)
+
+        self._previousWidth = 0
+
+    @property
+    def delayCodes(self):
+        """Get a list of the delay codes checked by the user."""
+        codes = []
+
+        if self._delayCodeData is not None:
+            codeExtractor = self._delayCodeData[0]
+            for checkButton in self._checkButtons:
+                if checkButton.get_active():
+                    codes.append(codeExtractor(checkButton.delayCodeRow))
+
+        return codes
+
+    @property
+    def hasDelayCode(self):
+        """Determine if there is at least one delay code selected."""
+        if self._delayCodeData is not None:
+            for checkButton in self._checkButtons:
+                if checkButton.get_active():
+                    return True
+
+        return False
+
+    def allocate_column_sizes(self, allocation):
+        """Allocate the column sizes."""
+        if allocation.width!=self._previousWidth:
+            self._previousWidth = allocation.width
+            index = 0
+            lastIndex = len(self._alignments) - 1
+            for alignment in self._alignments:
+                column = self._treeView.get_column(index)
+                width = alignment.allocatedWidth
+                width += 8 if (index==0 or index==lastIndex) else 16
+                column.set_fixed_width(width)
+                index += 1
+
+    def setType(self, aircraftType):
+        """Setup the delay code table according to the given aircraft type."""
+        self._delayCodeData = data = getTable(aircraftType)
+        if data is None:
+            return
+
+        columns = self._treeView.get_columns()
+        for column in columns:
+            self._treeView.remove_column(column)
+
+        (_extractor, headers, rows) = data
+        numColumns = len(headers) + 1
+        numRows = len(rows)
+
+        column = gtk.TreeViewColumn("", gtk.CellRendererText())
+        column.set_sizing(TREE_VIEW_COLUMN_FIXED)
+        self._treeView.append_column(column)
+
+        for header in headers:
+            column = gtk.TreeViewColumn(header, gtk.CellRendererText())
+            column.set_sizing(TREE_VIEW_COLUMN_FIXED)
+            self._treeView.append_column(column)
+
+        self._table = gtk.Table(numRows, numColumns)
+        self._table.set_homogeneous(False)
+        self._table.set_col_spacings(16)
+        self._table.set_row_spacings(4)
+        self._eventBox.add(self._table)
+
+        self._alignments = []
+        self._checkButtons = []
+
+        firstDelayCodeRow = True
+        for i in range(0, numRows):
+            (type, elements) = rows[i]
+            if type==CAPTION:
+                alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                          xscale = 1.0)
+                label = gtk.Label("<b>" + elements + "</b>")
+                label.set_use_markup(True)
+                label.set_alignment(0.0, 0.5)
+                alignment.add(label)
+                self._table.attach(alignment, 1, numColumns, i, i+1,
+                                   yoptions = FILL)
+                self._table.set_row_spacing(i, 8)
+            elif type==DELAYCODE:
+                checkButton = CheckButton(elements)
+                checkButton.connect("toggled", self._delayCodesChanged)
+                self._checkButtons.append(checkButton)
+                alignment = Alignment(xalign = 0.5, yalign = 0.5, xscale = 1.0)
+                alignment.add(checkButton)
+                self._table.attach(alignment, 0, 1, i, i+1,
+                                   xoptions = FILL, yoptions = FILL)
+                if firstDelayCodeRow:
+                    self._alignments.append(alignment)
+
+                for j in range(0, numColumns-1):
+                    label = gtk.Label(elements[j])
+                    label.set_alignment(0.0, 0.5)
+                    alignment = Alignment(xalign = 0.5, yalign = 0.5,
+                                          xscale = 1.0)
+                    alignment.add(label)
+                    xoptions = FILL
+                    if j==(numColumns-2): xoptions |= EXPAND
+                    self._table.attach(alignment, j+1, j+2, i, i+1,
+                                       xoptions = xoptions, yoptions = FILL)
+                    if firstDelayCodeRow:
+                        self._alignments.append(alignment)
+                firstDelayCodeRow = False
+
+        self._previousWidth = 0
+        self.show_all()
+
+    def reset(self):
+        """Reset the delay code table."""
+        columns = self._treeView.get_columns()
+        for column in columns:
+            self._treeView.remove_column(column)
+        if self._table is not None:
+            self._eventBox.remove(self._table)
+        self._table = None
+        self.show_all()
+
+    def activateCode(self, code):
+        """Check the checkbox for the given code."""
+        index = 0
+        for (type, data) in self._delayCodeData[2]:
+            if type==DELAYCODE:
+                if code==data[0].strip():
+                    self._checkButtons[index].set_active(True)
+                    break
+                index += 1
+
+    def _delayCodesChanged(self, button):
+        """Called when one of the delay codes have changed."""
+        self._info.delayCodesChanged()
+
+#------------------------------------------------------------------------------
Index: src/mlx/gui/delaycodes.py
===================================================================
--- src/mlx/gui/delaycodes.py	(revision 840)
+++ 	(revision )
@@ -1,396 +1,0 @@
-# Module to handle the GUI aspects of the table of the delay codes
-
-#------------------------------------------------------------------------------
-
-from dcdata import CAPTION, DELAYCODE, getTable
-
-from mlx.gui.common import *
-
-import mlx.const as const
-
-#------------------------------------------------------------------------------
-
-if pygobject:
-
-#------------------------------------------------------------------------------
-
-    class Viewport(gtk.Viewport):
-        """Viewport implementation that alleviates the problem with improper
-        resizing by the VBox."""
-        def __init__(self):
-            """Construct the viewport."""
-            gtk.Viewport.__init__(self)
-            self._recursive = False
-            self._vboxHeight = None
-
-        def setVBOXHeight(self, vboxHeight):
-            """Set the height of the VBox which will be used to calculate the
-            viewport's height."""
-            self._vboxHeight = vboxHeight
-
-        def do_size_allocate(self, allocation):
-            """Called when the viewport's size is allocated.
-
-            The height in the allocation object is modified so that it is only
-            so high to fit into the VBox."""
-            if self._vboxHeight is not None:
-                allocation.height = self._vboxHeight - allocation.y
-                self._vboxHeight = None
-            gtk.Viewport.do_size_allocate(self, allocation)
-
-    class DelayCodeTableBase(gtk.VBox, gtk.Scrollable):
-        """PyGObject-specific base class for the delay code table."""
-        __gproperties__ = {
-            "vscroll-policy" : ( gtk.ScrollablePolicy,
-                                 "vscroll-policy",
-                                 "The vertical scrolling policy",
-                                 gtk.ScrollablePolicy.MINIMUM,
-                                 gobject.PARAM_READWRITE ),
-            "vadjustment" : ( gtk.Adjustment,
-                              "vadjustment",
-                              "The vertical adjustment",
-                              gobject.PARAM_READWRITE ),
-            "hscroll-policy" : ( gtk.ScrollablePolicy,
-                                 "hscroll-policy",
-                                 "The horizontal scrolling policy",
-                                 gtk.ScrollablePolicy.MINIMUM,
-                                 gobject.PARAM_READWRITE ),
-            "hadjustment" : ( gtk.Adjustment,
-                              "hadjustment",
-                              "The horizontal adjustment",
-                              gobject.PARAM_READWRITE )  }
-
-
-        @staticmethod
-        def _createViewport():
-            """Create an instance of the viewport class used by this base class."""
-            return Viewport()
-
-        def __init__(self):
-            """Construct the delay code table."""
-            super(DelayCodeTableBase, self).__init__()
-
-        def do_size_allocate(self, allocation):
-            """Allocate the size for the table and its children.
-
-            This sets up the VBox height in the viewport and then calls the
-            do_size_allocate() function of VBox()."""
-            self._viewport.setVBOXHeight(allocation.height)
-            gtk.VBox.do_size_allocate(self, allocation)
-            self.allocate_column_sizes(allocation)
-
-        def do_get_property(self, prop):
-            """Get the value of one of the properties defined above.
-
-            The request is forwarded to the viewport."""
-            if prop.name=="vscroll-policy":
-                return self._viewport.get_vscroll_policy()
-            elif prop.name=="hscroll-policy":
-                return self._viewport.get_hscroll_policy()
-            elif prop.name=="vadjustment":
-                return self._viewport.get_vadjustment()
-            elif prop.name=="hadjustment":
-                return self._viewport.get_hadjustment()
-            else:
-                raise AttributeError("mlx.gui.delaycodes.DelayCodeTableBase: property %s is not handled in do_get_property" %
-                                     (prop.name,))
-
-        def do_set_property(self, prop, value):
-            """Set the value of the adjustment properties defined above.
-
-            The adjustments are forwarded to the viewport."""
-            if prop.name=="vadjustment":
-                self._viewport.set_vadjustment(value)
-            elif prop.name=="hadjustment":
-                self._viewport.set_hadjustment(value)
-                self._treeView.set_hadjustment(value)
-            else:
-                raise AttributeError("mlx.gui.delaycodes.DelayCodeTableBase: property %s is not handled in do_set_property" %
-                                     (prop.name,))
-
-        def setStyle(self):
-            """Set the style of the event box from the treeview."""
-
-    class Alignment(gtk.Alignment):
-        """An alignment that remembers the width it was given."""
-        def __init__(self, xalign = 0.0, yalign=0.0,
-                     xscale = 0.0, yscale = 0.0 ):
-            """Construct the alignment."""
-            super(Alignment, self).__init__(xalign = xalign, yalign = yalign,
-                                            xscale = xscale, yscale = yscale)
-            self.allocatedWidth = 0
-
-        def do_size_allocate(self, allocation):
-            """Called with the new size allocation."""
-            self.allocatedWidth = allocation.width
-            gtk.Alignment.do_size_allocate(self, allocation)
-
-#------------------------------------------------------------------------------
-
-else: # pygobject
-
-#------------------------------------------------------------------------------
-
-    class DelayCodeTableBase (gtk.VBox):
-        """Base class of the delay code table for PyGtk."""
-
-        __gsignals__ = {
-            "set-scroll-adjustments": (
-                gobject.SIGNAL_RUN_LAST,
-                gobject.TYPE_NONE, (gtk.Adjustment, gtk.Adjustment))
-                }
-
-        @staticmethod
-        def _createViewport():
-            """Create an instance of the viewport class used by this base class."""
-            return gtk.Viewport()
-
-        def __init__(self):
-            """Construct the base class."""
-            super(DelayCodeTableBase, self).__init__()
-            self.set_set_scroll_adjustments_signal("set-scroll-adjustments")
-            self.connect("size-allocate", self._do_size_allocate)
-
-        def do_set_scroll_adjustments(self, hAdjustment, vAdjustment):
-            """Set the adjustments on the viewport."""
-            self._viewport.set_hadjustment(hAdjustment)
-            self._viewport.set_vadjustment(vAdjustment)
-            self._treeView.set_hadjustment(hAdjustment)
-
-        def _do_size_allocate(self, widget, allocation):
-            """Handler of the size-allocate signal.
-
-            Calls allocate_column_sizes()."""
-            self.allocate_column_sizes(allocation)
-
-        def setStyle(self):
-            """Set the style of the event box from the treeview."""
-            if self._treeView is not None:
-                style = self._treeView.rc_get_style()
-                self._eventBox.modify_bg(0, style.bg[2])
-                self._eventBox.modify_fg(0, style.fg[2])
-
-    class Alignment(gtk.Alignment):
-        """An alignment that remembers the width it was given."""
-        def __init__(self, xalign = 0.0, yalign=0.0,
-                     xscale = 0.0, yscale = 0.0 ):
-            """Construct the alignment."""
-            super(Alignment, self).__init__(xalign = xalign, yalign = yalign,
-                                            xscale = xscale, yscale = yscale)
-            self.allocatedWidth = 0
-            self.connect("size-allocate", self._do_size_allocate)
-
-        def _do_size_allocate(self, widget, allocation):
-            """Called with the new size allocation."""
-            self.allocatedWidth = allocation.width
-
-#------------------------------------------------------------------------------
-
-class CheckButton(gtk.CheckButton):
-    """A check button that contains a reference to a row in the delay code
-    data table."""
-    def __init__(self, delayCodeRow):
-        """Construct the check button."""
-        super(CheckButton, self).__init__()
-        self.delayCodeRow = delayCodeRow
-
-#------------------------------------------------------------------------------
-
-# CAPTION = 1
-
-# DELAYCODE = 2
-
-# _data1 = ( lambda row: row[0].strip(),
-#            ["Num", "Code", "Title", "Description"],
-#            [ (CAPTION, "Others"),
-#              (DELAYCODE, ("        6", "OA     ", "NO GATES/STAND AVAILABLE",
-#                           "Due to own airline activity")),
-#              (DELAYCODE, ("9", "SG", "SCHEDULED GROUND TIME",
-#                           "Planned turnaround time less than declared minimum")),
-#              (CAPTION, "Passenger and baggage"),
-#              (DELAYCODE, ("11", "PD", "LATE CHECK-IN",
-#                           "Check-in reopened for late passengers")),
-#              (DELAYCODE, ("12", "PL", "LATE CHECK-IN",
-#                           "Check-in not completed by flight closure time")),
-#              (DELAYCODE, ("13", "PE", "CHECK-IN ERROR",
-#                           "Error with passenger or baggage details")) ] )
-
-# _data2 = ( lambda row: row[0].strip(),
-#            ["MA", "IATA", "Description"],
-#            [ (CAPTION, "Passenger and baggage"),
-#              (DELAYCODE, (" 012", "01   ",
-#                           "Late shipping of parts and/or materials")),
-#              (DELAYCODE, (" 111", "11",
-#                           "Check-in reopened for late passengers")),
-#              (DELAYCODE, (" 121", "12",
-#                           "Check-in not completed by flight closure time")),
-#              (DELAYCODE, (" 132", "13",
-#                           "Error with passenger or baggage details"))
-#             ])
-
-#------------------------------------------------------------------------------
-
-class DelayCodeTable(DelayCodeTableBase):
-    """The delay code table."""
-    def __init__(self, info):
-        """Construct the delay code table."""
-        super(DelayCodeTable, self).__init__()
-
-        self._info = info
-
-        self._delayCodeData = None
-
-        self._treeView = gtk.TreeView(gtk.ListStore(str, str))
-        self._treeView.set_rules_hint(True)
-
-        self.pack_start(self._treeView, False, False, 0)
-
-        self._alignments = []
-        self._checkButtons = []
-
-        self._eventBox = gtk.EventBox()
-
-        self._table = None
-
-        self._viewport = self._createViewport()
-        self._viewport.add(self._eventBox)
-        self._viewport.set_shadow_type(SHADOW_NONE)
-
-        self.pack_start(self._viewport, True, True, 0)
-
-        self._previousWidth = 0
-
-    @property
-    def delayCodes(self):
-        """Get a list of the delay codes checked by the user."""
-        codes = []
-
-        if self._delayCodeData is not None:
-            codeExtractor = self._delayCodeData[0]
-            for checkButton in self._checkButtons:
-                if checkButton.get_active():
-                    codes.append(codeExtractor(checkButton.delayCodeRow))
-
-        return codes
-
-    @property
-    def hasDelayCode(self):
-        """Determine if there is at least one delay code selected."""
-        if self._delayCodeData is not None:
-            for checkButton in self._checkButtons:
-                if checkButton.get_active():
-                    return True
-
-        return False
-
-    def allocate_column_sizes(self, allocation):
-        """Allocate the column sizes."""
-        if allocation.width!=self._previousWidth:
-            self._previousWidth = allocation.width
-            index = 0
-            lastIndex = len(self._alignments) - 1
-            for alignment in self._alignments:
-                column = self._treeView.get_column(index)
-                width = alignment.allocatedWidth
-                width += 8 if (index==0 or index==lastIndex) else 16
-                column.set_fixed_width(width)
-                index += 1
-
-    def setType(self, aircraftType):
-        """Setup the delay code table according to the given aircraft type."""
-        self._delayCodeData = data = getTable(aircraftType)
-        if data is None:
-            return
-
-        columns = self._treeView.get_columns()
-        for column in columns:
-            self._treeView.remove_column(column)
-
-        (_extractor, headers, rows) = data
-        numColumns = len(headers) + 1
-        numRows = len(rows)
-
-        column = gtk.TreeViewColumn("", gtk.CellRendererText())
-        column.set_sizing(TREE_VIEW_COLUMN_FIXED)
-        self._treeView.append_column(column)
-
-        for header in headers:
-            column = gtk.TreeViewColumn(header, gtk.CellRendererText())
-            column.set_sizing(TREE_VIEW_COLUMN_FIXED)
-            self._treeView.append_column(column)
-
-        self._table = gtk.Table(numRows, numColumns)
-        self._table.set_homogeneous(False)
-        self._table.set_col_spacings(16)
-        self._table.set_row_spacings(4)
-        self._eventBox.add(self._table)
-
-        self._alignments = []
-        self._checkButtons = []
-
-        firstDelayCodeRow = True
-        for i in range(0, numRows):
-            (type, elements) = rows[i]
-            if type==CAPTION:
-                alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                          xscale = 1.0)
-                label = gtk.Label("<b>" + elements + "</b>")
-                label.set_use_markup(True)
-                label.set_alignment(0.0, 0.5)
-                alignment.add(label)
-                self._table.attach(alignment, 1, numColumns, i, i+1,
-                                   yoptions = FILL)
-                self._table.set_row_spacing(i, 8)
-            elif type==DELAYCODE:
-                checkButton = CheckButton(elements)
-                checkButton.connect("toggled", self._delayCodesChanged)
-                self._checkButtons.append(checkButton)
-                alignment = Alignment(xalign = 0.5, yalign = 0.5, xscale = 1.0)
-                alignment.add(checkButton)
-                self._table.attach(alignment, 0, 1, i, i+1,
-                                   xoptions = FILL, yoptions = FILL)
-                if firstDelayCodeRow:
-                    self._alignments.append(alignment)
-
-                for j in range(0, numColumns-1):
-                    label = gtk.Label(elements[j])
-                    label.set_alignment(0.0, 0.5)
-                    alignment = Alignment(xalign = 0.5, yalign = 0.5,
-                                          xscale = 1.0)
-                    alignment.add(label)
-                    xoptions = FILL
-                    if j==(numColumns-2): xoptions |= EXPAND
-                    self._table.attach(alignment, j+1, j+2, i, i+1,
-                                       xoptions = xoptions, yoptions = FILL)
-                    if firstDelayCodeRow:
-                        self._alignments.append(alignment)
-                firstDelayCodeRow = False
-
-        self._previousWidth = 0
-        self.show_all()
-
-    def reset(self):
-        """Reset the delay code table."""
-        columns = self._treeView.get_columns()
-        for column in columns:
-            self._treeView.remove_column(column)
-        if self._table is not None:
-            self._eventBox.remove(self._table)
-        self._table = None
-        self.show_all()
-
-    def activateCode(self, code):
-        """Check the checkbox for the given code."""
-        index = 0
-        for (type, data) in self._delayCodeData[2]:
-            if type==DELAYCODE:
-                if code==data[0].strip():
-                    self._checkButtons[index].set_active(True)
-                    break
-                index += 1
-
-    def _delayCodesChanged(self, button):
-        """Called when one of the delay codes have changed."""
-        self._info.delayCodesChanged()
-
-#------------------------------------------------------------------------------
Index: src/mlx/gui/faultexplain.py
===================================================================
--- src/mlx/gui/faultexplain.py	(revision 842)
+++ src/mlx/gui/faultexplain.py	(revision 842)
@@ -0,0 +1,315 @@
+# Module to provide a widget displaying faults that occured during the flight
+# and giving the user some space to explain it
+
+#-------------------------------------------------------------------------------
+
+from mlx.gui.common import *
+
+#-------------------------------------------------------------------------------
+
+## @package mlx.gui.faultexplain
+#
+# The widget and associated logic to display faults and allow the pilot to
+# explain them. Each fault is displayed as the text it is accompanied by in the
+# log and there is a text entry field where the user can enter the
+# corresponding explanation. \ref FaultFrame belongs to one fault, while
+# \ref FaultExplainWidget contains the collection of all frames, which is a
+# VBox in a scrolled window.
+
+#-------------------------------------------------------------------------------
+
+class FaultFrame(gtk.Frame):
+    """A frame containing the information about a single fault.
+
+    It consists of a text view with the text of the fault and an editable text
+    view for the explanation."""
+    def __init__(self, faultText):
+        """Construct the frame."""
+        gtk.Frame.__init__(self)
+
+        self._faultText = faultText
+
+        vbox = gtk.VBox()
+
+        self._fault = fault = gtk.TextView()
+        fault.set_editable(False)
+        fault.set_can_focus(False)
+        fault.set_wrap_mode(WRAP_WORD)
+
+        buffer = fault.get_buffer()
+        self._faultTag  = buffer.create_tag("fault", weight=WEIGHT_BOLD)
+
+        self.faultText = faultText
+
+        faultAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                       xscale = 1.0, yscale = 0.0)
+        faultAlignment.set_padding(padding_top = 0, padding_bottom = 0,
+                                   padding_left = 2, padding_right = 2)
+        faultAlignment.add(fault)
+        vbox.pack_start(faultAlignment, True, True, 4)
+
+        self._explanation = explanation = gtk.TextView()
+        explanation.set_wrap_mode(WRAP_WORD)
+        explanation.set_accepts_tab(False)
+        explanation.set_size_request(-1, 100)
+
+        buffer = explanation.get_buffer()
+        buffer.connect("changed", self._explanationChanged)
+
+        vbox.pack_start(explanation, True, True, 4)
+
+        self.add(vbox)
+        self.show_all()
+
+        if pygobject:
+            styleContext = self.get_style_context()
+            color = styleContext.get_background_color(gtk.StateFlags.NORMAL)
+            fault.override_background_color(0, color)
+        else:
+            style = self.rc_get_style()
+            fault.modify_base(0, style.bg[0])
+
+        self._hasExplanation = False
+
+    @property
+    def faultText(self):
+        """Get the text of the fault."""
+        return self._faultText
+
+    @faultText.setter
+    def faultText(self, faultText):
+        """Update the text of the fault."""
+        self._faultText = faultText
+
+        buffer = self._fault.get_buffer()
+        buffer.set_text(faultText)
+        buffer.apply_tag(self._faultTag,
+                         buffer.get_start_iter(), buffer.get_end_iter())
+
+    @property
+    def explanation(self):
+        """Get the text of the explanation."""
+        buffer = self._explanation.get_buffer()
+        return buffer.get_text(buffer.get_start_iter(),
+                               buffer.get_end_iter(), True)
+
+    @explanation.setter
+    def explanation(self, explanation):
+        """Set the explanation."""
+        self._explanation.get_buffer().set_text(explanation)
+
+    @property
+    def hasExplanation(self):
+        """Determine if there is a valid explanation."""
+        return self._hasExplanation
+
+    @property
+    def html(self):
+        """Convert the contents of the widget into HTML."""
+        return self._faultText + "<br/></b>" + self.explanation + "<b>"
+
+    def setMnemonicFor(self, widget):
+        """Set the explanation text view as the mnemonic widget for the given
+        one."""
+        widget.set_mnemonic_widget(self._explanation)
+
+    def _explanationChanged(self, textBuffer):
+        """Called when the explanation's text has changed."""
+        hasExplanation = len(textBuffer.get_text(textBuffer.get_start_iter(),
+                                                 textBuffer.get_end_iter(),
+                                                 True))>3
+        if self._hasExplanation != hasExplanation:
+            self._hasExplanation = hasExplanation
+            self.emit("explanation-changed", hasExplanation)
+
+#-------------------------------------------------------------------------------
+
+gobject.signal_new("explanation-changed", FaultFrame, gobject.SIGNAL_RUN_FIRST,
+                   None, (bool,))
+
+#-------------------------------------------------------------------------------
+
+class FaultExplainWidget(gtk.Frame):
+    """The widget for the faults and their explanations."""
+    @staticmethod
+    def getFaultFrame(alignment):
+        """Get the fault frame from the given alignment."""
+        return alignment.get_children()[0]
+
+    def __init__(self, gui):
+        gtk.Frame.__init__(self)
+
+        self._gui = gui
+        self.set_label(xstr("info_faults"))
+        label = self.get_label_widget()
+        label.set_use_underline(True)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 4,
+                              padding_left = 4, padding_right = 4)
+
+        self._outerBox = outerBox = gtk.EventBox()
+        outerBox.add(alignment)
+
+        self._innerBox = innerBox = gtk.EventBox()
+        alignment.add(self._innerBox)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 0, padding_bottom = 0,
+                              padding_left = 0, padding_right = 0)
+
+        innerBox.add(alignment)
+
+        scroller = gtk.ScrolledWindow()
+        scroller.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+        scroller.set_shadow_type(SHADOW_NONE)
+
+        self._faults = gtk.VBox()
+        self._faults.set_homogeneous(False)
+        scroller.add_with_viewport(self._faults)
+
+        alignment.add(scroller)
+
+        self._faultWidgets = {}
+
+        self.add(outerBox)
+        self.show_all()
+
+        self._numFaults = 0
+        self._numExplanations = 0
+
+    @property
+    def fullyExplained(self):
+        """Indicate if the faults have been fully explained."""
+        return self._numExplanations>=self._numFaults
+
+    @property
+    def html(self):
+        """Convert the contents of the widget into HTML."""
+        html = ""
+        for alignment in self._faults.get_children():
+            faultFrame = FaultExplainWidget.getFaultFrame(alignment)
+            if html:
+                html += "<br><br>"
+            html += faultFrame.html
+        return html
+
+    def addFault(self, id, faultText):
+        """Add a fault with the given ID and text."""
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                      xscale = 1.0, yscale = 0.0)
+        alignment.set_padding(padding_top = 2, padding_bottom = 2,
+                              padding_left = 4, padding_right = 4)
+
+        faultFrame = FaultFrame(faultText)
+        if self._numFaults==0:
+            faultFrame.setMnemonicFor(self.get_label_widget())
+        faultFrame.connect("explanation-changed", self._explanationChanged)
+
+        alignment.add(faultFrame)
+        self._faults.pack_start(alignment, False, False, 4)
+        self._faults.show_all()
+
+        self._faultWidgets[id] = (alignment, faultFrame)
+
+        self._updateStats(numFaults = self._numFaults + 1)
+
+    def updateFault(self, id, faultText):
+        """Update the text of the fault with the given ID."""
+        self._faultWidgets[id][1].faultText = faultText
+
+    def clearFault(self, id):
+        """Clear (remove) the fault with the given ID."""
+        (alignment, faultFrame) = self._faultWidgets[id]
+        hasExplanation = faultFrame.hasExplanation
+
+        children = self._faults.get_children()
+        if alignment is children[0] and len(children)>1:
+            faultFrame = FaultExplainWidget.getFaultFrame(children[1])
+            faultFrame.setMnemonicFor(self.get_label_widget())
+
+        self._faults.remove(alignment)
+        self._faults.show_all()
+
+        del self._faultWidgets[id]
+
+        self._updateStats(numFaults = self._numFaults - 1,
+                          numExplanations = self._numExplanations -
+                          (1 if hasExplanation else 0))
+
+    def setExplanation(self, id, explanation):
+        """Set the explanation for the fault with the given ID"""
+        self._faultWidgets[id][1].explanation = explanation
+
+    def reset(self):
+        """Reset the widget by removing all faults."""
+        for (alignment, faultFrame) in self._faultWidgets.itervalues():
+            self._faults.remove(alignment)
+        self._faults.show_all()
+
+        self._faultWidgets = {}
+        self._numFaults = self._numExplanations = 0
+        self._setColor()
+
+    def set_sensitive(self, sensitive):
+        """Set the sensitiviy of the widget.
+
+        The outer event box's sensitivity is changed only."""
+        self._outerBox.set_sensitive(sensitive)
+
+    def _updateStats(self, numFaults = None, numExplanations = None):
+        """Update the statistics.
+
+        If the explanation status has changed, emit the corresponding
+        signal."""
+        before = self.fullyExplained
+
+        if numFaults is None:
+            numFaults = self._numFaults
+
+        if numExplanations is None:
+            numExplanations = self._numExplanations
+
+        after = numExplanations >= numFaults
+
+        self._numFaults = numFaults
+        self._numExplanations = numExplanations
+
+        if before!=after:
+            self._setColor()
+            self.emit("explanations-changed", after)
+
+    def _explanationChanged(self, faultFrame, hasExplanation):
+        """Called when the status of an explanation has changed."""
+        self._updateStats(numExplanations = (self._numExplanations +
+                                             (1 if hasExplanation else -1)))
+
+    def _setColor(self):
+        """Set the color to indicate if an unexplained fault is present or
+        not."""
+        allExplained = self._numExplanations >= self._numFaults
+        if pygobject:
+            styleContext = self.get_style_context()
+            if allExplained:
+                outerColour = innerColour = gdk.RGBA(red = 0.0, green=0.0,
+                                                     blue=0.0, alpha=0.0)
+            else:
+                outerColour = \
+                  styleContext.get_background_color(gtk.StateFlags.SELECTED)
+                innerColour = self._gui.backgroundColour
+
+            self._outerBox.override_background_color(gtk.StateFlags.NORMAL,
+                                                     outerColour)
+            self._innerBox.override_background_color(gtk.StateFlags.NORMAL,
+                                                     innerColour)
+        else:
+            style = self.rc_get_style()
+            self._outerBox.modify_bg(0, style.bg[0 if allExplained else 3])
+
+#-------------------------------------------------------------------------------
+
+gobject.signal_new("explanations-changed", FaultExplainWidget,
+                   gobject.SIGNAL_RUN_FIRST, None, (bool,))
Index: src/mlx/gui/faultexplain.py
===================================================================
--- src/mlx/gui/faultexplain.py	(revision 842)
+++ 	(revision )
@@ -1,315 +1,0 @@
-# Module to provide a widget displaying faults that occured during the flight
-# and giving the user some space to explain it
-
-#-------------------------------------------------------------------------------
-
-from mlx.gui.common import *
-
-#-------------------------------------------------------------------------------
-
-## @package mlx.gui.faultexplain
-#
-# The widget and associated logic to display faults and allow the pilot to
-# explain them. Each fault is displayed as the text it is accompanied by in the
-# log and there is a text entry field where the user can enter the
-# corresponding explanation. \ref FaultFrame belongs to one fault, while
-# \ref FaultExplainWidget contains the collection of all frames, which is a
-# VBox in a scrolled window.
-
-#-------------------------------------------------------------------------------
-
-class FaultFrame(gtk.Frame):
-    """A frame containing the information about a single fault.
-
-    It consists of a text view with the text of the fault and an editable text
-    view for the explanation."""
-    def __init__(self, faultText):
-        """Construct the frame."""
-        gtk.Frame.__init__(self)
-
-        self._faultText = faultText
-
-        vbox = gtk.VBox()
-
-        self._fault = fault = gtk.TextView()
-        fault.set_editable(False)
-        fault.set_can_focus(False)
-        fault.set_wrap_mode(WRAP_WORD)
-
-        buffer = fault.get_buffer()
-        self._faultTag  = buffer.create_tag("fault", weight=WEIGHT_BOLD)
-
-        self.faultText = faultText
-
-        faultAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                       xscale = 1.0, yscale = 0.0)
-        faultAlignment.set_padding(padding_top = 0, padding_bottom = 0,
-                                   padding_left = 2, padding_right = 2)
-        faultAlignment.add(fault)
-        vbox.pack_start(faultAlignment, True, True, 4)
-
-        self._explanation = explanation = gtk.TextView()
-        explanation.set_wrap_mode(WRAP_WORD)
-        explanation.set_accepts_tab(False)
-        explanation.set_size_request(-1, 100)
-
-        buffer = explanation.get_buffer()
-        buffer.connect("changed", self._explanationChanged)
-
-        vbox.pack_start(explanation, True, True, 4)
-
-        self.add(vbox)
-        self.show_all()
-
-        if pygobject:
-            styleContext = self.get_style_context()
-            color = styleContext.get_background_color(gtk.StateFlags.NORMAL)
-            fault.override_background_color(0, color)
-        else:
-            style = self.rc_get_style()
-            fault.modify_base(0, style.bg[0])
-
-        self._hasExplanation = False
-
-    @property
-    def faultText(self):
-        """Get the text of the fault."""
-        return self._faultText
-
-    @faultText.setter
-    def faultText(self, faultText):
-        """Update the text of the fault."""
-        self._faultText = faultText
-
-        buffer = self._fault.get_buffer()
-        buffer.set_text(faultText)
-        buffer.apply_tag(self._faultTag,
-                         buffer.get_start_iter(), buffer.get_end_iter())
-
-    @property
-    def explanation(self):
-        """Get the text of the explanation."""
-        buffer = self._explanation.get_buffer()
-        return buffer.get_text(buffer.get_start_iter(),
-                               buffer.get_end_iter(), True)
-
-    @explanation.setter
-    def explanation(self, explanation):
-        """Set the explanation."""
-        self._explanation.get_buffer().set_text(explanation)
-
-    @property
-    def hasExplanation(self):
-        """Determine if there is a valid explanation."""
-        return self._hasExplanation
-
-    @property
-    def html(self):
-        """Convert the contents of the widget into HTML."""
-        return self._faultText + "<br/></b>" + self.explanation + "<b>"
-
-    def setMnemonicFor(self, widget):
-        """Set the explanation text view as the mnemonic widget for the given
-        one."""
-        widget.set_mnemonic_widget(self._explanation)
-
-    def _explanationChanged(self, textBuffer):
-        """Called when the explanation's text has changed."""
-        hasExplanation = len(textBuffer.get_text(textBuffer.get_start_iter(),
-                                                 textBuffer.get_end_iter(),
-                                                 True))>3
-        if self._hasExplanation != hasExplanation:
-            self._hasExplanation = hasExplanation
-            self.emit("explanation-changed", hasExplanation)
-
-#-------------------------------------------------------------------------------
-
-gobject.signal_new("explanation-changed", FaultFrame, gobject.SIGNAL_RUN_FIRST,
-                   None, (bool,))
-
-#-------------------------------------------------------------------------------
-
-class FaultExplainWidget(gtk.Frame):
-    """The widget for the faults and their explanations."""
-    @staticmethod
-    def getFaultFrame(alignment):
-        """Get the fault frame from the given alignment."""
-        return alignment.get_children()[0]
-
-    def __init__(self, gui):
-        gtk.Frame.__init__(self)
-
-        self._gui = gui
-        self.set_label(xstr("info_faults"))
-        label = self.get_label_widget()
-        label.set_use_underline(True)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 4,
-                              padding_left = 4, padding_right = 4)
-
-        self._outerBox = outerBox = gtk.EventBox()
-        outerBox.add(alignment)
-
-        self._innerBox = innerBox = gtk.EventBox()
-        alignment.add(self._innerBox)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 0, padding_bottom = 0,
-                              padding_left = 0, padding_right = 0)
-
-        innerBox.add(alignment)
-
-        scroller = gtk.ScrolledWindow()
-        scroller.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-        scroller.set_shadow_type(SHADOW_NONE)
-
-        self._faults = gtk.VBox()
-        self._faults.set_homogeneous(False)
-        scroller.add_with_viewport(self._faults)
-
-        alignment.add(scroller)
-
-        self._faultWidgets = {}
-
-        self.add(outerBox)
-        self.show_all()
-
-        self._numFaults = 0
-        self._numExplanations = 0
-
-    @property
-    def fullyExplained(self):
-        """Indicate if the faults have been fully explained."""
-        return self._numExplanations>=self._numFaults
-
-    @property
-    def html(self):
-        """Convert the contents of the widget into HTML."""
-        html = ""
-        for alignment in self._faults.get_children():
-            faultFrame = FaultExplainWidget.getFaultFrame(alignment)
-            if html:
-                html += "<br><br>"
-            html += faultFrame.html
-        return html
-
-    def addFault(self, id, faultText):
-        """Add a fault with the given ID and text."""
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                      xscale = 1.0, yscale = 0.0)
-        alignment.set_padding(padding_top = 2, padding_bottom = 2,
-                              padding_left = 4, padding_right = 4)
-
-        faultFrame = FaultFrame(faultText)
-        if self._numFaults==0:
-            faultFrame.setMnemonicFor(self.get_label_widget())
-        faultFrame.connect("explanation-changed", self._explanationChanged)
-
-        alignment.add(faultFrame)
-        self._faults.pack_start(alignment, False, False, 4)
-        self._faults.show_all()
-
-        self._faultWidgets[id] = (alignment, faultFrame)
-
-        self._updateStats(numFaults = self._numFaults + 1)
-
-    def updateFault(self, id, faultText):
-        """Update the text of the fault with the given ID."""
-        self._faultWidgets[id][1].faultText = faultText
-
-    def clearFault(self, id):
-        """Clear (remove) the fault with the given ID."""
-        (alignment, faultFrame) = self._faultWidgets[id]
-        hasExplanation = faultFrame.hasExplanation
-
-        children = self._faults.get_children()
-        if alignment is children[0] and len(children)>1:
-            faultFrame = FaultExplainWidget.getFaultFrame(children[1])
-            faultFrame.setMnemonicFor(self.get_label_widget())
-
-        self._faults.remove(alignment)
-        self._faults.show_all()
-
-        del self._faultWidgets[id]
-
-        self._updateStats(numFaults = self._numFaults - 1,
-                          numExplanations = self._numExplanations -
-                          (1 if hasExplanation else 0))
-
-    def setExplanation(self, id, explanation):
-        """Set the explanation for the fault with the given ID"""
-        self._faultWidgets[id][1].explanation = explanation
-
-    def reset(self):
-        """Reset the widget by removing all faults."""
-        for (alignment, faultFrame) in self._faultWidgets.itervalues():
-            self._faults.remove(alignment)
-        self._faults.show_all()
-
-        self._faultWidgets = {}
-        self._numFaults = self._numExplanations = 0
-        self._setColor()
-
-    def set_sensitive(self, sensitive):
-        """Set the sensitiviy of the widget.
-
-        The outer event box's sensitivity is changed only."""
-        self._outerBox.set_sensitive(sensitive)
-
-    def _updateStats(self, numFaults = None, numExplanations = None):
-        """Update the statistics.
-
-        If the explanation status has changed, emit the corresponding
-        signal."""
-        before = self.fullyExplained
-
-        if numFaults is None:
-            numFaults = self._numFaults
-
-        if numExplanations is None:
-            numExplanations = self._numExplanations
-
-        after = numExplanations >= numFaults
-
-        self._numFaults = numFaults
-        self._numExplanations = numExplanations
-
-        if before!=after:
-            self._setColor()
-            self.emit("explanations-changed", after)
-
-    def _explanationChanged(self, faultFrame, hasExplanation):
-        """Called when the status of an explanation has changed."""
-        self._updateStats(numExplanations = (self._numExplanations +
-                                             (1 if hasExplanation else -1)))
-
-    def _setColor(self):
-        """Set the color to indicate if an unexplained fault is present or
-        not."""
-        allExplained = self._numExplanations >= self._numFaults
-        if pygobject:
-            styleContext = self.get_style_context()
-            if allExplained:
-                outerColour = innerColour = gdk.RGBA(red = 0.0, green=0.0,
-                                                     blue=0.0, alpha=0.0)
-            else:
-                outerColour = \
-                  styleContext.get_background_color(gtk.StateFlags.SELECTED)
-                innerColour = self._gui.backgroundColour
-
-            self._outerBox.override_background_color(gtk.StateFlags.NORMAL,
-                                                     outerColour)
-            self._innerBox.override_background_color(gtk.StateFlags.NORMAL,
-                                                     innerColour)
-        else:
-            style = self.rc_get_style()
-            self._outerBox.modify_bg(0, style.bg[0 if allExplained else 3])
-
-#-------------------------------------------------------------------------------
-
-gobject.signal_new("explanations-changed", FaultExplainWidget,
-                   gobject.SIGNAL_RUN_FIRST, None, (bool,))
Index: src/mlx/gui/flight.py
===================================================================
--- src/mlx/gui/flight.py	(revision 866)
+++ src/mlx/gui/flight.py	(revision 866)
@@ -0,0 +1,5994 @@
+# -*- encoding: utf-8 -*-
+
+from mlx.gui.common import *
+import mlx.gui.cef as cef
+from mlx.gui.flightlist import ColumnDescriptor, FlightList, PendingFlightsWindow
+
+import mlx.const as const
+import mlx.fs as fs
+import mlx.acft as acft
+from mlx.flight import Flight
+from mlx.checks import PayloadChecker
+from mlx.gates import lhbpGates
+import mlx.util as util
+from mlx.pirep import PIREP
+from mlx.i18n import xstr, getLanguage
+from mlx.sound import startSound
+import mlx.web as web
+
+import datetime
+import time
+import os
+import tempfile
+import threading
+import re
+import webbrowser
+
+#-----------------------------------------------------------------------------
+
+## @package mlx.gui.flight
+#
+# The flight "wizard".
+#
+# This module implements the main tab of the application, the flight
+# wizard. The wizard consists of \ref Page "pages", that come one after the
+# other. As some pages might be skipped, the pages dynamically store the index
+# of the previous page so that going back to it is simpler. The \ref
+# Page.activate "activate" function is called before a page is first shown
+# during a flight. This function should initialize the page's controls and fill
+# it with initial data. When a page is left for the first time, its \ref
+# Page.finalize "finalize" function is called. It should set those controls
+# insensitive, that will not be available if the user comes back to this page.
+#
+# Each page has a title at the top displayed in inverted colors and a big
+# font. There is a help text below it centered, that shortly describes what is
+# expected on the page. There can be two help texts: one shown when the page is
+# first displayed during a flight, another shown when the user goes back to the
+# page. The main content area is below this, also centered. Finally, there are
+# some buttons at the bottom on the right. As some buttons occur very
+# frequently, there are functions to add them (\ref Page.addCancelFlightButton
+# "addCancelFlightButton", \ref Page.addPreviousButton "addPreviousButton" and
+# \ref Page.addNextButton "addNextButton".
+#
+# The \ref Wizard class is the main class to collect the pages. It also stores
+# some data passed from one page to another and provides properties to access
+# data items set via the wizard pages.
+
+#-----------------------------------------------------------------------------
+
+comboModel = gtk.ListStore(gobject.TYPE_STRING)
+comboModel.append(("N/A",))
+comboModel.append(("VECTORS",))
+
+#-----------------------------------------------------------------------------
+
+class Page(gtk.Alignment):
+    """A page in the flight wizard."""
+    def __init__(self, wizard, id, title, help, completedHelp = None):
+        """Construct the page."""
+        super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
+                                   xscale = 1.0, yscale = 1.0)
+        self.set_padding(padding_top = 4, padding_bottom = 4,
+                         padding_left = 12, padding_right = 12)
+
+        frame = gtk.Frame()
+        self.add(frame)
+
+        self._vbox = gtk.VBox()
+        self._vbox.set_homogeneous(False)
+        frame.add(self._vbox)
+
+        eventBox = gtk.EventBox()
+
+        alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
+
+        titleLabel = gtk.Label(title)
+        titleLabel.modify_font(pango.FontDescription("bold 24"))
+        alignment.set_padding(padding_top = 4, padding_bottom = 4,
+                              padding_left = 6, padding_right = 0)
+
+        alignment.add(titleLabel)
+        eventBox.add(alignment)
+
+        self._vbox.pack_start(eventBox, False, False, 0)
+
+        self._titleEventBox = eventBox
+        self._titleLabel = titleLabel
+
+        mainBox = gtk.VBox()
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 16, padding_bottom = 16,
+                              padding_left = 16, padding_right = 16)
+        alignment.add(mainBox)
+        self._vbox.pack_start(alignment, True, True, 0)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.set_padding(padding_top = 0, padding_bottom = 16,
+                              padding_left = 0, padding_right = 0)
+
+        self._help = help
+        self._completedHelp = completedHelp
+
+        if self._completedHelp is None or \
+           len(help.splitlines())>=len(completedHelp.splitlines()):
+            longerHelp = help
+        else:
+            longerHelp = completedHelp
+
+        self._helpLabel = gtk.Label(longerHelp)
+        # FIXME: should be a constant in common
+        self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
+                                    else gtk.JUSTIFY_CENTER)
+        self._helpLabel.set_use_markup(True)
+        alignment.add(self._helpLabel)
+        mainBox.pack_start(alignment, False, False, 0)
+
+        self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                            xscale = 1.0, yscale = 1.0)
+        mainBox.pack_start(self._mainAlignment, True, True, 0)
+
+        buttonAlignment =  gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
+        buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
+                                    padding_left = 16, padding_right = 16)
+
+        self._buttonBox = gtk.HBox()
+        self._buttonBox.set_homogeneous(False)
+        self._defaultButton = None
+        buttonAlignment.add(self._buttonBox)
+
+        self._vbox.pack_start(buttonAlignment, False, False, 0)
+
+        self._wizard = wizard
+        self._id = id
+        self._nextPageID = None
+
+        self._cancelFlightButton = None
+
+        self._completed = False
+        self._fromPage = None
+
+    @property
+    def id(self):
+        """Get the identifier of the page."""
+        return self._id
+
+    @property
+    def nextPageID(self):
+        """Get the identifier of the next page, if set."""
+        return self._nextPageID
+
+    @nextPageID.setter
+    def nextPageID(self, nextPageID):
+        """Set the identifier of the next page."""
+        self._nextPageID = nextPageID
+
+    def setMainWidget(self, widget):
+        """Set the given widget as the main one."""
+        self._mainAlignment.add(widget)
+
+    def addButton(self, label, default = False, sensitive = True,
+                  tooltip = None, clicked = None, padding = 4,
+                  clickedArg = None):
+        """Add a button with the given label.
+
+        Return the button object created."""
+        button = gtk.Button(label)
+        self._buttonBox.pack_start(button, False, False, padding)
+        button.set_use_underline(True)
+        if default:
+            button.set_can_default(True)
+            self._defaultButton = button
+        button.set_sensitive(sensitive)
+        if tooltip is not None:
+            button.set_tooltip_text(tooltip)
+        if clicked is not None:
+            if clickedArg is None:
+                button.connect("clicked", clicked)
+            else:
+                button.connect("clicked", clicked, clickedArg)
+        return button
+
+    def addCancelFlightButton(self):
+        """Add the 'Cancel flight' button to the page."""
+        self._cancelFlightButton = \
+            self.addButton(xstr("button_cancelFlight"),
+                           sensitive = True,
+                           tooltip = xstr("button_cancelFlight_tooltip"),
+                           clicked = self._cancelFlight,
+                           padding = 16)
+        return self._cancelFlightButton
+
+    def addPreviousButton(self, sensitive = True, clicked = None):
+        """Add the 'Next' button to the page."""
+        return self.addButton(xstr("button_previous"),
+                              sensitive = sensitive,
+                              tooltip = xstr("button_previous_tooltip"),
+                              clicked = clicked)
+
+    def addNextButton(self, default = True, sensitive = True,
+                      clicked = None):
+        """Add the 'Next' button to the page."""
+        return self.addButton(xstr("button_next"),
+                              default = default,
+                              sensitive = sensitive,
+                              tooltip = xstr("button_next_tooltip"),
+                              clicked = clicked)
+
+    def setStyle(self):
+        """Set the styles of some of the items on the page."""
+        if pygobject:
+            context = self.get_style_context()
+            color = context.get_background_color(gtk.StateFlags.SELECTED)
+            self._titleEventBox.modify_bg(0, color.to_color())
+            color = context.get_color(gtk.StateFlags.SELECTED)
+            self._titleLabel.modify_fg(0, color.to_color())
+        else:
+            style = self.rc_get_style()
+            self._titleEventBox.modify_bg(0, style.bg[3])
+            self._titleLabel.modify_fg(0, style.fg[3])
+
+    def initialize(self):
+        """Initialize the page.
+
+        It sets up the primary help, and calls the activate() function."""
+        self._helpLabel.set_markup(self._help)
+        self._helpLabel.set_sensitive(True)
+        self.activate()
+
+    def activate(self):
+        """Called when this page becomes active.
+
+        This default implementation does nothing."""
+        pass
+
+    def setHelp(self, help):
+        """Set the help string."""
+        self._help = help
+        if not self._completed:
+            self._helpLabel.set_markup(self._help)
+            self._helpLabel.set_sensitive(True)
+
+    def complete(self):
+        """Called when the page is completed.
+
+        It greys out/changes the help text and then calls finalize()."""
+        self.finalize()
+        if self._completedHelp is None:
+            self._helpLabel.set_sensitive(False)
+        else:
+            self._helpLabel.set_markup(self._completedHelp)
+        self._completed = True
+
+    def finalize(self):
+        """Called when the page is finalized."""
+        pass
+
+    def grabDefault(self):
+        """If the page has a default button, make it the default one."""
+        if self._defaultButton is not None:
+            self._defaultButton.grab_default()
+
+    def reset(self):
+        """Reset the page if the wizard is reset."""
+        self._completed = False
+        self._fromPage = None
+        if self._cancelFlightButton is not None:
+            self._cancelFlightButton.set_sensitive(True)
+
+    def goBack(self):
+        """Go to the page we were invoked from."""
+        assert self._fromPage is not None
+
+        self._wizard.setCurrentPage(self._fromPage, finalize = False)
+
+    def flightEnded(self):
+        """Called when the flight has ended.
+
+        This default implementation disables the cancel flight button."""
+        if self._cancelFlightButton is not None:
+            self._cancelFlightButton.set_sensitive(False)
+
+    def _cancelFlight(self, button):
+        """Called when the Cancel flight button is clicked."""
+        self._wizard.gui.cancelFlight()
+
+#-----------------------------------------------------------------------------
+
+class LoginPage(Page):
+    """The login page."""
+    def __init__(self, wizard):
+        """Construct the login page."""
+        super(LoginPage, self).__init__(wizard, "login",
+                                        xstr("login"), xstr("loginHelp"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(3, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(32)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("label_pilotID"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 0, 1)
+
+        self._pilotID = gtk.Entry()
+        self._pilotID.connect("changed", self._pilotIDChanged)
+        self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
+        table.attach(self._pilotID, 1, 2, 0, 1)
+        label.set_mnemonic_widget(self._pilotID)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("label_password"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 1, 2)
+
+        self._password = gtk.Entry()
+        self._password.set_visibility(False)
+        self._password.connect("changed", self._setControls)
+        self._password.set_tooltip_text(xstr("login_password_tooltip"))
+        table.attach(self._password, 1, 2, 1, 2)
+        label.set_mnemonic_widget(self._password)
+
+        self._rememberButton = gtk.CheckButton(xstr("remember_password"))
+        self._rememberButton.set_use_underline(True)
+        self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
+        table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
+
+        self.addButton(xstr("button_login_register"),
+                       clicked = self._registerClicked,
+                       tooltip = xstr("button_login_register_tooltip"))
+
+        self.addButton(xstr("button_offline"),
+                       clicked = self._offlineClicked,
+                       tooltip = xstr("button_offline_tooltip"))
+
+        self._loginButton = self.addButton(xstr("button_login"), default = True)
+        self._loginButton.connect("clicked", self._loginClicked)
+        self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
+
+
+    @property
+    def pilotID(self):
+        """Get the pilot ID, if given."""
+        return self._pilotID.get_text()
+
+    def activate(self):
+        """Activate the page."""
+        config = self._wizard.gui.config
+        self._pilotID.set_text(config.pilotID)
+        self._password.set_text(config.password)
+        self._rememberButton.set_active(config.rememberPassword)
+        self._setControls(None)
+
+    def _pilotIDChanged(self, entry):
+        """Called when the pilot ID has changed.
+
+        It sets the text to upper-case and calls _setControls to update other
+        stuff."""
+        entry.set_text(entry.get_text().upper())
+        self._setControls(entry)
+
+    def _setControls(self, entry = None):
+        """Set the sensitivity of the various controls.
+
+        The login button is sensitive only if both the pilot ID and the
+        password fields contain values.
+
+        The password field is sensitive only, if the entrance exam checkbox is
+        not selected.
+
+        The remember password checkbox is sensitive only, if the password field
+        contains text.
+
+        The entrance exam checkbox is sensitive only, if the pilot ID is not
+        empty."""
+        pilotID = self._pilotID.get_text()
+        password = self._password.get_text()
+        self._rememberButton.set_sensitive(password!="")
+        self._loginButton.set_sensitive(pilotID!="" and password!="")
+
+    def _registerClicked(self, button):
+        """Called when the Register button was clicked."""
+        self._wizard.jumpPage("register")
+
+    def _offlineClicked(self, button):
+        """Called when the offline button was clicked."""
+        print "mlx.flight.LoginPage: offline flight selected"
+        self._wizard.nextPage()
+
+    def _loginClicked(self, button):
+        """Called when the login button was clicked."""
+        print "mlx.flight.LoginPage: logging in"
+        self._wizard.login(self._handleLoginResult,
+                           self._pilotID.get_text(),
+                           self._password.get_text())
+
+    def _handleLoginResult(self, returned, result):
+        """Handle the login result."""
+        self._loginButton.set_sensitive(True)
+        if returned and result.loggedIn:
+            config = self._wizard.gui.config
+
+            config.pilotID = self._pilotID.get_text()
+
+            rememberPassword = self._rememberButton.get_active()
+            config.password = result.password if rememberPassword else ""
+
+            config.rememberPassword = rememberPassword
+
+            config.save()
+            if result.rank=="STU":
+                self._wizard.jumpPage("student")
+            else:
+                self._wizard.nextPage()
+
+#-----------------------------------------------------------------------------
+
+class FlightSelectionPage(Page):
+    """The page to select the flight."""
+    def __init__(self, wizard):
+        """Construct the flight selection page."""
+        help = xstr("flightsel_help")
+        completedHelp = xstr("flightsel_chelp")
+        super(FlightSelectionPage, self).__init__(wizard, "flightsel",
+                                                  xstr("flightsel_title"),
+                                                  help, completedHelp = completedHelp)
+
+        mainBox = gtk.HBox()
+        mainBox.set_homogeneous(False)
+
+        leftVBox = gtk.VBox()
+
+        alignment = gtk.Alignment(xscale = 1.0)
+        alignment.set_size_request(150, 0)
+
+        leftVBox.pack_start(alignment, False, False, 0)
+
+        mainBox.pack_start(leftVBox, True, True, 0)
+
+        self._flightList = FlightList(popupMenuProducer =
+                                      self._createListPopupMenu,
+                                      widthRequest = 400)
+        self._flightList.connect("row-activated", self._rowActivated)
+        self._flightList.connect("selection-changed", self._selectionChanged)
+
+        mainBox.pack_start(self._flightList, False, False, 8)
+
+        flightButtonBox = gtk.VBox()
+
+        alignment = gtk.Alignment(xscale = 1.0)
+        alignment.set_size_request(150, 0)
+        flightButtonBox.pack_start(alignment, False, False, 0)
+
+        flightButtonWidthAlignment = gtk.Alignment(xscale=0.5, yscale=0.0,
+                                                   xalign=0.0, yalign=0.0)
+        flightButtonWidthAlignment.set_padding(padding_top = 0,
+                                               padding_bottom = 0,
+                                               padding_left = 8,
+                                               padding_right = 0)
+        flightButtonWidthBox = gtk.VBox()
+
+        self._saveButton = gtk.Button(xstr("flightsel_save"))
+        self._saveButton.set_use_underline(True)
+        self._saveButton.set_sensitive(False)
+        self._saveButton.set_tooltip_text(xstr("flightsel_save_tooltip"))
+        self._saveButton.connect("clicked", self._saveClicked)
+
+        flightButtonWidthBox.pack_start(self._saveButton, True, True, 4)
+
+        self._printButton = gtk.Button(xstr("flightsel_print"))
+        self._printButton.set_use_underline(True)
+        self._printButton.set_sensitive(False)
+        self._printButton.set_tooltip_text(xstr("flightsel_print_tooltip"))
+        self._printButton.connect("clicked", self._printClicked)
+
+        flightButtonWidthBox.pack_start(self._printButton, True, True, 4)
+
+        self._deleteButton = gtk.Button(xstr("flightsel_delete"))
+        self._deleteButton.set_use_underline(True)
+        self._deleteButton.set_sensitive(False)
+        self._deleteButton.set_tooltip_text(xstr("flightsel_delete_tooltip"))
+        self._deleteButton.connect("clicked", self._deleteClicked)
+
+        flightButtonWidthBox.pack_start(self._deleteButton, True, True, 4)
+
+        flightButtonWidthAlignment.add(flightButtonWidthBox)
+        flightButtonBox.pack_start(flightButtonWidthAlignment, False, False, 0)
+
+        mainBox.pack_start(flightButtonBox, True, True, 0)
+
+        self.setMainWidget(mainBox)
+
+        self._pendingButton = self.addButton(xstr("flightsel_pending"),
+                                             sensitive = False,
+                                             clicked = self._pendingClicked,
+                                             tooltip = xstr("flightsel_pending_tooltip"))
+
+        self._saveDialog = None
+
+        self._refreshButton = self.addButton(xstr("flightsel_refresh"),
+                                             sensitive = True,
+                                             clicked = self._refreshClicked,
+                                             tooltip = xstr("flightsel_refresh_tooltip"))
+
+        self._loadButton = self.addButton(xstr("flightsel_load"),
+                                          sensitive = True,
+                                          tooltip = xstr("flightsel_load_tooltip"))
+        self._loadButton.connect("clicked", self._loadButtonClicked)
+        self._loadDialog = None
+
+        self._button = self.addNextButton(sensitive = False,
+                                          clicked =  self._forwardClicked)
+
+        self._flights = []
+
+        self._pendingFlightsWindow = PendingFlightsWindow(self._wizard)
+        self._pendingFlightsWindowShown = False
+        self._pendingFlightsWindow.connect("delete-event",
+                                           self._deletePendingFlightsWindow)
+
+        self._printSettings = None
+
+    def activate(self):
+        """Fill the flight list."""
+        self._setupHelp()
+        self._flightList.set_sensitive(True)
+        self._loadButton.set_sensitive(True)
+        self._refreshButton.set_sensitive(self._wizard.loggedIn)
+        self._buildFlights()
+
+    def finalize(self):
+        """Finalize the page."""
+        self._flightList.set_sensitive(False)
+        self._loadButton.set_sensitive(False)
+        self._refreshButton.set_sensitive(False)
+
+    def _setupHelp(self):
+        """Setup the help string"""
+        help = ""
+
+        if self._wizard.loggedIn:
+            numReported = len(self._wizard.loginResult.reportedFlights)
+            numRejected = len(self._wizard.loginResult.rejectedFlights)
+            if numReported==0 and numRejected==0:
+                help = xstr("flightsel_prehelp_nopending")
+            elif numReported>0 and numRejected==0:
+                help = xstr("flightsel_prehelp_rep_0rej") % (numReported,)
+            elif numReported==0 and numRejected>0:
+                help = xstr("flightsel_prehelp_0rep_rej") % (numRejected,)
+            else:
+                help = xstr("flightsel_prehelp_rep_rej") % \
+                       (numReported, numRejected)
+
+        help += xstr("flightsel_help")
+
+        self.setHelp(help)
+
+    def _buildFlights(self):
+        """Rebuild the flights from the login result."""
+        self._flights = []
+        self._flightList.clear()
+        self._pendingFlightsWindow.clear()
+        loginResult = self._wizard.loginResult
+        if self._wizard.loggedIn:
+            for flight in loginResult.flights:
+                self.addFlight(flight)
+            for flight in loginResult.reportedFlights:
+                self._pendingFlightsWindow.addReportedFlight(flight)
+            for flight in loginResult.rejectedFlights:
+                self._pendingFlightsWindow.addRejectedFlight(flight)
+
+        self._updatePendingButton()
+
+    def addFlight(self, flight):
+        """Add the given file to the list of flights."""
+        self._flights.append(flight)
+        self._flightList.addFlight(flight)
+
+    def _reflyFlight(self, flight):
+        """Refly the given flight."""
+        self.addFlight(flight)
+        self._updatePending()
+
+    def _updatePending(self):
+        """Update the stuff depending on the set of pending flights."""
+        self._setupHelp()
+        self._updatePendingButton()
+
+    def _pendingClicked(self, button):
+        """Called when the Pending flights button is clicked."""
+        self._pendingFlightsWindow.show_all()
+        self._pendingFlightsWindowShown = True
+        self._updateNextButton()
+
+    def _deletePendingFlightsWindow(self, window, event):
+        """Called when the pending flights window is closed."""
+        self._pendingFlightsWindow.hide()
+        self._pendingFlightsWindowShown = False
+        self._updateNextButton()
+        return True
+
+    def _saveClicked(self, button):
+        """Called when the Save flight button is clicked."""
+        self._saveSelected()
+
+    def _saveSelected(self):
+        """Save the selected flight."""
+        flight = self._getSelectedFlight()
+        date = flight.departureTime.date()
+        name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
+               (date.year, date.month, date.day, flight.callsign,
+                flight.departureICAO, flight.arrivalICAO)
+
+        dialog = self._getSaveDialog()
+        dialog.set_current_name(name)
+        dialog.show_all()
+        response = dialog.run()
+        dialog.hide()
+
+        if response==RESPONSETYPE_OK:
+            fileName = text2unicode(dialog.get_filename())
+            print "Saving", fileName
+            try:
+                with open(fileName, "wt") as f:
+                    flight.writeIntoFile(f)
+            except Exception, e:
+                print "Failed to save flight:", util.utf2unicode(str(e))
+                dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
+                                           type = MESSAGETYPE_ERROR,
+                                           message_format =
+                                           xstr("flightsel_save_failed"))
+                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+                dialog.set_title(WINDOW_TITLE_BASE)
+                secondary = xstr("flightsel_save_failed_sec")
+                dialog.format_secondary_markup(secondary)
+                dialog.run()
+                dialog.hide()
+
+    def _printClicked(self, button):
+        """Called when the Print briefing button is clicked."""
+        self._printSelected()
+
+    def _printSelected(self):
+        """Print the briefing for the selected flight."""
+        wizard = self._wizard
+        flight = self._getSelectedFlight()
+
+        printOperation = gtk.PrintOperation()
+        if self._printSettings is not None:
+            printOperation.set_print_settings(self._printSettings)
+
+        printOperation.set_n_pages(1)
+        printOperation.set_show_progress(True)
+        printOperation.connect("draw_page", self._drawBriefing)
+
+        name = "MAVA Briefing %s %s %s" % (wizard.loginResult.pilotID,
+                                           flight.callsign,
+                                           flight.departureTime.strftime("%Y-%m-%d %H:%M"))
+        printOperation.set_job_name(name)
+        printOperation.set_export_filename(name)
+        printOperation.set_use_full_page(False)
+
+        result = printOperation.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG,
+                                    wizard.gui.mainWindow)
+
+        if result == gtk.PRINT_OPERATION_RESULT_APPLY:
+            self._printSettings = printOperation.get_print_settings()
+        elif result == gtk.PRINT_OPERATION_RESULT_ERROR:
+            errorDialog(xstr("flightsel_print_failed",
+                             wizard.gui.mainWindow,
+                             secondary = printOperation.get_error()))
+
+    def _drawBriefing(self, printOperation, context, pageNumber):
+        """Draw the briefing."""
+        wizard = self._wizard
+        loginResult = wizard.loginResult
+        flight=self._getSelectedFlight()
+
+        print "DPI", context.get_dpi_x(), context.get_dpi_y()
+
+        scale = context.get_dpi_x() / 72.0
+
+        cr = context.get_cairo_context()
+        cr.set_antialias(cairo.ANTIALIAS_GRAY)
+
+        cr.set_source_rgb(0.0, 0.0, 0.0)
+        cr.set_line_width(2.0 * scale)
+        cr.rectangle(0, 0, context.get_width(), context.get_height())
+        cr.stroke()
+
+        layout = cr.create_layout()
+        layout.set_text(u"Malév VA official briefing")
+        font = pango.FontDescription("sans")
+        font.set_size(int(32 * scale * pango.SCALE))
+        font.set_weight(pango.WEIGHT_NORMAL)
+        layout.set_font_description(font)
+
+        (_ink, (x0, y0, x1, y1)) = layout.get_extents()
+        width = float(x1 + 1 - x0) / pango.SCALE
+
+        y = 25 * scale
+
+        cr.move_to((context.get_width() - width)/2.0, y)
+        cr.set_line_width(0.1 * scale)
+        cr.layout_path(layout)
+        cr.stroke_preserve()
+        cr.fill()
+
+        y += float(y1 + 1 - y0) / pango.SCALE
+        y += 6 * scale
+
+        layout = cr.create_layout()
+        layout.set_text(u"%s (%s) részére" %
+                        (loginResult.pilotName, loginResult.pilotID))
+        font = pango.FontDescription("sans")
+        font.set_size(int(16 * scale * pango.SCALE))
+        font.set_weight(450)
+        layout.set_font_description(font)
+        (_ink, (x0, y0, x1, y1)) = layout.get_extents()
+        width = float(x1 + 1 - x0) / pango.SCALE
+
+        cr.move_to((context.get_width() - width)/2.0, y)
+        cr.set_line_width(0.1 * scale)
+        cr.layout_path(layout)
+        cr.stroke_preserve()
+        cr.fill()
+
+        y += float(y1 + 1 - y0) / pango.SCALE
+        y += 4 * scale
+
+        cr.move_to(0, y)
+        cr.line_to(context.get_width(), y)
+        cr.set_line_width(1.0 * scale)
+        cr.stroke()
+
+        y += 20 * scale
+
+        font = pango.FontDescription("sans")
+        font.set_size(int(7 * scale * pango.SCALE))
+        font.set_weight(150)
+
+        table = []
+        table.append(("Flight", flight.callsign))
+        table.append(("Date", flight.date))
+        table.append(("Aircraft",
+                      aircraftNames[flight.aircraftType] + ", Lajstrom: " +
+                      flight.tailNumber))
+        table.append(("DOW",
+                      str(acft.getClass(flight.aircraftType).dow) + " kgs"))
+        table.append(("From", flight.departureICAO))
+        table.append(("To", flight.arrivalICAO))
+        table.append(("ETD (UTC)", flight.departureTime.strftime("%H:%M:%S")))
+        table.append(("ETA (UTC)", flight.arrivalTime.strftime("%H:%M:%S")))
+        table.append(("Crew", str(flight.numCrew)))
+        table.append(("Pass", str(flight.numPassengers)))
+        table.append(("Bag", str(flight.bagWeight)))
+        table.append(("Mail", str(flight.mailWeight)))
+        table.append(("Route", flight.route))
+
+        tableY = y
+        tableX = 15 * scale
+        labelFill = 5 * scale
+        labelValueFill = 25 * scale
+        valueFill = 5 * scale
+        labelX = tableX + labelFill
+        tableWidth = context.get_width() * 55 / 100 - tableX
+
+        labelLayouts = []
+        maxLabelWidth = 0
+        totalHeight = 0
+        for (label, value) in table:
+            labelLayout = cr.create_layout()
+            labelLayout.set_text(label)
+            labelLayout.set_font_description(font)
+
+            (_ink, (x0, y0, x1, y1)) = labelLayout.get_extents()
+            maxLabelWidth = max(maxLabelWidth, x1 + 1 - x0)
+            labelHeight = y1 + 1 - y0
+
+            valueLayout = cr.create_layout()
+            valueLayout.set_text(value)
+            valueLayout.set_font_description(font)
+
+            labelLayouts.append((labelLayout, valueLayout, labelHeight))
+
+        maxLabelWidth = maxLabelWidth / pango.SCALE
+
+        valueX = labelX + labelValueFill + maxLabelWidth
+
+        layouts = []
+        valueWidth = tableWidth - \
+            (labelFill + maxLabelWidth + labelValueFill + valueFill)
+        for (labelLayout, valueLayout, labelHeight) in labelLayouts:
+            valueLayout.set_width(int(valueWidth * pango.SCALE))
+
+            (_ink, (x0, y0, x1, y1)) = valueLayout.get_extents()
+            valueHeight = y1 + 1 - y0
+
+            height = float(max(labelHeight, valueHeight))/pango.SCALE
+            layouts.append((labelLayout, valueLayout, height))
+
+        rowIndex = 0
+        for (labelLayout, valueLayout, height) in layouts:
+            if (rowIndex%2)==0:
+                cr.set_source_rgb(0.85, 0.85, 0.85)
+            else:
+                cr.set_source_rgb(0.9, 0.9, 0.9)
+
+            cr.rectangle(tableX, y-2*scale, tableWidth, height + 4 * scale)
+            cr.fill()
+
+            cr.set_source_rgb(0.0, 0.0, 0.0)
+
+            cr.move_to(labelX, y)
+            cr.set_line_width(0.1)
+            cr.layout_path(labelLayout)
+            cr.stroke_preserve()
+            cr.fill()
+
+            cr.move_to(valueX, y)
+            cr.set_line_width(0.1)
+            cr.layout_path(valueLayout)
+            cr.stroke_preserve()
+            cr.fill()
+
+            y += height
+            y += 4 * scale
+
+            rowIndex += 1
+
+        cr.set_source_rgb(0.0, 0.0, 0.0)
+        cr.set_line_width(1.0 * scale)
+        cr.rectangle(tableX, tableY - 2 * scale, tableWidth, y - tableY)
+        cr.stroke()
+
+        cr.move_to(valueX - 5 * scale, tableY - 2 * scale)
+        cr.line_to(valueX - 5 * scale, y - 2 * scale)
+        cr.stroke()
+
+    def _deleteClicked(self, button):
+        """Called when the Delete flight button is clicked."""
+        self._deleteSelected()
+
+    def _deleteSelected(self):
+        """Delete the selected flight."""
+        if askYesNo(xstr("flightsel_delete_confirm"),
+                    parent = self._wizard.gui.mainWindow):
+            flight = self._getSelectedFlight()
+            gui = self._wizard.gui
+            gui.beginBusy(xstr("flightsel_delete_busy"))
+
+            gui.webHandler.deleteFlights(self._deleteResultCallback,
+                                         [flight.id])
+
+    def _deleteResultCallback(self, returned, result):
+        """Called when the deletion result is available."""
+        gobject.idle_add(self._handleDeleteResult, returned, result)
+
+    def _handleDeleteResult(self, returned, result):
+        """Handle the delete result."""
+        gui = self._wizard.gui
+        gui.endBusy()
+
+        if returned:
+            indexes = self._flightList.selectedIndexes
+
+            flights = [self._flights[index] for index in indexes]
+
+            self._flightList.removeFlights(indexes)
+            for index in indexes[::-1]:
+                del self._flights[index]
+        else:
+            communicationErrorDialog()
+
+
+    def _refreshClicked(self, button):
+        """Called when the refresh button is clicked."""
+        self._wizard.reloadFlights(self._refreshCallback)
+
+    def _refreshCallback(self, returned, result):
+        """Callback for the refresh."""
+        if returned:
+            self._setupHelp()
+            if result.loggedIn:
+                self._buildFlights()
+
+    def _selectionChanged(self, flightList, indexes):
+        """Called when the selection is changed."""
+        self._saveButton.set_sensitive(len(indexes)==1)
+        self._printButton.set_sensitive(len(indexes)==1)
+        self._deleteButton.set_sensitive(len(indexes)==1)
+        self._updateNextButton()
+
+    def _updatePendingButton(self):
+        """Update the senstivity of the Pending button."""
+        self._pendingButton.set_sensitive(self._pendingFlightsWindow.hasFlights)
+
+    def _updateNextButton(self):
+        """Update the sensitivity of the Next button."""
+        sensitive = len(self._flightList.selectedIndexes)==1 and \
+          not self._pendingFlightsWindowShown
+        self._button.set_sensitive(sensitive)
+
+    def _loadButtonClicked(self, loadButton):
+        """Called when the load a flight button is clicked."""
+        dialog = self._getLoadDialog()
+        dialog.show_all()
+        response = dialog.run()
+        dialog.hide()
+
+        if response==RESPONSETYPE_OK:
+            fileName = text2unicode(dialog.get_filename())
+            print "Loading", fileName
+            bookedFlight = web.BookedFlight()
+            try:
+                with open(fileName, "rt") as f:
+                    bookedFlight.readFromFile(f)
+                self.addFlight(bookedFlight)
+            except Exception, e:
+                print "Failed to load flight:", util.utf2unicode(str(e))
+                dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
+                                           type = MESSAGETYPE_ERROR,
+                                           message_format =
+                                           xstr("flightsel_load_failed"))
+                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+                dialog.set_title(WINDOW_TITLE_BASE)
+                secondary = xstr("flightsel_load_failed_sec")
+                dialog.format_secondary_markup(secondary)
+                dialog.run()
+                dialog.hide()
+
+    def _forwardClicked(self, button):
+        """Called when the forward button was clicked."""
+        if self._completed:
+            self._wizard.jumpPage(self._nextID, finalize = False)
+        else:
+            self._flightSelected()
+
+    def _rowActivated(self, flightList, index):
+        """Called when a row is activated."""
+        if not self._completed:
+            self._flightSelected()
+
+    def _flightSelected(self):
+        """Called when a flight has been selected."""
+        flight = self._getSelectedFlight()
+        self._wizard._bookedFlight = flight
+        self._wizard.gui.enableFlightInfo(flight.aircraftType)
+
+        self._updateDepartureGate()
+
+    def _getSelectedFlight(self):
+        """Get the currently selected flight."""
+        indexes = self._flightList.selectedIndexes
+        assert(len(indexes)==1)
+        return self._flights[indexes[0]]
+
+    def _updateDepartureGate(self):
+        """Update the departure gate for the booked flight."""
+        flight = self._wizard._bookedFlight
+        if self._wizard.gui.config.onlineGateSystem and \
+           self._wizard.loggedIn and not self._wizard.entranceExam:
+            if flight.departureICAO=="LHBP":
+                self._wizard.getFleet(self._fleetRetrieved)
+            else:
+                self._wizard.updatePlane(self._planeUpdated,
+                                         flight.tailNumber,
+                                         const.PLANE_AWAY)
+        else:
+            self._nextID = "connect"
+            self._wizard.jumpPage("connect")
+
+    def _fleetRetrieved(self, fleet):
+        """Called when the fleet has been retrieved."""
+        if fleet is None:
+            self._nextID = "connect"
+            self._wizard.jumpPage("connect")
+        else:
+            plane = fleet[self._wizard._bookedFlight.tailNumber]
+            if plane is None:
+                self._nextID = "connect"
+                self._wizard.jumpPage("connect")
+            elif plane.gateNumber is not None and \
+                 not fleet.isGateConflicting(plane):
+                self._wizard._departureGate = plane.gateNumber
+                self._nextID = "connect"
+                self._wizard.jumpPage("connect")
+            else:
+                self._nextID = "gatesel"
+                self._wizard.jumpPage("gatesel")
+
+    def _planeUpdated(self, success):
+        """Callback for the plane updating."""
+        self._nextID = "connect"
+        self._wizard.jumpPage("connect")
+
+    def _getSaveDialog(self):
+        """Get the dialog to load a flight file."""
+        if self._saveDialog is not None:
+            return self._saveDialog
+
+        gui = self._wizard.gui
+        dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
+                                       xstr("flightsel_save_title"),
+                                       action = FILE_CHOOSER_ACTION_SAVE,
+                                       buttons = (gtk.STOCK_CANCEL,
+                                                  RESPONSETYPE_CANCEL,
+                                                  gtk.STOCK_OK, RESPONSETYPE_OK),
+                                       parent = gui.mainWindow)
+        dialog.set_modal(True)
+        dialog.set_do_overwrite_confirmation(True)
+
+        filter = gtk.FileFilter()
+        filter.set_name(xstr("flightsel_filter_flights"))
+        filter.add_pattern("*.vaflight")
+        dialog.add_filter(filter)
+
+        filter = gtk.FileFilter()
+        filter.set_name(xstr("file_filter_all"))
+        filter.add_pattern("*.*")
+        dialog.add_filter(filter)
+
+        self._saveDialog = dialog
+
+        return dialog
+
+    def _getLoadDialog(self):
+        """Get the dialog to load a flight file."""
+        if self._loadDialog is not None:
+            return self._loadDialog
+
+        gui = self._wizard.gui
+        dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
+                                       xstr("flightsel_load_title"),
+                                       action = FILE_CHOOSER_ACTION_OPEN,
+                                       buttons = (gtk.STOCK_CANCEL,
+                                                  RESPONSETYPE_CANCEL,
+                                                  gtk.STOCK_OK, RESPONSETYPE_OK),
+                                       parent = gui.mainWindow)
+        dialog.set_modal(True)
+
+        filter = gtk.FileFilter()
+        filter.set_name(xstr("flightsel_filter_flights"))
+        filter.add_pattern("*.vaflight")
+        dialog.add_filter(filter)
+
+        filter = gtk.FileFilter()
+        filter.set_name(xstr("file_filter_all"))
+        filter.add_pattern("*.*")
+        dialog.add_filter(filter)
+
+        self._loadDialog = dialog
+
+        return dialog
+
+    def _createListPopupMenu(self):
+        """Get the flight list popup menu."""
+        menu = gtk.Menu()
+
+        menuItem = gtk.MenuItem()
+        menuItem.set_label(xstr("flightsel_popup_select"))
+        menuItem.set_use_underline(True)
+        menuItem.connect("activate", self._popupSelect)
+        menuItem.show()
+
+        menu.append(menuItem)
+
+        menuItem = gtk.MenuItem()
+        menuItem.set_label(xstr("flightsel_popup_save"))
+        menuItem.set_use_underline(True)
+        menuItem.connect("activate", self._popupSave)
+        menuItem.show()
+
+        menu.append(menuItem)
+
+        menuItem = gtk.MenuItem()
+        menuItem.set_label(xstr("flightsel_popup_print"))
+        menuItem.set_use_underline(True)
+        menuItem.connect("activate", self._popupPrint)
+        menuItem.show()
+
+        menu.append(menuItem)
+
+        menuItem = gtk.MenuItem()
+        menuItem.set_label(xstr("flightsel_popup_delete"))
+        menuItem.set_use_underline(True)
+        menuItem.connect("activate", self._popupDelete)
+        menuItem.show()
+
+        menu.append(menuItem)
+
+        return menu
+
+    def _popupSelect(self, menuItem):
+        """Called when the Select menu item is activated in the popup menu."""
+        if not self._completed:
+            self._flightSelected()
+
+    def _popupSave(self, menuItem):
+        """Called when the Save menu item is activated in the popup menu."""
+        if not self._completed:
+            self._saveSelected()
+
+    def _popupPrint(self, menuItem):
+        """Called when the Print briefing menu item is activated in the popup menu."""
+        if not self._completed:
+            self._printSelected()
+
+    def _popupDelete(self, menuItem):
+        """Called when the Delete menu item is activated in the popup menu."""
+        if not self._completed:
+            self._deleteSelected()
+
+#-----------------------------------------------------------------------------
+
+class GateSelectionPage(Page):
+    """Page to select a free gate at LHBP.
+    This page should be displayed only if we have fleet information!."""
+    def __init__(self, wizard):
+        """Construct the gate selection page."""
+        super(GateSelectionPage, self).__init__(wizard, "gatesel",
+                                                xstr("gatesel_title"),
+                                                xstr("gatesel_help"))
+
+        self._listStore = gtk.ListStore(str)
+        self._gateList = gtk.TreeView(self._listStore)
+        column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
+                                    text = 0)
+        column.set_expand(True)
+        self._gateList.append_column(column)
+        self._gateList.set_headers_visible(False)
+        self._gateList.connect("row-activated", self._rowActivated)
+
+        gateSelection = self._gateList.get_selection()
+        gateSelection.connect("changed", self._selectionChanged)
+
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.add(self._gateList)
+        scrolledWindow.set_size_request(50, -1)
+        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC,
+                                  gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC)
+        scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
+                                       else gtk.SHADOW_IN)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
+        alignment.add(scrolledWindow)
+
+        self.setMainWidget(alignment)
+
+        self.addCancelFlightButton()
+
+        self.addPreviousButton(clicked = self._backClicked)
+
+        self._button = self.addNextButton(sensitive = False,
+                                          clicked = self._forwardClicked)
+
+    def activate(self):
+        """Fill the gate list."""
+        self._listStore.clear()
+        self._gateList.set_sensitive(True)
+        occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
+        for gate in lhbpGates.gates:
+            if gate.isAvailable(lhbpGates, occupiedGateNumbers):
+                self._listStore.append([gate.number])
+
+    def finalize(self):
+        """Finalize the page."""
+        self._gateList.set_sensitive(False)
+
+    def _selectionChanged(self, selection):
+        """Called when the selection is changed."""
+        self._button.set_sensitive(selection.count_selected_rows()==1)
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        """Called when the forward button is clicked."""
+        if not self._completed:
+            self._gateSelected()
+        else:
+            self._wizard.jumpPage("connect")
+
+    def _rowActivated(self, flightList, path, column):
+        """Called when a row is activated."""
+        if not self._completed:
+            self._gateSelected()
+
+    def _gateSelected(self):
+        """Called when a gate has been selected."""
+        selection = self._gateList.get_selection()
+        (listStore, iter) = selection.get_selected()
+        (gateNumber,) = listStore.get(iter, 0)
+
+        self._wizard._departureGate = gateNumber
+
+        self._wizard.updatePlane(self._planeUpdated,
+                                 self._wizard._bookedFlight.tailNumber,
+                                 const.PLANE_HOME, gateNumber)
+
+    def _planeUpdated(self, success):
+        """Callback for the plane updating call."""
+        if success is None or success:
+            self._wizard.jumpPage("connect")
+        else:
+            dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
+                                       type = MESSAGETYPE_ERROR,
+                                       message_format = xstr("gatesel_conflict"))
+            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+            dialog.set_title(WINDOW_TITLE_BASE)
+            dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
+            dialog.run()
+            dialog.hide()
+
+            self._wizard.getFleet(self._fleetRetrieved)
+
+    def _fleetRetrieved(self, fleet):
+        """Called when the fleet has been retrieved."""
+        if fleet is None:
+            self._wizard.jumpPage("connect")
+        else:
+            self.activate()
+
+#-----------------------------------------------------------------------------
+
+class RegisterPage(Page):
+    """A page to enter the registration data."""
+
+    # The minimal year of birth
+    _minYearOfBirth = 1900
+
+    # The maximal year of birth
+    _maxYearOfBirth = datetime.date.today().year
+
+    # The regular expression to check the e-mail address with
+    _emailAddressRE = re.compile("[^@]+@[^@]+\.[^@]+")
+
+    def __init__(self, wizard):
+        """Construct the registration page."""
+        super(RegisterPage, self).__init__(wizard, "register",
+                                           xstr("register_title"),
+                                           xstr("register_help"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(12, 3)
+        table.set_row_spacings(4)
+        table.set_col_spacings(32)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        row = 0
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_name1"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        self._name1 = gtk.Entry()
+        self._name1.connect("changed", self._updateButtons)
+        self._name1.set_tooltip_text(xstr("register_name1_tooltip"))
+        self._name1.set_width_chars(15)
+        table.attach(self._name1, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._name1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_name2"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        self._name2 = gtk.Entry()
+        self._name2.connect("changed", self._updateButtons)
+        self._name2.set_tooltip_text(xstr("register_name2_tooltip"))
+        self._name2.set_width_chars(15)
+        table.attach(self._name2, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._name2)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_year_of_birth"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+
+        self._yearOfBirth = gtk.SpinButton()
+        self._yearOfBirth.set_increments(step = 1, page = 10)
+        self._yearOfBirth.set_range(min = RegisterPage._minYearOfBirth,
+                                    max = RegisterPage._maxYearOfBirth)
+        self._yearOfBirth.set_numeric(True)
+        self._yearOfBirth.set_tooltip_text(xstr("register_year_of_birth_tooltip"))
+        self._yearOfBirth.set_width_chars(5)
+        self._yearOfBirth.connect("changed", self._updateButtons)
+        self._yearOfBirth.connect("value-changed", self._updateButtons)
+        alignment.add(self._yearOfBirth)
+        table.attach(alignment, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._yearOfBirth)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_email"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        self._emailAddress = gtk.Entry()
+        self._emailAddress.connect("changed", self._updateButtons)
+        self._emailAddress.set_tooltip_text(xstr("register_email_tooltip"))
+        table.attach(self._emailAddress, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._emailAddress)
+
+        self._emailAddressPublic = gtk.CheckButton(xstr("register_email_public"))
+        self._emailAddressPublic.set_use_underline(True)
+        self._emailAddressPublic.set_tooltip_text(xstr("register_email_public_tooltip"))
+        table.attach(self._emailAddressPublic, 2, 3, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_vatsim_id"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+        self._vatsimID = IntegerEntry()
+        self._vatsimID.connect("changed", self._updateButtons)
+        self._vatsimID.set_tooltip_text(xstr("register_vatsim_id_tooltip"))
+        self._vatsimID.set_width_chars(7)
+        alignment.add(self._vatsimID)
+        table.attach(alignment, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._vatsimID)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_ivao_id"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+        self._ivaoID = IntegerEntry()
+        self._ivaoID.connect("changed", self._updateButtons)
+        self._ivaoID.set_tooltip_text(xstr("register_ivao_id_tooltip"))
+        self._ivaoID.set_width_chars(7)
+        alignment.add(self._ivaoID)
+        table.attach(alignment, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._ivaoID)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_phone_num"))
+        label.set_use_underline(True)
+        label.set_use_markup(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        self._phoneNumber = gtk.Entry()
+        self._phoneNumber.set_tooltip_text(xstr("register_phone_num_tooltip"))
+        table.attach(self._phoneNumber, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._phoneNumber)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_nationality"))
+        label.set_use_underline(True)
+        label.set_use_markup(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+
+        self._nationality = gtk.Entry()
+        self._nationality.set_tooltip_text(xstr("register_nationality_tooltip"))
+        table.attach(self._nationality, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._nationality)
+
+        placeholder = gtk.Label()
+        placeholder.set_text(xstr("register_password_mismatch"))
+        placeholder.set_use_markup(True)
+        placeholder.set_child_visible(False)
+        placeholder.hide()
+        table.attach(placeholder, 2, 3, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_password"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        self._password = gtk.Entry()
+        self._password.set_visibility(False)
+        self._password.connect("changed", self._updateButtons)
+        self._password.set_tooltip_text(xstr("register_password_tooltip"))
+        table.attach(self._password, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._password)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        self._passwordStatus = gtk.Label()
+        alignment.add(self._passwordStatus)
+        table.attach(alignment, 2, 3, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_password2"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        self._password2 = gtk.Entry()
+        self._password2.set_visibility(False)
+        self._password2.connect("changed", self._updateButtons)
+        self._password2.set_tooltip_text(xstr("register_password2_tooltip"))
+        table.attach(self._password2, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._password2)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        self._password2Status = gtk.Label()
+        alignment.add(self._password2Status)
+        table.attach(alignment, 2, 3, row, row+1)
+
+        row += 1
+
+        self._rememberButton = gtk.CheckButton(xstr("remember_password"))
+        self._rememberButton.set_use_underline(True)
+        self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
+        table.attach(self._rememberButton, 1, 2, row, row+1)
+
+        cancelButton = \
+          self.addButton(xstr("button_cancel"))
+        cancelButton.connect("clicked", self._cancelClicked)
+
+        self._registerButton = \
+          self.addButton(xstr("button_register"), default = True,
+                              tooltip = xstr("button_register_tooltip"))
+        self._registerButton.connect("clicked", self._registerClicked)
+
+        self._updateButtons()
+
+    @property
+    def name1(self):
+        """Get the first name component entered."""
+        return self._name1.get_text()
+
+    @property
+    def name2(self):
+        """Get the second name component entered."""
+        return self._name2.get_text()
+
+    @property
+    def yearOfBirth(self):
+        """Get the year of birth."""
+        yearOfBirthText = self._yearOfBirth.get_text()
+        return int(yearOfBirthText) if yearOfBirthText else 0
+
+    @property
+    def emailAddress(self):
+        """Get the e-mail address."""
+        return self._emailAddress.get_text()
+
+    @property
+    def emailAddressPublic(self):
+        """Get the whether the e-mail address is public."""
+        return self._emailAddressPublic.get_active()
+
+    @property
+    def vatsimID(self):
+        """Get the VATSIM ID."""
+        return self._vatsimID.get_int()
+
+    @property
+    def ivaoID(self):
+        """Get the IVAO ID."""
+        return self._ivaoID.get_int()
+
+    @property
+    def phoneNumber(self):
+        """Get the phone number."""
+        return self._phoneNumber.get_text()
+
+    @property
+    def nationality(self):
+        """Get the nationality."""
+        return self._nationality.get_text()
+
+    @property
+    def password(self):
+        """Get the password."""
+        return self._password.get_text()
+
+    @property
+    def rememberPassword(self):
+        """Get whether the password should be remembered."""
+        return self._rememberButton.get_active()
+
+    def activate(self):
+        """Setup the route from the booked flight."""
+        self._yearOfBirth.set_value(0)
+        self._yearOfBirth.set_text("")
+        self._updateButtons()
+
+    def _updateButtons(self, widget = None):
+        """Update the sensitive state of the buttons"""
+        yearOfBirth = self.yearOfBirth
+
+        emailAddress = self.emailAddress
+        emailAddressMatch = RegisterPage._emailAddressRE.match(emailAddress)
+
+        vatsimID = self.vatsimID
+        ivaoID = self.ivaoID
+
+        password = self.password
+        password2 = self._password2.get_text()
+        if not password:
+            self._passwordStatus.set_text("")
+        elif len(password)<5:
+            self._passwordStatus.set_text(xstr("register_password_too_short"))
+        else:
+            self._passwordStatus.set_text(xstr("register_password_ok"))
+        self._passwordStatus.set_use_markup(True)
+
+        if len(password)<5 or not password2:
+            self._password2Status.set_text("")
+        elif password!=password2:
+            self._password2Status.set_text(xstr("register_password_mismatch"))
+        else:
+            self._password2Status.set_text(xstr("register_password_ok"))
+        self._password2Status.set_use_markup(True)
+
+        sensitive = \
+            len(self.name1)>0 and len(self.name2)>0 and \
+            yearOfBirth>=RegisterPage._minYearOfBirth and \
+            yearOfBirth<=RegisterPage._maxYearOfBirth and \
+            emailAddressMatch is not None and \
+            (vatsimID>=800000 or ivaoID>=100000) and \
+            len(password)>=5 and password==password2
+
+        self._registerButton.set_sensitive(sensitive)
+
+    def _cancelClicked(self, button):
+        """Called when the Cancel button is clicked."""
+        self.goBack()
+
+    def _registerClicked(self, button):
+        """Called when the Register button is clicked."""
+        nameOrder = xstr("register_nameorder")
+
+        if nameOrder=="eastern":
+            surName = self.name1
+            firstName = self.name2
+        else:
+            surName = self.name2
+            firstName = self.name1
+
+        nationality = self.nationality.lower()
+
+        if getLanguage().lower()=="hu" or nationality.find("hung")!=-1 or \
+           nationality.find("magyar")!=-1:
+            requestedNameOrder = "eastern"
+        else:
+            requestedNameOrder = "western"
+
+        registrationData = web.Registration(surName, firstName,
+                                            requestedNameOrder,
+                                            self.yearOfBirth,
+                                            self.emailAddress,
+                                            self.emailAddressPublic,
+                                            self.vatsimID, self.ivaoID,
+                                            self.phoneNumber, self.nationality,
+                                            self.password)
+        print "Registering with data:"
+        print "  name:", self.name1, self.name2, registrationData.firstName, registrationData.surName, requestedNameOrder
+        print "  yearOfBirth:", self.yearOfBirth, registrationData.yearOfBirth
+        print "  emailAddress:", self.emailAddress, registrationData.emailAddress
+        print "  emailAddressPublic:", self.emailAddressPublic, registrationData.emailAddressPublic
+        print "  vatsimID:", self.vatsimID, registrationData.vatsimID
+        print "  ivaoID:", self.ivaoID, registrationData.ivaoID
+        print "  phoneNumber:", self.phoneNumber, registrationData.phoneNumber
+        print "  nationality:", self.nationality, registrationData.nationality
+
+        gui = self._wizard.gui
+        gui.beginBusy(xstr("register_busy"))
+        gui.webHandler.register(self._registerResultCallback, registrationData)
+
+    def _registerResultCallback(self, returned, result):
+        """Called when the registration result is available."""
+        gobject.idle_add(self._handleRegisterResult, returned, result)
+
+    def _handleRegisterResult(self, returned, result):
+        """Handle the registration result."""
+        gui = self._wizard.gui
+
+        gui.endBusy()
+
+        print "Registration result:"
+        print "  returned:", returned
+        if returned:
+            print "  registered:", result.registered
+            if result.registered:
+                print "  pilotID", result.pilotID
+                print "  loggedIn", result.loggedIn
+            print "  emailAlreadyRegistered:", result.emailAlreadyRegistered
+            print "  invalidData:", result.invalidData
+
+        registrationOK = returned and result.registered
+
+        message = xstr("register_ok") if registrationOK \
+                  else xstr("register_failed")
+        secondaryMessage = None
+        if registrationOK:
+            if result.loggedIn:
+                secondaryMessage = xstr("register_info") % (result.pilotID,)
+            else:
+                secondaryMessage = xstr("register_nologin") % (result.pilotID,)
+            messageType = MESSAGETYPE_INFO
+
+            config = gui.config
+            config.pilotID = result.pilotID
+            config.rememberPassword = self.rememberPassword
+            if config.rememberPassword:
+                config.password = self.password
+            else:
+                config.password = ""
+
+            config.save()
+        elif returned and result.emailAlreadyRegistered:
+            secondaryMessage = xstr("register_email_already")
+            messageType = MESSAGETYPE_ERROR
+        elif returned and result.invalidData:
+            secondaryMessage = xstr("register_invalid_data")
+            messageType = MESSAGETYPE_ERROR
+        else:
+            secondaryMessage = xstr("register_error")
+            messageType = MESSAGETYPE_ERROR
+
+        dialog = gtk.MessageDialog(parent = gui.mainWindow,
+                                   type = messageType,
+                                   message_format = message)
+        dialog.set_title(WINDOW_TITLE_BASE + " - " +
+                         xstr("register_result_title"))
+        dialog.format_secondary_markup(secondaryMessage)
+
+        dialog.add_button(xstr("button_ok"), 0)
+
+        dialog.run()
+        dialog.hide()
+
+        if registrationOK:
+            if result.loggedIn:
+                self._wizard._loginResult = result
+                self._wizard.nextPage()
+            else:
+                self._wizard.jumpPage("login")
+
+#-----------------------------------------------------------------------------
+
+class StudentPage(Page):
+    """A page displayed to students after logging in."""
+    _entryExamStatusQueryInterval = 60*1000
+
+    def __init__(self, wizard):
+        """Construct the student page."""
+        super(StudentPage, self).__init__(wizard, "student",
+                                          xstr("student_title"),
+                                          xstr("student_help"))
+
+
+        self._getEntryExamStatusCancelled = False
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.5, yscale = 0.0)
+
+        table = gtk.Table(6, 4)
+        table.set_row_spacings(4)
+        table.set_col_spacings(0)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        row = 0
+
+        labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
+                                       xscale=0.0, yscale = 0.0)
+        label = gtk.Label(xstr("student_entry_exam_status"))
+        label.set_alignment(0.0, 0.5)
+        labelAlignment.add(label)
+        labelAlignment.resize_children()
+        table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
+
+        alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
+                                  xscale=1.0, yscale = 0.0)
+        self._entryExamStatus = gtk.Label()
+        self._entryExamStatus.set_use_markup(True)
+        self._entryExamStatus.set_alignment(0.0, 0.5)
+        alignment.add(self._entryExamStatus)
+        alignment.resize_children()
+        table.attach(alignment, 1, 4, row, row + 1)
+
+        row += 1
+
+        buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
+        button = self._entryExamButton = gtk.Button(xstr("student_entry_exam"))
+        button.set_use_underline(True)
+        button.connect("clicked", self._entryExamClicked)
+        button.set_tooltip_text(xstr("student_entry_exam_tooltip"))
+
+        buttonAlignment.add(button)
+        table.attach(buttonAlignment, 0, 4, row, row + 1, xoptions = FILL,
+                     ypadding = 4)
+
+        row += 3
+
+        labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
+                                       xscale=0.0, yscale = 0.0)
+        label = gtk.Label(xstr("student_check_flight_status"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
+
+        alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
+                                  xscale=1.0, yscale = 0.0)
+        self._checkFlightStatus = gtk.Label()
+        self._checkFlightStatus.set_use_markup(True)
+        self._checkFlightStatus.set_alignment(0.0, 0.5)
+        alignment.add(self._checkFlightStatus)
+        table.attach(alignment, 1, 4, row, row + 1)
+
+        row += 1
+
+        alignment = gtk.Alignment(xalign=0.0, xscale=1.0)
+
+        hbox = gtk.HBox()
+        hbox.set_homogeneous(False)
+        hbox.set_spacing(0)
+
+        aircraftTypesModel = gtk.ListStore(str, int)
+        for aircraftType in web.BookedFlight.checkFlightTypes:
+            aircraftTypesModel.append([aircraftNames[aircraftType],
+                                       aircraftType])
+
+        aircraftTypeAlignment = gtk.Alignment(xalign = 0.0, xscale = 1.0)
+
+        self._aircraftType = gtk.ComboBox(model = aircraftTypesModel)
+        renderer = gtk.CellRendererText()
+        self._aircraftType.pack_start(renderer, True)
+        self._aircraftType.add_attribute(renderer, "text", 0)
+        self._aircraftType.set_tooltip_text(xstr("student_check_flight_type_tooltip"))
+        self._aircraftType.set_active(0)
+
+        aircraftTypeAlignment.add(self._aircraftType)
+
+        hbox.pack_start(aircraftTypeAlignment, False, False, 0)
+
+        buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
+        button = self._checkFlightButton = gtk.Button(xstr("student_check_flight"))
+        button.set_use_underline(True)
+        button.connect("clicked", self._checkFlightClicked)
+        button.set_tooltip_text(xstr("student_check_flight_tooltip"))
+
+        hbox.pack_start(button, True, True, 0)
+
+        alignment.add(hbox)
+        table.attach(alignment, 0, 4, row, row + 1, xoptions = FILL)
+
+    @property
+    def aircraftType(self):
+        """Get the type of the aircraft used to perform the check flight."""
+        index = self._aircraftType.get_active()
+        return self._aircraftType.get_model()[index][1]
+
+    def activate(self):
+        """Activate the student page."""
+        print "StudentPage.activate"
+        self._getEntryExamStatusCancelled = False
+
+        loginResult = self._wizard.loginResult
+        self._entryExamLink = loginResult.entryExamLink
+
+        self._updateEntryExamStatus(loginResult.entryExamPassed)
+        self._getEntryExamStatus()
+
+        # FIXME: call with real value
+        self._updateCheckFlightStatus(self._wizard.loginResult.checkFlightStatus)
+
+    def finalize(self):
+        """Finalize the page."""
+        print "StudentPage.finalize"
+        self._getEntryExamStatusCancelled = True
+
+    def _entryExamClicked(self, button):
+        """Called when the entry exam button is clicked."""
+        webbrowser.open(self._entryExamLink)
+
+    def _getEntryExamStatus(self):
+        """Initiate the query of the entry exam status after the interval."""
+        if not self._getEntryExamStatusCancelled:
+            gobject.timeout_add(StudentPage._entryExamStatusQueryInterval,
+                                lambda: self._wizard.gui.webHandler. \
+                                getEntryExamStatus(self._entryExamStatusCallback))
+
+    def _entryExamStatusCallback(self, returned, result):
+        """Called when the entry exam status is available."""
+        gobject.idle_add(self._handleEntryExamStatus, returned, result)
+
+    def _handleEntryExamStatus(self, returned, result):
+        """Called when the entry exam status is availabe."""
+        print "_handleEntryExamStatus", returned, result
+        if returned and not self._getEntryExamStatusCancelled:
+            self._entryExamLink = result.entryExamLink
+            self._updateEntryExamStatus(result.entryExamPassed)
+            if result.madeFO:
+                self._madeFO()
+            else:
+                self._getEntryExamStatus()
+
+    def _updateEntryExamStatus(self, passed):
+        """Update the entry exam status display and button."""
+        self._entryExamStatus.set_text(xstr("student_entry_exam_passed")
+                                       if passed else
+                                       xstr("student_entry_exam_not_passed"))
+        self._entryExamStatus.set_use_markup(True)
+        self._entryExamButton.set_sensitive(not passed)
+        self._wizard._loginResult.entryExamPassed = passed
+
+    def _checkFlightClicked(self, button):
+        """Called when the check flight button is clicked."""
+        aircraftType = self.aircraftType
+        self._wizard._bookedFlight = \
+            web.BookedFlight.forCheckFlight(aircraftType)
+        self._wizard.gui.enableFlightInfo(aircraftType)
+        self._wizard.jumpPage("connect")
+
+    def _updateCheckFlightStatus(self, passed):
+        """Update the status of the check flight."""
+        self._aircraftType.set_sensitive(not passed)
+        self._checkFlightStatus.set_text(xstr("student_check_flight_passed")
+                                         if passed else
+                                         xstr("student_check_flight_not_passed"))
+        self._checkFlightStatus.set_use_markup(True)
+        self._checkFlightButton.set_sensitive(not passed)
+
+    def _madeFO(self):
+        """Handle the event when the pilot has become a first officer."""
+        wizard = self._wizard
+        loginResult = wizard.loginResult
+        loginResult.rank = "FO"
+
+        gui = wizard.gui
+
+        dialog = gtk.MessageDialog(parent = gui.mainWindow,
+                                   type = MESSAGETYPE_INFO,
+                                   message_format = xstr("student_fo"))
+
+        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+        dialog.set_title(WINDOW_TITLE_BASE)
+        secondary = xstr("student_fo_secondary")
+        dialog.format_secondary_markup(secondary)
+        dialog.run()
+        dialog.hide()
+
+        gui.reset()
+
+#-----------------------------------------------------------------------------
+
+class ConnectPage(Page):
+    """Page which displays the departure airport and gate (if at LHBP)."""
+    def __init__(self, wizard):
+        """Construct the connect page."""
+        help = "Load the aircraft below into the simulator and park it\n" \
+               "at the given airport, at the gate below, if present.\n\n" \
+               "Then press the Connect button to connect to the simulator."
+        completedHelp = "The basic data of your flight can be read below."
+        super(ConnectPage, self).__init__(wizard, "connect",
+                                          xstr("connect_title"),
+                                          xstr("connect_help"),
+                                          completedHelp = xstr("connect_chelp"))
+
+        self._selectSimulator = os.name=="nt" or "FORCE_SELECT_SIM" in os.environ
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(7 if self._selectSimulator else 5, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(True)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("connect_flightno"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 0, 1)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._flightNumber = gtk.Label()
+        self._flightNumber.set_width_chars(9)
+        self._flightNumber.set_alignment(0.0, 0.5)
+        labelAlignment.add(self._flightNumber)
+        table.attach(labelAlignment, 1, 2, 0, 1)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("connect_acft"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 1, 2)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._aircraft = gtk.Label()
+        self._aircraft.set_width_chars(25)
+        self._aircraft.set_alignment(0.0, 0.5)
+        labelAlignment.add(self._aircraft)
+        table.attach(labelAlignment, 1, 2, 1, 2)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("connect_tailno"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 2, 3)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._tailNumber = gtk.Label()
+        self._tailNumber.set_width_chars(10)
+        self._tailNumber.set_alignment(0.0, 0.5)
+        labelAlignment.add(self._tailNumber)
+        table.attach(labelAlignment, 1, 2, 2, 3)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("connect_airport"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 3, 4)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._departureICAO = gtk.Label()
+        self._departureICAO.set_width_chars(6)
+        self._departureICAO.set_alignment(0.0, 0.5)
+        labelAlignment.add(self._departureICAO)
+        table.attach(labelAlignment, 1, 2, 3, 4)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("connect_gate"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 4, 5)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._departureGate = gtk.Label()
+        self._departureGate.set_width_chars(5)
+        self._departureGate.set_alignment(0.0, 0.5)
+        labelAlignment.add(self._departureGate)
+        table.attach(labelAlignment, 1, 2, 4, 5)
+
+        if self._selectSimulator:
+            labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0, yalign=0.5)
+            label = gtk.Label(xstr("connect_sim"))
+            labelAlignment.add(label)
+            table.attach(labelAlignment, 0, 1, 5, 7)
+
+            selectAlignment = gtk.Alignment(xalign=0.0, xscale=0.0, yalign=0.5)
+
+            selectBox = gtk.HBox()
+            if pygobject:
+                self._selectMSFS = \
+                  gtk.RadioButton.new_with_mnemonic_from_widget(None,
+                                                                xstr("connect_sim_msfs"))
+            else:
+                self._selectMSFS = gtk.RadioButton(None,
+                                                   xstr("connect_sim_msfs"))
+
+            selectBox.pack_start(self._selectMSFS, False, False, 0);
+
+            if pygobject:
+                self._selectXPlane = \
+                  gtk.RadioButton.new_with_mnemonic_from_widget(self._selectMSFS,
+                                                                xstr("connect_sim_xplane"))
+            else:
+                self._selectXPlane = gtk.RadioButton(self._selectMSFS,
+                                                     xstr("connect_sim_xplane"))
+
+            selectBox.pack_start(self._selectXPlane, False, False, 8);
+
+            selectAlignment.add(selectBox)
+            table.attach(selectAlignment, 1, 2, 5, 7)
+
+
+        self.addCancelFlightButton()
+
+        self._previousButton = \
+          self.addPreviousButton(clicked = self._backClicked)
+
+        self._button = self.addButton(xstr("button_connect"), default = True,
+                                      tooltip = xstr("button_connect_tooltip"))
+        self._clickedID = self._button.connect("clicked", self._connectClicked)
+
+    def activate(self):
+        """Setup the departure information."""
+        self._button.set_label(xstr("button_connect"))
+        self._button.set_use_underline(True)
+        self._button.set_tooltip_text(xstr("button_connect_tooltip"))
+        self._button.disconnect(self._clickedID)
+        self._clickedID = self._button.connect("clicked", self._connectClicked)
+
+        bookedFlight = self._wizard._bookedFlight
+
+        self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
+
+        aircraftType = aircraftNames[bookedFlight.aircraftType]
+        self._aircraft.set_markup("<b>" + aircraftType + "</b>")
+
+        self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
+
+        icao = bookedFlight.departureICAO
+        self._departureICAO.set_markup("<b>" + icao + "</b>")
+        gate = self._wizard._departureGate
+        if gate!="-":
+            gate = "<b>" + gate + "</b>"
+        self._departureGate.set_markup(gate)
+
+        if self._selectSimulator:
+            config = self._wizard.gui.config
+            self._selectMSFS.set_active(config.defaultMSFS)
+            self._selectXPlane.set_active(not config.defaultMSFS)
+
+        self._previousButton.set_sensitive(not self._wizard.entranceExam)
+
+    def finalize(self):
+        """Finalize the page."""
+        self._button.set_label(xstr("button_next"))
+        self._button.set_use_underline(True)
+        self._button.set_tooltip_text(xstr("button_next_tooltip"))
+        self._button.disconnect(self._clickedID)
+        self._clickedID = self._button.connect("clicked", self._forwardClicked)
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _connectClicked(self, button):
+        """Called when the Connect button is pressed."""
+        if self._selectSimulator:
+            simulatorType = const.SIM_MSFS9 if self._selectMSFS.get_active() \
+                                            else const.SIM_XPLANE10
+        else:
+            simulatorType = const.SIM_MSFS9 if os.name=="nt" \
+              else const.SIM_XPLANE10
+
+        config = self._wizard.gui.config
+        config.defaultMSFS = simulatorType == const.SIM_MSFS9
+        config.save()
+
+        self._wizard._connectSimulator(simulatorType)
+
+    def _forwardClicked(self, button):
+        """Called when the Forward button is pressed."""
+        self._wizard.nextPage()
+
+#-----------------------------------------------------------------------------
+
+class PayloadPage(Page):
+    """Page to allow setting up the payload."""
+    def __init__(self, wizard):
+        """Construct the page."""
+        super(PayloadPage, self).__init__(wizard, "payload",
+                                          xstr("payload_title"),
+                                          xstr("payload_help"),
+                                          completedHelp = xstr("payload_chelp"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(7, 3)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        label = gtk.Label(xstr("payload_crew"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 0, 1)
+
+        self._numCrew = IntegerEntry(defaultValue = 0)
+        self._numCrew.set_width_chars(6)
+        self._numCrew.connect("integer-changed", self._weightChanged)
+        self._numCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
+        table.attach(self._numCrew, 1, 2, 0, 1)
+        label.set_mnemonic_widget(self._numCrew)
+
+        label = gtk.Label(xstr("payload_pax"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 1, 2)
+
+        self._numPassengers = IntegerEntry(defaultValue = 0)
+        self._numPassengers.set_width_chars(6)
+        self._numPassengers.connect("integer-changed", self._weightChanged)
+        self._numPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
+        table.attach(self._numPassengers, 1, 2, 1, 2)
+        label.set_mnemonic_widget(self._numPassengers)
+
+        label = gtk.Label(xstr("payload_bag"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 2, 3)
+
+        self._bagWeight = IntegerEntry(defaultValue = 0)
+        self._bagWeight.set_width_chars(6)
+        self._bagWeight.connect("integer-changed", self._weightChanged)
+        self._bagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
+        table.attach(self._bagWeight, 1, 2, 2, 3)
+        label.set_mnemonic_widget(self._bagWeight)
+
+        table.attach(gtk.Label("kg"), 2, 3, 2, 3)
+
+        label = gtk.Label(xstr("payload_cargo"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 3, 4)
+
+        self._cargoWeight = IntegerEntry(defaultValue = 0)
+        self._cargoWeight.set_width_chars(6)
+        self._cargoWeight.connect("integer-changed", self._weightChanged)
+        self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
+        table.attach(self._cargoWeight, 1, 2, 3, 4)
+        label.set_mnemonic_widget(self._cargoWeight)
+
+        table.attach(gtk.Label("kg"), 2, 3, 3, 4)
+
+        label = gtk.Label(xstr("payload_mail"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 4, 5)
+
+        self._mailWeight = IntegerEntry(defaultValue = 0)
+        self._mailWeight.set_width_chars(6)
+        self._mailWeight.connect("integer-changed", self._weightChanged)
+        self._mailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
+        table.attach(self._mailWeight, 1, 2, 4, 5)
+        label.set_mnemonic_widget(self._mailWeight)
+
+        table.attach(gtk.Label("kg"), 2, 3, 4, 5)
+
+        label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
+        label.set_alignment(0.0, 0.5)
+        label.set_use_markup(True)
+        table.attach(label, 0, 1, 5, 6)
+
+        self._calculatedZFW = gtk.Label()
+        self._calculatedZFW.set_width_chars(6)
+        self._calculatedZFW.set_alignment(1.0, 0.5)
+        table.attach(self._calculatedZFW, 1, 2, 5, 6)
+
+        table.attach(gtk.Label("kg"), 2, 3, 5, 6)
+
+        self._zfwButton = gtk.Button(xstr("payload_fszfw"))
+        self._zfwButton.set_use_underline(True)
+        self._zfwButton.connect("clicked", self._zfwRequested)
+        self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
+        table.attach(self._zfwButton, 0, 1, 6, 7)
+
+        self._simulatorZFW = gtk.Label("-")
+        self._simulatorZFW.set_width_chars(6)
+        self._simulatorZFW.set_alignment(1.0, 0.5)
+        table.attach(self._simulatorZFW, 1, 2, 6, 7)
+        self._simulatorZFWValue = None
+
+        table.attach(gtk.Label("kg"), 2, 3, 6, 7)
+
+        self.addCancelFlightButton()
+        self._backButton = self.addPreviousButton(clicked = self._backClicked)
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+    @property
+    def numCrew(self):
+        """The number of the crew members on the flight."""
+        return self._numCrew.get_int()
+
+    @property
+    def numPassengers(self):
+        """The number of the passengers on the flight."""
+        return self._numPassengers.get_int()
+
+    @property
+    def bagWeight(self):
+        """Get the bag weight entered."""
+        return self._bagWeight.get_int()
+
+    @property
+    def cargoWeight(self):
+        """Get the cargo weight entered."""
+        return self._cargoWeight.get_int()
+
+    @property
+    def mailWeight(self):
+        """Get the bag weight entered."""
+        return self._mailWeight.get_int()
+
+    def activate(self):
+        """Setup the information."""
+        bookedFlight = self._wizard._bookedFlight
+
+        self._numCrew.set_int(bookedFlight.numCrew)
+        self._numCrew.set_sensitive(True)
+        self._numPassengers.set_int(bookedFlight.numPassengers)
+        self._numPassengers.set_sensitive(True)
+
+        self._bagWeight.set_int(bookedFlight.bagWeight)
+        self._bagWeight.set_sensitive(True)
+        self._cargoWeight.set_int(bookedFlight.cargoWeight)
+        self._cargoWeight.set_sensitive(True)
+        self._mailWeight.set_int(bookedFlight.mailWeight)
+        self._mailWeight.set_sensitive(True)
+
+        self._simulatorZFW.set_text("-")
+        self._simulatorZFWValue = None
+        self._zfwButton.set_sensitive(True)
+        self._updateCalculatedZFW()
+
+    def finalize(self):
+        """Finalize the payload page."""
+        self._numCrew.set_sensitive(False)
+        self._numPassengers.set_sensitive(False)
+        self._bagWeight.set_sensitive(False)
+        self._cargoWeight.set_sensitive(False)
+        self._mailWeight.set_sensitive(False)
+        self._wizard.gui.initializeWeightHelp()
+
+    def calculateZFW(self):
+        """Calculate the ZFW value."""
+        zfw = self._wizard.gui._flight.aircraft.dow
+        zfw += (self._numCrew.get_int() + self._numPassengers.get_int()) * 82
+        zfw += self._bagWeight.get_int()
+        zfw += self._cargoWeight.get_int()
+        zfw += self._mailWeight.get_int()
+        return zfw
+
+    def _updateCalculatedZFW(self):
+        """Update the calculated ZFW"""
+        zfw = self.calculateZFW()
+
+        markupBegin = "<b>"
+        markupEnd = "</b>"
+        if self._simulatorZFWValue is not None and \
+           PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
+            markupBegin += '<span foreground="red">'
+            markupEnd = "</span>" + markupEnd
+        self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
+
+    def _weightChanged(self, entry, weight):
+        """Called when one of the weight values or humanm counts has changed."""
+        self._updateCalculatedZFW()
+
+    def _zfwRequested(self, button):
+        """Called when the ZFW is requested from the simulator."""
+        self._zfwButton.set_sensitive(False)
+        self._backButton.set_sensitive(False)
+        self._button.set_sensitive(False)
+        gui = self._wizard.gui
+        gui.beginBusy(xstr("payload_zfw_busy"))
+        gui.simulator.requestZFW(self._handleZFW)
+
+    def _handleZFW(self, zfw):
+        """Called when the ZFW value is retrieved."""
+        gobject.idle_add(self._processZFW, zfw)
+
+    def _processZFW(self, zfw):
+        """Process the given ZFW value received from the simulator."""
+        self._wizard.gui.endBusy()
+        self._zfwButton.set_sensitive(True)
+        self._backButton.set_sensitive(True)
+        self._button.set_sensitive(True)
+        self._simulatorZFWValue = zfw
+        self._simulatorZFW.set_text("%.0f" % (zfw,))
+        self._updateCalculatedZFW()
+
+    def _forwardClicked(self, button):
+        """Called when the forward button is clicked."""
+        self._wizard.nextPage()
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+#-----------------------------------------------------------------------------
+
+class TimePage(Page):
+    """Page displaying the departure and arrival times and allows querying the
+    current time from the flight simulator."""
+    def __init__(self, wizard):
+        super(TimePage, self).__init__(wizard, "time",
+                                       xstr("time_title"),
+                                       xstr("time_help"),
+                                       completedHelp = xstr("time_chelp"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(3, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        label = gtk.Label(xstr("time_departure"))
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 0, 1)
+
+        self._departure = gtk.Label()
+        self._departure.set_alignment(0.0, 0.5)
+        table.attach(self._departure, 1, 2, 0, 1)
+
+        label = gtk.Label(xstr("time_arrival"))
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 1, 2)
+
+        self._arrival = gtk.Label()
+        self._arrival.set_alignment(0.0, 0.5)
+        table.attach(self._arrival, 1, 2, 1, 2)
+
+        self._timeButton = gtk.Button(xstr("time_fs"))
+        self._timeButton.set_use_underline(True)
+        self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
+        self._timeButton.connect("clicked", self._timeRequested)
+        table.attach(self._timeButton, 0, 1, 2, 3)
+
+        self._simulatorTime = gtk.Label("-")
+        self._simulatorTime.set_alignment(0.0, 0.5)
+        table.attach(self._simulatorTime, 1, 2, 2, 3)
+
+        self.addCancelFlightButton()
+
+        self._backButton = self.addPreviousButton(clicked = self._backClicked)
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+    def activate(self):
+        """Activate the page."""
+        self._timeButton.set_sensitive(True)
+        bookedFlight = self._wizard._bookedFlight
+        self._departure.set_text(str(bookedFlight.departureTime.time()))
+        self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
+        self._simulatorTime.set_text("-")
+
+    def _timeRequested(self, button):
+        """Request the time from the simulator."""
+        self._timeButton.set_sensitive(False)
+        self._backButton.set_sensitive(False)
+        self._button.set_sensitive(False)
+        self._wizard.gui.beginBusy(xstr("time_busy"))
+        self._wizard.gui.simulator.requestTime(self._handleTime)
+
+    def _handleTime(self, timestamp):
+        """Handle the result of a time retrieval."""
+        gobject.idle_add(self._processTime, timestamp)
+
+    def _processTime(self, timestamp):
+        """Process the given time."""
+        self._wizard.gui.endBusy()
+        self._timeButton.set_sensitive(True)
+        self._backButton.set_sensitive(True)
+        self._button.set_sensitive(True)
+        tm = time.gmtime(timestamp)
+        t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
+        self._simulatorTime.set_text(str(t))
+
+        ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
+        dt = self._wizard._bookedFlight.departureTime.time()
+        dts = dt.hour * 3600 + dt.minute * 60 + dt.second
+        diff = dts-ts
+
+        markupBegin = ""
+        markupEnd = ""
+        if diff < 0:
+            markupBegin = '<b><span foreground="red">'
+            markupEnd = '</span></b>'
+        elif diff < 3*60 or diff > 30*60:
+            markupBegin = '<b><span foreground="orange">'
+            markupEnd = '</span></b>'
+
+        self._departure.set_markup(markupBegin + str(dt) + markupEnd)
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        """Called when the forward button is clicked."""
+        if not self._completed:
+            gui = self._wizard.gui
+            gui.beginBusy(xstr("fuel_get_busy"))
+
+            gui.simulator.getFuel(self._handleFuel)
+        else:
+            self._wizard.nextPage()
+
+    def _handleFuel(self, fuelData):
+        """Callback for the fuel query operation."""
+        gobject.idle_add(self._processFuel, fuelData)
+
+    def _processFuel(self, fuelData):
+        """Process the given fuel data."""
+        self._wizard.gui.endBusy()
+        self._wizard._fuelData = fuelData
+        self._wizard.nextPage()
+
+#-----------------------------------------------------------------------------
+
+class RoutePage(Page):
+    """The page containing the route and the flight level."""
+    def __init__(self, wizard):
+        """Construct the page."""
+        super(RoutePage, self).__init__(wizard, "route",
+                                        xstr("route_title"),
+                                        xstr("route_help"),
+                                        completedHelp = xstr("route_chelp"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        mainBox = gtk.VBox()
+        alignment.add(mainBox)
+        self.setMainWidget(alignment)
+
+        levelBox = gtk.HBox()
+
+        label = gtk.Label(xstr("route_level"))
+        label.set_use_underline(True)
+        levelBox.pack_start(label, True, True, 0)
+
+        self._cruiseLevel = gtk.SpinButton()
+        self._cruiseLevel.set_increments(step = 10, page = 100)
+        self._cruiseLevel.set_range(min = 0, max = 500)
+        self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
+        self._cruiseLevel.set_numeric(True)
+        self._cruiseLevel.connect("changed", self._cruiseLevelChanged)
+        self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
+        label.set_mnemonic_widget(self._cruiseLevel)
+
+        levelBox.pack_start(self._cruiseLevel, False, False, 8)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(levelBox)
+
+        mainBox.pack_start(alignment, False, False, 0)
+
+
+        routeBox = gtk.VBox()
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("route_route"))
+        label.set_use_underline(True)
+        alignment.add(label)
+        routeBox.pack_start(alignment, True, True, 0)
+
+        routeWindow = gtk.ScrolledWindow()
+        routeWindow.set_size_request(400, 80)
+        routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
+                                    else gtk.SHADOW_IN)
+        routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
+                               else gtk.POLICY_AUTOMATIC,
+                               gtk.PolicyType.AUTOMATIC if pygobject
+                               else gtk.POLICY_AUTOMATIC)
+
+        self._uppercasingRoute = False
+
+        self._route = gtk.TextView()
+        self._route.set_tooltip_text(xstr("route_route_tooltip"))
+        self._route.set_wrap_mode(WRAP_WORD)
+        self._route.get_buffer().connect("changed", self._routeChanged)
+        self._route.get_buffer().connect_after("insert-text", self._routeInserted)
+        routeWindow.add(self._route)
+
+        label.set_mnemonic_widget(self._route)
+        routeBox.pack_start(routeWindow, True, True, 0)
+
+        mainBox.pack_start(routeBox, True, True, 8)
+
+        alternateBox = gtk.HBox()
+
+        label = gtk.Label(xstr("route_altn"))
+        label.set_use_underline(True)
+        alternateBox.pack_start(label, True, True, 0)
+
+        self._alternate = gtk.Entry()
+        self._alternate.set_width_chars(6)
+        self._alternate.connect("changed", self._alternateChanged)
+        self._alternate.set_tooltip_text(xstr("route_altn_tooltip"))
+        label.set_mnemonic_widget(self._alternate)
+
+        alternateBox.pack_start(self._alternate, False, False, 8)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(alternateBox)
+
+        mainBox.pack_start(alignment, False, False, 0)
+
+        self.addCancelFlightButton()
+
+        self._backButton = self.addPreviousButton(clicked = self._backClicked)
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+    @property
+    def filedCruiseLevel(self):
+        """Get the filed cruise level."""
+        return self._cruiseLevel.get_value_as_int()
+
+    @property
+    def route(self):
+        """Get the route."""
+        return self._getRoute()
+
+    @property
+    def alternate(self):
+        """Get the ICAO code of the alternate airport."""
+        return self._alternate.get_text()
+
+    def activate(self):
+        """Setup the route from the booked flight."""
+        self._cruiseLevel.set_value(0)
+        self._cruiseLevel.set_text("")
+        self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
+        self._alternate.set_text("")
+        self._updateForwardButton()
+
+    def _getRoute(self):
+        """Get the text of the route."""
+        buffer = self._route.get_buffer()
+        return buffer.get_text(buffer.get_start_iter(),
+                               buffer.get_end_iter(), True)
+
+    def _updateForwardButton(self):
+        """Update the sensitivity of the forward button."""
+        cruiseLevelText = self._cruiseLevel.get_text()
+        cruiseLevel = int(cruiseLevelText) if cruiseLevelText else 0
+        alternate = self._alternate.get_text()
+        self._button.set_sensitive(cruiseLevel>=50 and self._getRoute()!="" and
+                                   (len(alternate)==4 or self._wizard.entranceExam))
+
+    def _cruiseLevelChanged(self, *arg):
+        """Called when the cruise level has changed."""
+        self._updateForwardButton()
+
+    def _routeChanged(self, textBuffer):
+        """Called when the route has changed."""
+        if not self._uppercasingRoute:
+            self._updateForwardButton()
+
+    def _routeInserted(self, textBuffer, iter, text, length):
+        """Called when new characters are inserted into the route.
+
+        It uppercases all characters."""
+        if not self._uppercasingRoute:
+            self._uppercasingRoute = True
+
+            iter1 = iter.copy()
+            iter1.backward_chars(length)
+            textBuffer.delete(iter, iter1)
+
+            textBuffer.insert(iter, text.upper())
+
+            self._uppercasingRoute = False
+
+    def _alternateChanged(self, entry):
+        """Called when the alternate airport has changed."""
+        entry.set_text(entry.get_text().upper())
+        self._updateForwardButton()
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        """Called when the Forward button is clicked."""
+        if self._wizard.gui.flight.aircraft.simBriefData is None:
+            self._wizard.usingSimBrief = False
+        if self._wizard.gui.config.useSimBrief and \
+           self._wizard.usingSimBrief is not False:
+            self._wizard.jumpPage("simbrief_setup")
+        else:
+            self._wizard.usingSimBrief = False
+            self._wizard.jumpPage("fuel")
+
+#-----------------------------------------------------------------------------
+
+class SimBriefCredentialsDialog(gtk.Dialog):
+    """A dialog window to ask for SimBrief credentials."""
+    def __init__(self, gui, userName, password, rememberPassword):
+        """Construct the dialog."""
+        super(SimBriefCredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
+                                                        xstr("simbrief_credentials_title"),
+                                                        gui.mainWindow,
+                                                        DIALOG_MODAL)
+        self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
+        self.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+
+        contentArea = self.get_content_area()
+
+        contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                         xscale = 0.0, yscale = 0.0)
+        contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
+                                     padding_left = 8, padding_right = 8)
+
+        contentArea.pack_start(contentAlignment, False, False, 0)
+
+        contentVBox = gtk.VBox()
+        contentAlignment.add(contentVBox)
+
+        label = gtk.Label(xstr("simbrief_login_failed"))
+        label.set_alignment(0.0, 0.0)
+
+        contentVBox.pack_start(label, False, False, 0)
+
+        tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
+                                   padding_left = 0, padding_right = 0)
+
+        table = gtk.Table(3, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+
+        tableAlignment.add(table)
+        contentVBox.pack_start(tableAlignment, True, True, 0)
+
+        label = gtk.Label(xstr("simbrief_username"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 0, 1)
+
+        self._userName = gtk.Entry()
+        self._userName.set_width_chars(16)
+        #self._userName.connect("changed",
+        #                       lambda button: self._updateForwardButton())
+        self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
+        self._userName.set_text(userName)
+        table.attach(self._userName, 1, 2, 0, 1)
+        label.set_mnemonic_widget(self._userName)
+
+        label = gtk.Label(xstr("simbrief_password"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 1, 2)
+
+        self._password = gtk.Entry()
+        self._password.set_visibility(False)
+        #self._password.connect("changed",
+        #                       lambda button: self._updateForwardButton())
+        self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
+        self._password.set_text(password)
+        table.attach(self._password, 1, 2, 1, 2)
+        label.set_mnemonic_widget(self._password)
+
+        self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
+        self._rememberButton.set_use_underline(True)
+        self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
+        self._rememberButton.set_active(rememberPassword)
+        table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
+
+    @property
+    def userName(self):
+        """Get the user name entered."""
+        return self._userName.get_text()
+
+    @property
+    def password(self):
+        """Get the password entered."""
+        return self._password.get_text()
+
+    @property
+    def rememberPassword(self):
+        """Get whether the password is to be remembered."""
+        return self._rememberButton.get_active()
+
+    def run(self):
+        """Run the dialog."""
+        self.show_all()
+
+        response = super(SimBriefCredentialsDialog, self).run()
+
+        self.hide()
+
+        return response
+
+#-----------------------------------------------------------------------------
+
+class SimBriefSetupPage(Page):
+    """Page for setting up some parameters for SimBrief."""
+    monthNum2Name = [
+        "JAN",
+        "FEB",
+        "MAR",
+        "APR",
+        "MAY",
+        "JUN",
+        "JUL",
+        "AUG",
+        "SEP",
+        "OCT",
+        "NOV",
+        "DEC"
+        ]
+
+    progress2Message = {
+        cef.SIMBRIEF_PROGRESS_SEARCHING_BROWSER: "simbrief_progress_searching_browser",
+        cef.SIMBRIEF_PROGRESS_LOADING_FORM: "simbrief_progress_loading_form",
+        cef.SIMBRIEF_PROGRESS_FILLING_FORM: "simbrief_progress_filling_form",
+        cef.SIMBRIEF_PROGRESS_WAITING_LOGIN: "simbrief_progress_waiting_login",
+        cef.SIMBRIEF_PROGRESS_LOGGING_IN: "simbrief_progress_logging_in",
+        cef.SIMBRIEF_PROGRESS_WAITING_RESULT: "simbrief_progress_waiting_result",
+        cef.SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING: "simbrief_progress_retrieving_briefing"
+        }
+
+    result2Message = {
+        cef.SIMBRIEF_RESULT_ERROR_OTHER: "simbrief_result_error_other",
+        cef.SIMBRIEF_RESULT_ERROR_NO_FORM: "simbrief_result_error_no_form",
+        cef.SIMBRIEF_RESULT_ERROR_NO_POPUP: "simbrief_result_error_no_popup",
+        cef.SIMBRIEF_RESULT_ERROR_LOGIN_FAILED: "simbrief_result_error_login_failed"
+        }
+
+    @staticmethod
+    def getHTMLFilePath():
+        """Get the path of the HTML file to contain the generated flight
+        plan."""
+        if os.name=="nt":
+            return os.path.join(tempfile.gettempdir(),
+                                "mlx_simbrief" +
+                                (".secondary" if secondaryInstallation else "") +
+                                ".html")
+        else:
+            import pwd
+            return os.path.join(tempfile.gettempdir(),
+                                "mlx_simbrief." + pwd.getpwuid(os.getuid())[0] + "" +
+                                (".secondary" if secondaryInstallation else "") +
+                                ".html")
+
+    def __init__(self, wizard):
+        """Construct the setup page."""
+
+        super(SimBriefSetupPage, self).__init__(wizard, "simbrief_setup",
+                                                xstr("simbrief_setup_title"),
+                                                xstr("simbrief_setup_help"),
+                                                xstr("simbrief_setup_chelp"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(9, 3)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        label = gtk.Label(xstr("simbrief_username"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 0, 1)
+
+        self._userName = gtk.Entry()
+        self._userName.set_width_chars(16)
+        self._userName.connect("changed",
+                               lambda button: self._updateForwardButton())
+        self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
+        table.attach(self._userName, 1, 2, 0, 1)
+        label.set_mnemonic_widget(self._userName)
+
+        label = gtk.Label(xstr("simbrief_password"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 1, 2)
+
+        self._password = gtk.Entry()
+        self._password.set_visibility(False)
+        self._password.connect("changed",
+                               lambda button: self._updateForwardButton())
+        self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
+        table.attach(self._password, 1, 2, 1, 2)
+        label.set_mnemonic_widget(self._password)
+
+        self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
+        self._rememberButton.set_use_underline(True)
+        self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
+        table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
+
+        label = gtk.Label(xstr("simbrief_extra_fuel"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 3, 4)
+
+        self._extraFuel = IntegerEntry(defaultValue = 0)
+        self._extraFuel.set_width_chars(6)
+        self._extraFuel.set_tooltip_text(xstr("simbrief_extra_fuel_tooltip"))
+        table.attach(self._extraFuel, 1, 2, 3, 4)
+        label.set_mnemonic_widget(self._extraFuel)
+
+        table.attach(gtk.Label("kg"), 2, 3, 3, 4)
+
+        label = gtk.Label(xstr("simbrief_takeoff_runway"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 4, 5)
+
+        self._takeoffRunway = gtk.Entry()
+        self._takeoffRunway.set_width_chars(10)
+        self._takeoffRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
+        self._takeoffRunway.connect("changed", self._upperChanged)
+        table.attach(self._takeoffRunway, 1, 2, 4, 5)
+        label.set_mnemonic_widget(self._takeoffRunway)
+
+        label = gtk.Label(xstr("simbrief_landing_runway"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 5, 6)
+
+        self._landingRunway = gtk.Entry()
+        self._landingRunway.set_width_chars(10)
+        self._landingRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
+        self._landingRunway.connect("changed", self._upperChanged)
+        table.attach(self._landingRunway, 1, 2, 5, 6)
+        label.set_mnemonic_widget(self._landingRunway)
+
+        label = gtk.Label(xstr("simbrief_climb_profile"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 6, 7)
+
+        self._climbProfile = gtk.ComboBox()
+        renderer = gtk.CellRendererText()
+        self._climbProfile.pack_start(renderer, True)
+        self._climbProfile.add_attribute(renderer, "text", 0)
+        self._climbProfile.set_tooltip_text(xstr("simbrief_climb_profile_tooltip"))
+        table.attach(self._climbProfile, 1, 2, 6, 7)
+        label.set_mnemonic_widget(self._climbProfile)
+
+        label = gtk.Label(xstr("simbrief_cruise_profile"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 7, 8)
+
+        self._cruiseProfile = gtk.ComboBox()
+        renderer = gtk.CellRendererText()
+        self._cruiseProfile.pack_start(renderer, True)
+        self._cruiseProfile.add_attribute(renderer, "text", 0)
+        self._cruiseProfile.set_tooltip_text(xstr("simbrief_cruise_profile_tooltip"))
+        table.attach(self._cruiseProfile, 1, 2, 7, 8)
+        label.set_mnemonic_widget(self._cruiseProfile)
+
+        label = gtk.Label(xstr("simbrief_descent_profile"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 8, 9)
+
+        self._descentProfile = gtk.ComboBox()
+        renderer = gtk.CellRendererText()
+        self._descentProfile.pack_start(renderer, True)
+        self._descentProfile.add_attribute(renderer, "text", 0)
+        self._descentProfile.set_tooltip_text(xstr("simbrief_descent_profile_tooltip"))
+        table.attach(self._descentProfile, 1, 2, 8, 9)
+        label.set_mnemonic_widget(self._descentProfile)
+
+        self.addCancelFlightButton()
+
+        self._backButton = self.addPreviousButton(clicked = self._backClicked)
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+    def activate(self):
+        """Activate the SimBrief setup page"""
+        config = self._wizard.gui.config
+
+        self._userName.set_text(config.simBriefUserName)
+        self._userName.set_sensitive(True)
+
+        self._password.set_text(config.simBriefPassword)
+        self._password.set_sensitive(True)
+
+        self._rememberButton.set_active(config.rememberSimBriefPassword)
+        self._rememberButton.set_sensitive(True)
+
+        self._extraFuel.set_int(0)
+        self._extraFuel.set_sensitive(True)
+
+        self._takeoffRunway.set_text("")
+        self._takeoffRunway.set_sensitive(True)
+
+        self._landingRunway.set_text("")
+        self._landingRunway.set_sensitive(True)
+
+        simBriefData = self._wizard.gui.flight.aircraft.simBriefData
+        for (control, profiles) in [(self._climbProfile,
+                                     simBriefData.climbProfiles),
+                                    (self._cruiseProfile,
+                                     simBriefData.cruiseProfiles),
+                                    (self._descentProfile,
+                                     simBriefData.descentProfiles)]:
+            model = gtk.ListStore(str)
+            for profile in profiles:
+                model.append([profile])
+            control.set_model(model)
+            control.set_sensitive(True)
+
+        self._climbProfile.set_active(0)
+        self._cruiseProfile.set_active(0)
+        self._descentProfile.set_active(0)
+
+        self._updateForwardButton()
+
+    def _updateForwardButton(self):
+        """Update the sensitivity of the forward button."""
+        self._button.set_sensitive(len(self._userName.get_text())>0 and
+                                   len(self._password.get_text())>0)
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        if self._completed:
+            self._wizard.nextPage()
+        else:
+            config = self._wizard.gui.config
+
+            config.simBriefUserName = self._userName.get_text()
+
+            rememberPassword = self._rememberButton.get_active()
+            config.simBriefPassword = \
+              self._password.get_text() if rememberPassword else ""
+            config.rememberSimBriefPassword = rememberPassword
+
+            config.save()
+
+            plan = self._getPlan()
+            print "plan:", plan
+
+            takeoffRunway = self._takeoffRunway.get_text()
+            if takeoffRunway:
+                self._wizard.takeoffRunway = takeoffRunway
+
+            landingRunway = self._landingRunway.get_text()
+            if landingRunway:
+                self._wizard.landingRunway = landingRunway
+
+            self._userName.set_sensitive(False)
+            self._password.set_sensitive(False)
+            self._rememberButton.set_sensitive(False)
+            self._extraFuel.set_sensitive(False)
+            self._takeoffRunway.set_sensitive(False)
+            self._landingRunway.set_sensitive(False)
+
+            self._climbProfile.set_sensitive(False)
+            self._cruiseProfile.set_sensitive(False)
+            self._descentProfile.set_sensitive(False)
+
+            self._wizard.gui.beginBusy(xstr("simbrief_calling"))
+
+            cef.callSimBrief(plan,
+                             self._getCredentials,
+                             self._simBriefProgress,
+                             SimBriefSetupPage.getHTMLFilePath())
+
+            startSound(const.SOUND_NOTAM)
+
+    def _getCredentials(self, count):
+        """Get the credentials.
+
+        If count is 0, the user name and password entered into the setup page
+        are returned. Otherwise a dialog box is displayed informing the user of
+        invalid credentials and requesting another set of them."""
+        print "_getCredentials", count
+        if count==0:
+            return (self._userName.get_text(), self._password.get_text())
+        else:
+            gui = self._wizard.gui
+            config = gui.config
+
+            dialog = SimBriefCredentialsDialog(gui,
+                                               config.simBriefUserName,
+                                               config.simBriefPassword,
+                                               config.rememberSimBriefPassword)
+            response = dialog.run()
+
+            if response==RESPONSETYPE_OK:
+                userName = dialog.userName
+                self._userName.set_text(userName)
+                password = dialog.password
+                self._password.set_text(password)
+                rememberPassword = dialog.rememberPassword
+
+                config.simBriefUserName = userName
+
+                config.simBriefPassword = \
+                    password if rememberPassword else ""
+                config.rememberSimBriefPassword = rememberPassword
+
+                config.save()
+
+                return (userName, password)
+            else:
+                return (None, None)
+
+    def _simBriefProgress(self, progress, result, flightInfo):
+        """The real SimBrief progress handler."""
+        print "_simBriefProgress", progress, result, flightInfo
+        if result==cef.SIMBRIEF_RESULT_NONE:
+            message = SimBriefSetupPage.progress2Message.get(progress,
+                                                             "simbrief_progress_unknown")
+            self._wizard.gui.updateBusyState(xstr(message))
+        else:
+            self._wizard.gui.endBusy()
+
+            if result==cef.SIMBRIEF_RESULT_OK:
+                self._wizard.departureMETARChanged(flightInfo["orig_metar"],
+                                                   self)
+                self._wizard.arrivalMETARChanged(flightInfo["dest_metar"], self)
+                self._wizard.nextPage()
+            else:
+                message = SimBriefSetupPage.result2Message.get(result,
+                                                               "simbrief_result_unknown")
+                dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
+                                           type = MESSAGETYPE_ERROR,
+                                           message_format =
+                                           xstr(message) + "\n"+
+                                           xstr("simbrief_cancelled"))
+
+                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+                dialog.set_title(WINDOW_TITLE_BASE)
+                secondary = xstr("flightsel_save_failed_sec")
+                dialog.format_secondary_markup(secondary)
+                dialog.run()
+                dialog.hide()
+
+                self._wizard.usingSimBrief = False
+                self._wizard.jumpPage("fuel", fromPageShift = 1)
+
+    def _getPlan(self):
+        """Get the flight plan data for SimBrief."""
+        plan = {
+            "airline": "MAH",
+            "selcal": "XXXX",
+            "fuelfactor": "P000",
+            "contpct": "0.05",
+            "resvrule": "45",
+            "taxiout": "10",
+            "taxiin": "10",
+            "civalue": "AUTO"
+            }
+
+        wizard = self._wizard
+        gui = wizard.gui
+
+        loginResult = wizard.loginResult
+        plan["cpt"] = loginResult.pilotName
+        plan["pid"] = loginResult.pilotID
+
+        bookedFlight = wizard.bookedFlight
+        plan["fltnum"] = wizard.bookedFlight.callsign[2:]
+        plan["type"] = const.icaoCodes[bookedFlight.aircraftType]
+        plan["orig"] = bookedFlight.departureICAO
+        plan["dest"] = bookedFlight.arrivalICAO
+        plan["reg"] = bookedFlight.tailNumber
+        plan["fin"] = bookedFlight.tailNumber[3:]
+        plan["pax"] = str(bookedFlight.numPassengers)
+
+        departureTime = bookedFlight.departureTime
+        plan["date"] = "%d%s%d" % (departureTime.day,
+                                   SimBriefSetupPage.monthNum2Name[departureTime.month-1],
+                                   departureTime.year%100)
+        plan["deph"] = str(departureTime.hour)
+        plan["depm"] = str(departureTime.minute)
+
+        arrivalTime = bookedFlight.arrivalTime
+        plan["steh"] = str(arrivalTime.hour)
+        plan["stem"] = str(arrivalTime.minute)
+
+        plan["manualzfw"] = str(wizard.zfw / 1000.0)
+        plan["cargo"] = str((wizard.bagWeight + wizard.cargoWeight + wizard.mailWeight)/1000.0)
+
+        plan["route"] = wizard.route
+        plan["fl"] = str(wizard.filedCruiseAltitude)
+        plan["altn"] = wizard.alternate
+
+        plan["addedfuel"] = str(self._extraFuel.get_int() / 1000.0)
+        plan["origrwy"] = self._takeoffRunway.get_text()
+        plan["destrwy"] = self._landingRunway.get_text()
+
+        for (key, control) in [("climb", self._climbProfile),
+                               ("cruise", self._cruiseProfile),
+                               ("descent", self._descentProfile)]:
+            model = control.get_model()
+            active = control.get_active_iter()
+            value = model.get_value(active, 0)
+            plan[key] = value
+
+        return plan
+
+    def _upperChanged(self, entry, arg = None):
+        """Called when the value of some entry widget has changed and the value
+        should be converted to uppercase."""
+        entry.set_text(entry.get_text().upper())
+
+#-----------------------------------------------------------------------------
+
+class SimBriefingPage(Page):
+    """Page to display the SimBrief HTML briefing."""
+    class BrowserLifeSpanHandler(object):
+        """The life-span handler of a browser."""
+        def __init__(self, simBriefingPage):
+            """Construct the life-span handler for the given page."""
+            self._simBriefingPage = simBriefingPage
+
+        def OnBeforeClose(self, browser):
+            """Called before closing the browser."""
+            self._simBriefingPage._invalidateBrowser()
+
+    def __init__(self, wizard):
+        """Construct the setup page."""
+
+        super(SimBriefingPage, self).__init__(wizard, "simbrief_result",
+                                              xstr("simbrief_result_title"), "")
+
+        self._alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                       xscale = 1.0, yscale = 1.0)
+
+        self._container = cef.getContainer()
+        self._alignment.add(self._container)
+
+        self.setMainWidget(self._alignment)
+
+        self._browser = None
+
+        self.addCancelFlightButton()
+
+        self.addPreviousButton(clicked = self._backClicked)
+
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+        self._button.set_label(xstr("briefing_button"))
+        self._button.set_has_tooltip(False)
+        self._button.set_use_stock(False)
+
+    def activate(self):
+        """Activate the SimBrief flight plan page"""
+        if self._browser is None:
+            self._startBrowser()
+        else:
+            self._browser.Reload()
+
+    def grabDefault(self):
+        """If the page has a default button, make it the default one."""
+        super(SimBriefingPage, self).grabDefault()
+
+        if self._browser is None:
+            self._startBrowser()
+
+    def _backClicked(self, button):
+        """Called when the Back button has been pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        """Called when the Forward button has been pressed."""
+        if not self._completed:
+            self._button.set_label(xstr("button_next"))
+            self._button.set_tooltip_text(xstr("button_next_tooltip"))
+            self._wizard.usingSimBrief = True
+            self.complete()
+
+        self._wizard.nextPage()
+
+    def _startBrowser(self):
+        """Start the browser.
+
+        If a container is needed, create one."""
+        if self._container is None:
+            self._container = cef.getContainer()
+            self._alignment.add(self._container)
+
+        url = "file://" + SimBriefSetupPage.getHTMLFilePath()
+        self._browser = cef.startInContainer(self._container, url)
+
+        lifeSpanHandler = SimBriefingPage.BrowserLifeSpanHandler(self)
+        self._browser.SetClientHandler(lifeSpanHandler)
+
+    def _invalidateBrowser(self):
+        """Invalidate the browser (and associated stuff)."""
+        self._alignment.remove(self._container)
+        self._container = None
+        self._browser = None
+
+#-----------------------------------------------------------------------------
+
+class FuelTank(gtk.VBox):
+    """Widget for the fuel tank."""
+    def __init__(self, fuelTank, name, capacity, currentWeight):
+        """Construct the widget for the tank with the given name."""
+        super(FuelTank, self).__init__()
+
+        self._enabled = True
+        self.fuelTank = fuelTank
+        self.capacity = capacity
+        self.currentWeight = currentWeight
+        self.expectedWeight = currentWeight
+
+        label = gtk.Label("<b>" + name + "</b>")
+        label.set_use_markup(True)
+        label.set_use_underline(True)
+        label.set_justify(JUSTIFY_CENTER)
+        label.set_alignment(0.5, 1.0)
+        self.pack_start(label, False, False, 4)
+
+        self._tankFigure = gtk.EventBox()
+        self._tankFigure.set_size_request(38, -1)
+        self._tankFigure.set_visible_window(False)
+        self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
+
+        if pygobject:
+            self._tankFigure.connect("draw", self._drawTankFigure)
+        else:
+            self._tankFigure.connect("expose_event", self._drawTankFigure)
+        self._tankFigure.connect("button_press_event", self._buttonPressed)
+        self._tankFigure.connect("motion_notify_event", self._motionNotify)
+        self._tankFigure.connect("scroll-event", self._scrolled)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 1.0)
+        alignment.add(self._tankFigure)
+
+        self.pack_start(alignment, True, True, 4)
+
+        self._expectedButton = gtk.SpinButton()
+        self._expectedButton.set_numeric(True)
+        self._expectedButton.set_range(0, self.capacity)
+        self._expectedButton.set_increments(10, 100)
+        self._expectedButton.set_value(currentWeight)
+        self._expectedButton.set_alignment(1.0)
+        self._expectedButton.set_width_chars(5)
+        self._expectedButton.connect("value-changed", self._expectedChanged)
+
+        label.set_mnemonic_widget(self._expectedButton)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 1.0)
+        alignment.add(self._expectedButton)
+        self.pack_start(alignment, False, False, 4)
+
+    def setCurrent(self, currentWeight):
+        """Set the current weight."""
+        self.currentWeight = currentWeight
+        self._redraw()
+
+    def isCorrect(self):
+        """Determine if the contents of the fuel tank are as expected"""
+        return abs(self.expectedWeight - self.currentWeight)<=1
+
+    def disable(self):
+        """Disable the fuel tank."""
+        self._expectedButton.set_sensitive(False)
+        self._enabled = False
+
+    def _redraw(self):
+        """Redraw the tank figure."""
+        self._tankFigure.queue_draw()
+
+    def _drawTankFigure(self, tankFigure, eventOrContext):
+        """Draw the tank figure."""
+        triangleSize = 5
+
+        context = eventOrContext if pygobject else tankFigure.window.cairo_create()
+        (xOffset, yOffset) = (0, 0) if pygobject \
+                             else (tankFigure.allocation.x, tankFigure.allocation.y)
+
+        width = tankFigure.get_allocated_width() if pygobject \
+                else tankFigure.allocation.width
+        height = tankFigure.get_allocated_height() if pygobject \
+                 else tankFigure.allocation.height
+
+        rectangleX0 = triangleSize
+        rectangleY0 = triangleSize
+        rectangleX1 = width - 1 - triangleSize
+        rectangleY1 = height - 1 - triangleSize
+        rectangleLineWidth = 2.0
+
+        context.set_source_rgb(0.0, 0.0, 0.0)
+        context.set_line_width(rectangleLineWidth)
+        context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
+                          yOffset + rectangleY0 + rectangleLineWidth/2,
+                          rectangleX1 - rectangleX0 - rectangleLineWidth,
+                          rectangleY1 - rectangleY0 - rectangleLineWidth)
+        context.stroke()
+
+        rectangleInnerLeft   = rectangleX0 + rectangleLineWidth
+        rectangleInnerRight  = rectangleX1 - rectangleLineWidth
+        self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
+        self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
+
+        rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
+        rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
+
+        context.set_source_rgb(1.0, 0.9, 0.6)
+        currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
+        currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
+        context.rectangle(xOffset + rectangleInnerLeft,
+                          yOffset + rectangleInnerTop +
+                          rectangleInnerHeight - currentHeight,
+                          rectangleInnerWidth, currentHeight)
+        context.fill()
+
+        expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
+        expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
+
+        context.set_line_width(1.5)
+        context.set_source_rgb(0.0, 0.85, 0.85)
+        context.move_to(xOffset + rectangleX0, yOffset + expectedY)
+        context.line_to(xOffset + rectangleX1, yOffset + expectedY)
+        context.stroke()
+
+        context.set_line_width(0.0)
+        context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
+        context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
+        context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
+        context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
+        context.fill()
+
+        context.set_line_width(0.0)
+        context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
+        context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
+        context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
+        context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
+        context.fill()
+
+        return True
+
+    def _setExpectedFromY(self, y):
+        """Set the expected weight from the given Y-coordinate."""
+        level = (self._rectangleInnerBottom - y) / \
+                (self._rectangleInnerBottom - self._rectangleInnerTop)
+        level = min(1.0, max(0.0, level))
+        self._expectedButton.set_value(level * self.capacity)
+
+    def _buttonPressed(self, tankFigure, event):
+        """Called when a button is pressed in the figure.
+
+        The expected level will be set there."""
+        if self._enabled and event.button==1:
+            self._setExpectedFromY(event.y)
+
+    def _motionNotify(self, tankFigure, event):
+        """Called when the mouse pointer moves within the area of a tank figure."""
+        if self._enabled and event.state==BUTTON1_MASK:
+            self._setExpectedFromY(event.y)
+
+    def _scrolled(self, tankFigure, event):
+        """Called when a scroll event is received."""
+        if self._enabled:
+            increment = 1 if event.state==CONTROL_MASK \
+                        else 100 if event.state==SHIFT_MASK \
+                        else 10 if event.state==0 else 0
+            if increment!=0:
+                if event.direction==SCROLL_DOWN:
+                    increment *= -1
+                self._expectedButton.spin(SPIN_USER_DEFINED, increment)
+
+    def _expectedChanged(self, spinButton):
+        """Called when the expected value has changed."""
+        self.expectedWeight = spinButton.get_value_as_int()
+        self._redraw()
+
+#-----------------------------------------------------------------------------
+
+class FuelPage(Page):
+    """The page containing the fuel tank filling."""
+    _pumpStep = 0.02
+
+    def __init__(self, wizard):
+        """Construct the page."""
+        super(FuelPage, self).__init__(wizard, "fuel",
+                                       xstr("fuel_title"),
+                                       xstr("fuel_help_pre") +
+                                       xstr("fuel_help_post"),
+                                       completedHelp = xstr("fuel_chelp"))
+
+        self._fuelTanks = []
+        self._fuelTable = None
+        self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                            xscale = 0.0, yscale = 1.0)
+        self.setMainWidget(self._fuelAlignment)
+
+        tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
+        self._setupTanks(tankData)
+
+        self.addCancelFlightButton()
+
+        self._backButton = self.addPreviousButton(clicked = self._backClicked)
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+        self._pumpIndex = 0
+
+    def activate(self):
+        """Activate the page."""
+        self._setupTanks(self._wizard._fuelData)
+
+        aircraft = self._wizard.gui.flight.aircraft
+        minLandingFuel = aircraft.minLandingFuel
+        recommendedLandingFuel = aircraft.recommendedLandingFuel
+
+        middleHelp = "" if minLandingFuel is None else \
+            (xstr("fuel_help_min") % (minLandingFuel,)) \
+            if recommendedLandingFuel is None else \
+            (xstr("fuel_help_min_rec") % (minLandingFuel,
+                                          recommendedLandingFuel))
+        self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
+
+    def finalize(self):
+        """Finalize the page."""
+        for fuelTank in self._fuelTanks:
+            fuelTank.disable()
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        """Called when the forward button is clicked."""
+        if not self._completed:
+            self._pumpIndex = 0
+            self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
+            self._pump()
+        elif self._wizard.usingSimBrief:
+            self._wizard.jumpPage("takeoff")
+        else:
+            self._wizard.jumpPage("briefing1")
+
+    def _setupTanks(self, tankData):
+        """Setup the tanks for the given data."""
+        numTanks = len(tankData)
+        if self._fuelTable is not None:
+            self._fuelAlignment.remove(self._fuelTable)
+
+        self._fuelTanks = []
+        self._fuelTable = gtk.Table(numTanks, 1)
+        self._fuelTable.set_col_spacings(16)
+        index = 0
+        for (tank, current, capacity) in tankData:
+            fuelTank = FuelTank(tank,
+                                xstr("fuel_tank_" +
+                                     const.fuelTank2string(tank)),
+                                capacity, current)
+            self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
+            self._fuelTanks.append(fuelTank)
+            index += 1
+
+        self._fuelAlignment.add(self._fuelTable)
+        self.show_all()
+
+    def _pump(self):
+        """Perform one step of pumping.
+
+        It is checked, if the current tank's contents are of the right
+        quantity. If not, it is filled one step further to the desired
+        contents. Otherwise the next tank is started. If all tanks are are
+        filled, the next page is selected."""
+        numTanks = len(self._fuelTanks)
+
+        fuelTank = None
+        while self._pumpIndex < numTanks:
+            fuelTank = self._fuelTanks[self._pumpIndex]
+            if fuelTank.isCorrect():
+                self._pumpIndex += 1
+                fuelTank = None
+            else:
+                break
+
+        if fuelTank is None:
+            self._wizard.gui.endBusy()
+            if self._wizard.usingSimBrief:
+                self._wizard.gui.startMonitoring()
+                self._wizard.jumpPage("takeoff")
+            else:
+                bookedFlight = self._wizard._bookedFlight
+                self._wizard.gui.beginBusy(xstr("route_down_notams"))
+                self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
+                                                      bookedFlight.departureICAO,
+                                                      bookedFlight.arrivalICAO)
+                startSound(const.SOUND_NOTAM)
+        else:
+            currentLevel = fuelTank.currentWeight / fuelTank.capacity
+            expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
+            if currentLevel<expectedLevel:
+                currentLevel += FuelPage._pumpStep
+                if currentLevel>expectedLevel: currentLevel = expectedLevel
+            else:
+                currentLevel -= FuelPage._pumpStep
+                if currentLevel<expectedLevel: currentLevel = expectedLevel
+            fuelTank.setCurrent(currentLevel * fuelTank.capacity)
+            self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
+                                                      currentLevel)])
+            gobject.timeout_add(50, self._pump)
+
+    def _notamsCallback(self, returned, result):
+        """Callback for the NOTAMs."""
+        gobject.idle_add(self._handleNOTAMs, returned, result)
+
+    def _handleNOTAMs(self, returned, result):
+        """Handle the NOTAMs."""
+        if returned:
+            self._wizard._departureNOTAMs = result.departureNOTAMs
+            self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
+        else:
+            self._wizard._departureNOTAMs = None
+            self._wizard._arrivalNOTAMs = None
+
+        bookedFlight = self._wizard._bookedFlight
+        self._wizard.gui.beginBusy(xstr("route_down_metars"))
+        self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
+                                              [bookedFlight.departureICAO,
+                                               bookedFlight.arrivalICAO])
+
+    def _metarsCallback(self, returned, result):
+        """Callback for the METARs."""
+        gobject.idle_add(self._handleMETARs, returned, result)
+
+    def _handleMETARs(self, returned, result):
+        """Handle the METARs."""
+        self._wizard._departureMETAR = None
+        self._wizard._arrivalMETAR = None
+        bookedFlight = self._wizard._bookedFlight
+        if returned:
+            if bookedFlight.departureICAO in result.metars:
+                self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
+            if bookedFlight.arrivalICAO in result.metars:
+                self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
+
+        self._wizard.gui.endBusy()
+        self._backButton.set_sensitive(True)
+        self._button.set_sensitive(True)
+        self._wizard.nextPage()
+
+#-----------------------------------------------------------------------------
+
+class BriefingPage(Page):
+    """Page for the briefing."""
+    def __init__(self, wizard, departure):
+        """Construct the briefing page."""
+        self._departure = departure
+
+        number = 1 if departure else 2
+
+        title = xstr("briefing_title") % (number,
+                                          xstr("briefing_departure")
+                                          if departure
+                                          else xstr("briefing_arrival"))
+        super(BriefingPage, self).__init__(wizard,
+                                           "briefing%d" % (number,),
+                                           title, xstr("briefing_help"),
+                                           completedHelp = xstr("briefing_chelp"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 1.0, yscale = 1.0)
+
+        mainBox = gtk.VBox()
+        alignment.add(mainBox)
+        self.setMainWidget(alignment)
+
+        self._notamsFrame = gtk.Frame()
+        self._notamsFrame.set_label(xstr("briefing_notams_init"))
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.set_size_request(-1, 128)
+        # FIXME: these constants should be in common
+        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC,
+                                  gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC)
+        self._notams = gtk.TextView()
+        self._notams.set_editable(False)
+        self._notams.set_accepts_tab(False)
+        self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
+        scrolledWindow.add(self._notams)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 0,
+                              padding_left = 0, padding_right = 0)
+        alignment.add(scrolledWindow)
+        self._notamsFrame.add(alignment)
+        mainBox.pack_start(self._notamsFrame, True, True, 4)
+
+        self._metarFrame = gtk.Frame()
+        self._metarFrame.set_label(xstr("briefing_metar_init"))
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.set_size_request(-1, 32)
+        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC,
+                                  gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC)
+
+        self._updatingMETAR = False
+
+        self._metar = gtk.TextView()
+        self._metar.set_accepts_tab(False)
+        self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
+        self._metar.get_buffer().connect("changed", self._metarChanged)
+        self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
+        scrolledWindow.add(self._metar)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 0,
+                              padding_left = 0, padding_right = 0)
+        alignment.add(scrolledWindow)
+        self._metarFrame.add(alignment)
+        mainBox.pack_start(self._metarFrame, True, True, 4)
+        self.metarEdited = False
+
+        self.addCancelFlightButton()
+
+        self.addPreviousButton(clicked = self._backClicked)
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+    @property
+    def metar(self):
+        """Get the METAR on the page."""
+        buffer = self._metar.get_buffer()
+        return buffer.get_text(buffer.get_start_iter(),
+                               buffer.get_end_iter(), True)
+
+    def setMETAR(self, metar):
+        """Set the METAR."""
+        self._metar.get_buffer().set_text(metar)
+        self.metarEdited = False
+
+    def changeMETAR(self, metar):
+        """Change the METAR as a result of an edit on one of the other
+        pages."""
+        self._updatingMETAR = True
+        self._metar.get_buffer().set_text(metar)
+        self._updatingMETAR = False
+
+        self._updateButton()
+        self.metarEdited = True
+
+    def activate(self):
+        """Activate the page."""
+        if not self._departure:
+            self._button.set_label(xstr("briefing_button"))
+            self._button.set_has_tooltip(False)
+            self._button.set_use_stock(False)
+
+        bookedFlight = self._wizard._bookedFlight
+
+        icao = bookedFlight.departureICAO if self._departure \
+               else bookedFlight.arrivalICAO
+        notams = self._wizard._departureNOTAMs if self._departure \
+                 else self._wizard._arrivalNOTAMs
+        metar = self._wizard._departureMETAR if self._departure \
+                 else self._wizard._arrivalMETAR
+
+        self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
+        buffer = self._notams.get_buffer()
+        if notams is None:
+            buffer.set_text(xstr("briefing_notams_failed"))
+        elif not notams:
+            buffer.set_text(xstr("briefing_notams_missing"))
+        else:
+            s = ""
+            for notam in notams:
+                s += str(notam)
+                s += "-------------------- * --------------------\n"
+            buffer.set_text(s)
+
+        self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
+        buffer = self._metar.get_buffer()
+        self._updatingMETAR = True
+        if metar is None:
+            buffer.set_text("")
+            self.setHelp(xstr("briefing_help_nometar"))
+        else:
+            buffer.set_text(metar)
+        self._updatingMETAR = False
+        self._updateButton()
+
+        label = self._metarFrame.get_label_widget()
+        label.set_use_underline(True)
+        label.set_mnemonic_widget(self._metar)
+
+        self.metarEdited = False
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        """Called when the forward button is clicked."""
+        if not self._departure:
+            if not self._completed:
+                self._wizard.gui.startMonitoring()
+                self._button.set_label(xstr("button_next"))
+                self._button.set_tooltip_text(xstr("button_next_tooltip"))
+                self.complete()
+
+        self._wizard.nextPage()
+
+    def _metarChanged(self, buffer):
+        """Called when the METAR has changed."""
+        print "BriefingPage.metarChanged", self._updatingMETAR
+        if not self._updatingMETAR:
+            self.metarEdited = True
+            self._updateButton()
+            metar = buffer.get_text(buffer.get_start_iter(),
+                                buffer.get_end_iter(), True)
+            self._wizard.metarChanged(metar, self)
+
+    def _metarInserted(self, textBuffer, iter, text, length):
+        """Called when new characters are inserted into the METAR.
+
+        It uppercases all characters."""
+        print "BriefingPage.metarInserted", self._updatingMETAR
+        if not self._updatingMETAR:
+            self._updatingMETAR = True
+
+            iter1 = iter.copy()
+            iter1.backward_chars(length)
+            textBuffer.delete(iter, iter1)
+
+            textBuffer.insert(iter, text.upper())
+
+            self._updatingMETAR = False
+
+    def _updateButton(self):
+        """Update the sensitivity of the Next button based on the contents of
+        the METAR field."""
+        buffer = self._metar.get_buffer()
+        self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
+                                                   buffer.get_end_iter(),
+                                                   True)!="")
+
+
+#-----------------------------------------------------------------------------
+
+class TakeoffPage(Page):
+    """Page for entering the takeoff data."""
+    def __init__(self, wizard):
+        """Construct the takeoff page."""
+        super(TakeoffPage, self).__init__(wizard, "takeoff",
+                                          xstr("takeoff_title"),
+                                          xstr("takeoff_help"),
+                                          completedHelp = xstr("takeoff_chelp"))
+
+        self._forwardAllowed = False
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(9, 24)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        row = 0
+
+        label = gtk.Label(xstr("takeoff_metar"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row+1)
+
+        self._metar = gtk.Entry()
+        self._metar.set_width_chars(40)
+        self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
+        self._metar.connect("changed", self._metarChanged)
+        self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
+        table.attach(self._metar, 1, 24, row, row+1)
+        label.set_mnemonic_widget(self._metar)
+
+        self._updatingMETAR = False
+
+        row += 1
+
+        label = gtk.Label(xstr("takeoff_runway"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row+1)
+
+        self._runway = gtk.Entry()
+        self._runway.set_width_chars(10)
+        self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
+        self._runway.connect("changed", self._upperChanged)
+        table.attach(self._runway, 1, 3, row, row+1)
+        label.set_mnemonic_widget(self._runway)
+
+        row += 1
+
+        label = gtk.Label(xstr("takeoff_sid"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row+1)
+
+        if pygobject:
+            self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
+        else:
+            self._sid = gtk.ComboBoxEntry(comboModel)
+
+        self._sid.set_entry_text_column(0)
+        self._sid.get_child().set_width_chars(10)
+        self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
+        self._sid.connect("changed", self._upperChangedComboBox)
+        table.attach(self._sid, 1, 3, row, row+1)
+        label.set_mnemonic_widget(self._sid)
+
+        row += 1
+
+        label = gtk.Label(xstr("takeoff_v1"))
+        label.set_use_markup(True)
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row+1)
+
+        self._v1 = IntegerEntry()
+        self._v1.set_width_chars(4)
+        self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
+        self._v1.connect("integer-changed", self._valueChanged)
+        table.attach(self._v1, 2, 3, row, row+1)
+        label.set_mnemonic_widget(self._v1)
+
+        self._v1Unit = gtk.Label(xstr("label_knots"))
+        self._v1Unit.set_alignment(0.0, 0.5)
+        table.attach(self._v1Unit, 3, 4, row, row+1)
+
+        row += 1
+
+        label = gtk.Label(xstr("takeoff_vr"))
+        label.set_use_markup(True)
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row+1)
+
+        self._vr = IntegerEntry()
+        self._vr.set_width_chars(4)
+        self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
+        self._vr.connect("integer-changed", self._valueChanged)
+        table.attach(self._vr, 2, 3, row, row+1)
+        label.set_mnemonic_widget(self._vr)
+
+        self._vrUnit = gtk.Label(xstr("label_knots"))
+        self._vrUnit.set_alignment(0.0, 0.5)
+        table.attach(self._vrUnit, 3, 4, row, row+1)
+
+        row += 1
+
+        label = gtk.Label(xstr("takeoff_v2"))
+        label.set_use_markup(True)
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row+1)
+
+        self._v2 = IntegerEntry()
+        self._v2.set_width_chars(4)
+        self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
+        self._v2.connect("integer-changed", self._valueChanged)
+        table.attach(self._v2, 2, 3, row, row+1)
+        label.set_mnemonic_widget(self._v2)
+
+        self._v2Unit = gtk.Label(xstr("label_knots"))
+        self._v2Unit.set_alignment(0.0, 0.5)
+        table.attach(self._v2Unit, 3, 4, row, row+1)
+
+        row += 1
+
+        self._derateType = acft.DERATE_NONE
+
+        self._derateLabel = gtk.Label()
+        self._derateLabel.set_use_underline(True)
+        self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
+        self._derateLabel.set_alignment(0.0, 0.5)
+        table.attach(self._derateLabel, 0, 1, row, row+1)
+
+        self._derate = gtk.Alignment()
+        table.attach(self._derate, 2, 4, row, row+1)
+        self._derateWidget = None
+        self._derateEntry = None
+        self._derateUnit = None
+        self._derateButtons = None
+
+        row += 1
+
+        self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
+        self._antiIceOn.set_use_underline(True)
+        self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
+        table.attach(self._antiIceOn, 2, 4, row, row+1)
+
+        row += 1
+
+        self._rto = gtk.CheckButton(xstr("takeoff_rto"))
+        self._rto.set_use_underline(True)
+        self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
+        self._rto.connect("toggled", self._rtoToggled)
+        table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
+
+        self.addCancelFlightButton()
+
+        self.addPreviousButton(clicked = self._backClicked)
+
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+        self._active = False
+
+    @property
+    def runway(self):
+        """Get the runway."""
+        return self._runway.get_text()
+
+    @property
+    def sid(self):
+        """Get the SID."""
+        text = self._sid.get_child().get_text()
+        return text if self._sid.get_active()!=0 and text and text!="N/A" \
+               else None
+
+    @property
+    def v1(self):
+        """Get the v1 speed."""
+        return self._v1.get_int()
+
+    @property
+    def vr(self):
+        """Get the vr speed."""
+        return self._vr.get_int()
+
+    @property
+    def v2(self):
+        """Get the v2 speed."""
+        return self._v2.get_int()
+
+    @property
+    def derate(self):
+        """Get the derate value, if any."""
+        if self._derateWidget is None:
+            return None
+        if self._derateType==acft.DERATE_BOEING:
+            derate = self._derateEntry.get_text()
+            return derate if derate else None
+        elif self._derateType==acft.DERATE_EPR:
+            derate = self._derateWidget.get_text()
+            return derate if derate else None
+        elif self._derateType==acft.DERATE_TUPOLEV:
+            return acft.DERATE_TUPOLEV_NOMINAL \
+                   if self._derateButtons[0].get_active() \
+                   else acft.DERATE_TUPOLEV_TAKEOFF
+        elif self._derateType==acft.DERATE_B462:
+            return self._derateWidget.get_active()
+        else:
+            return None
+
+    @property
+    def antiIceOn(self):
+        """Get whether the anti-ice system has been turned on."""
+        return self._antiIceOn.get_active()
+
+    @antiIceOn.setter
+    def antiIceOn(self, value):
+        """Set the anti-ice indicator."""
+        self._antiIceOn.set_active(value)
+
+    @property
+    def rtoIndicated(self):
+        """Get whether the pilot has indicated if there was an RTO."""
+        return self._rto.get_active()
+
+    def activate(self):
+        """Activate the page."""
+        print "TakeoffPage.activate"
+
+        self._updatingMETAR = True
+        self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
+        self._updatingMETAR = False
+
+        if self._wizard.takeoffRunway is None:
+            self._runway.set_text("")
+        else:
+            self._runway.set_text(self._wizard.takeoffRunway)
+        self._runway.set_sensitive(True)
+        self._sid.set_active(0)
+        self._sid.set_sensitive(True)
+        self._v1.set_int(None)
+        self._v1.set_sensitive(True)
+        self._vr.set_int(None)
+        self._vr.set_sensitive(True)
+        self._v2.set_int(None)
+        self._v2.set_sensitive(True)
+
+        i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
+        speedUnit = xstr("label" + i18nSpeedUnit)
+        self._v1Unit.set_text(speedUnit)
+        self._vrUnit.set_text(speedUnit)
+        self._v2Unit.set_text(speedUnit)
+
+        self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
+        self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
+        self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
+
+        self._derateType = self._wizard.gui.flight.aircraft.derateType
+
+        self._setupDerateWidget()
+
+        self._rto.set_active(False)
+        self._rto.set_sensitive(False)
+
+        self._button.set_sensitive(False)
+        self._forwardAllowed = False
+
+        self._active = True
+
+    def allowForward(self):
+        """Allow going to the next page."""
+        print "TakeoffPage.allowForward"
+        self._forwardAllowed = True
+        self._updateForwardButton()
+
+    def reset(self):
+        """Reset the page if the wizard is reset."""
+        print "TakeoffPage.reset"
+
+        super(TakeoffPage, self).reset()
+        self._v1.reset()
+        self._vr.reset()
+        self._v2.reset()
+        self._hasDerate = False
+        self._antiIceOn.set_active(False)
+        self._active = False
+
+    def setRTOEnabled(self, enabled):
+        """Set the RTO checkbox enabled or disabled."""
+        if not enabled:
+            self._rto.set_active(False)
+        self._rto.set_sensitive(enabled)
+
+    def changeMETAR(self, metar):
+        """Change the METAR as a result of an edit on one of the other
+        pages."""
+        if self._active:
+            print "TakeoffPage.changeMETAR"
+            self._updatingMETAR = True
+            self._metar.get_buffer().set_text(metar, -1)
+            self._updatingMETAR = False
+
+            self._updateForwardButton()
+
+    def _updateForwardButton(self):
+        """Update the sensitivity of the forward button based on some conditions."""
+        sensitive = self._forwardAllowed and \
+                    self._metar.get_text()!="" and \
+                    self._runway.get_text()!="" and \
+                    self.sid is not None and \
+                    self.v1 is not None and \
+                    self.vr is not None and \
+                    self.v2 is not None and \
+                    self.v1 <= self.vr and \
+                    self.vr <= self.v2 and \
+                    (self._derateType==acft.DERATE_NONE or
+                     self.derate is not None)
+
+        print "TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive
+        if self._forwardAllowed:
+            print "  METAR: ", self._metar.get_text()
+            print "  runway: ", self._runway.get_text()
+            print "  SID:", self.sid
+            print "  V1:", self.v1
+            print "  VR:", self.vr
+            print "  V2:", self.v2
+            print "  derateType:", self._derateType
+            print "  derate:", self.derate
+
+        self._button.set_sensitive(sensitive)
+
+    def _valueChanged(self, widget, arg = None):
+        """Called when the value of some widget has changed."""
+        print "TakeoffPage._valueChanged"
+
+        self._updateForwardButton()
+
+    def _upperChanged(self, entry, arg = None):
+        """Called when the value of some entry widget has changed and the value
+        should be converted to uppercase."""
+        print "TakeoffPage._upperChanged"
+        entry.set_text(entry.get_text().upper())
+        self._valueChanged(entry, arg)
+
+    def _upperChangedComboBox(self, comboBox):
+        """Called for combo box widgets that must be converted to uppercase."""
+        entry = comboBox.get_child()
+        if comboBox.get_active()==-1:
+            entry.set_text(entry.get_text().upper())
+        self._valueChanged(entry)
+
+    def _derateChanged(self, entry):
+        """Called when the value of the derate is changed."""
+        print "TakeoffPage._derateChanged"
+        self._updateForwardButton()
+
+    def _rtoToggled(self, button):
+        """Called when the RTO check button is toggled."""
+        self._wizard.rtoToggled(button.get_active())
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        """Called when the forward button is clicked."""
+        aircraft = self._wizard.gui.flight.aircraft
+        aircraft.updateV1R2()
+        if self.derate is not None:
+            aircraft.updateDerate()
+        aircraft.updateTakeoffAntiIce()
+        self._wizard.nextPage()
+
+    def _setupDerateWidget(self):
+        """Setup the derate widget."""
+        if self._derateWidget is not None:
+            self._derate.remove(self._derateWidget)
+
+        if self._derateType==acft.DERATE_BOEING:
+            self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
+            self._derateLabel.set_use_underline(True)
+            self._derateLabel.set_sensitive(True)
+
+            self._derateEntry = gtk.Entry()
+            self._derateEntry.set_width_chars(7)
+            self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
+            self._derateEntry.set_alignment(1.0)
+            self._derateEntry.connect("changed", self._derateChanged)
+            self._derateLabel.set_mnemonic_widget(self._derateEntry)
+
+            self._derateUnit = gtk.Label("%")
+            self._derateUnit.set_alignment(0.0, 0.5)
+
+            self._derateWidget = gtk.Table(3, 1)
+            self._derateWidget.set_row_spacings(4)
+            self._derateWidget.set_col_spacings(16)
+            self._derateWidget.set_homogeneous(False)
+
+            self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
+            self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
+
+            self._derate.add(self._derateWidget)
+        elif self._derateType==acft.DERATE_EPR:
+            self._derateLabel.set_text("_EPR:")
+            self._derateLabel.set_use_underline(True)
+            self._derateLabel.set_sensitive(True)
+
+            self._derateWidget = gtk.Entry()
+            self._derateWidget.set_width_chars(7)
+            self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
+            self._derateWidget.set_alignment(1.0)
+            self._derateWidget.connect("changed", self._derateChanged)
+            self._derateLabel.set_mnemonic_widget(self._derateWidget)
+
+            self._derate.add(self._derateWidget)
+        elif self._derateType==acft.DERATE_TUPOLEV:
+            self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
+            self._derateLabel.set_use_underline(True)
+            self._derateLabel.set_sensitive(True)
+
+            if pygobject:
+                nominal = gtk.RadioButton.\
+                  new_with_label_from_widget(None,
+                                             xstr("takeoff_derate_tupolev_nominal"))
+            else:
+                nominal = gtk.RadioButton(None,
+                                          xstr("takeoff_derate_tupolev_nominal"))
+            nominal.set_use_underline(True)
+            nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
+            nominal.connect("toggled", self._derateChanged)
+
+            if pygobject:
+                takeoff = gtk.RadioButton.\
+                  new_with_label_from_widget(nominal,
+                                             xstr("takeoff_derate_tupolev_takeoff"))
+            else:
+                takeoff = gtk.RadioButton(nominal,
+                                          xstr("takeoff_derate_tupolev_takeoff"))
+
+            takeoff.set_use_underline(True)
+            takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
+            takeoff.connect("toggled", self._derateChanged)
+
+            self._derateButtons = [nominal, takeoff]
+
+            self._derateWidget = gtk.HBox()
+            self._derateWidget.pack_start(nominal, False, False, 4)
+            self._derateWidget.pack_start(takeoff, False, False, 4)
+
+            self._derate.add(self._derateWidget)
+        elif self._derateType==acft.DERATE_B462:
+            self._derateLabel.set_text("")
+
+            self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
+            self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
+            self._derateWidget.set_use_underline(True)
+            self._derate.add(self._derateWidget)
+        else:
+            self._derateWidget = None
+            self._derateLabel.set_text("")
+            self._derateLabel.set_sensitive(False)
+
+    def _metarChanged(self, entry):
+        """Called when the METAR has changed."""
+        print "TakeoffPage.metarChanged", self._updatingMETAR
+        if not self._updatingMETAR:
+            self._updateForwardButton()
+            self._wizard.metarChanged(entry.get_text(), self)
+
+    def _metarInserted(self, buffer, position, text, length):
+        """Called when new characters are inserted into the METAR.
+
+        It uppercases all characters."""
+        print "TakeoffPage.metarInserted", self._updatingMETAR
+        if not self._updatingMETAR:
+            self._updatingMETAR = True
+
+            buffer.delete_text(position, length)
+            buffer.insert_text(position, text.upper(), length)
+
+            self._updatingMETAR = False
+
+#-----------------------------------------------------------------------------
+
+class CruisePage(Page):
+    """The page containing the flight level that might change during flight."""
+    def __init__(self, wizard):
+        """Construct the page."""
+        super(CruisePage, self).__init__(wizard, "cruise",
+                                         xstr("cruise_title"),
+                                         xstr("cruise_help"))
+
+        self._loggable = False
+        self._loggedCruiseLevel = 240
+        self._activated = False
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
+                                  xscale = 0.0, yscale = 1.0)
+
+        mainBox = gtk.VBox()
+        alignment.add(mainBox)
+        self.setMainWidget(alignment)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+        mainBox.pack_start(alignment, False, False, 16)
+
+        levelBox = gtk.HBox()
+
+        label = gtk.Label(xstr("route_level"))
+        label.set_use_underline(True)
+        levelBox.pack_start(label, True, True, 0)
+
+        self._cruiseLevel = gtk.SpinButton()
+        self._cruiseLevel.set_increments(step = 10, page = 100)
+        self._cruiseLevel.set_range(min = 50, max = 500)
+        self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
+        self._cruiseLevel.set_numeric(True)
+        self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
+        label.set_mnemonic_widget(self._cruiseLevel)
+
+        levelBox.pack_start(self._cruiseLevel, False, False, 8)
+
+        self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
+        self._updateButton.set_use_underline(True)
+        self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
+        self._updateButton.connect("clicked", self._updateButtonClicked)
+
+        levelBox.pack_start(self._updateButton, False, False, 16)
+
+        mainBox.pack_start(levelBox, False, False, 0)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 1.0)
+        mainBox.pack_start(alignment, True, True, 0)
+
+        self.addCancelFlightButton()
+
+        self._backButton = self.addPreviousButton(clicked = self._backClicked)
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+    @property
+    def activated(self):
+        """Determine if the page is already activated or not."""
+        return self._activated
+
+    @property
+    def cruiseLevel(self):
+        """Get the cruise level."""
+        return self._loggedCruiseLevel
+
+    @property
+    def loggableCruiseLevel(self):
+        """Get the cruise level which should be logged."""
+        return self._cruiseLevel.get_value_as_int()
+
+    def setLoggable(self, loggable):
+        """Set whether the cruise altitude can be logged."""
+        self._loggable = loggable
+        self._updateButtons()
+
+    def activate(self):
+        """Setup the route from the booked flight."""
+        self._loggedCruiseLevel = self._wizard.filedCruiseLevel
+        self._cruiseLevel.set_value(self._loggedCruiseLevel)
+        self._activated = True
+
+    def reset(self):
+        """Reset the page."""
+        self._loggable = False
+        self._activated = False
+        super(CruisePage, self).reset()
+
+    def _updateButtons(self):
+        """Update the sensitivity of the buttons."""
+        self._updateButton.set_sensitive(self._loggable and
+                                         self.loggableCruiseLevel!=
+                                         self._loggedCruiseLevel)
+
+    def _cruiseLevelChanged(self, spinButton):
+        """Called when the cruise level has changed."""
+        self._updateButtons()
+
+    def _updateButtonClicked(self, button):
+        """Called when the update button is clicked."""
+        if self._wizard.cruiseLevelChanged():
+            self._loggedCruiseLevel = self.loggableCruiseLevel
+            self._updateButtons()
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        """Called when the Forward button is clicked."""
+        self._wizard.nextPage()
+
+#-----------------------------------------------------------------------------
+
+class LandingPage(Page):
+    """Page for entering landing data."""
+    def __init__(self, wizard):
+        """Construct the landing page."""
+        super(LandingPage, self).__init__(wizard, "landing",
+                                          xstr("landing_title"),
+                                          xstr("landing_help"),
+                                          completedHelp = xstr("landing_chelp"))
+
+        self._flightEnded = False
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(7, 24)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        row = 0
+
+        label = gtk.Label(xstr("landing_metar"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row+1)
+
+        self._metar = gtk.Entry()
+        self._metar.set_width_chars(40)
+        self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
+        self._metar.connect("changed", self._metarChanged)
+        self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
+        table.attach(self._metar, 2, 24, row, row+1)
+        label.set_mnemonic_widget(self._metar)
+
+        self._updatingMETAR = False
+
+        row += 1
+
+        label = gtk.Label(xstr("landing_star"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row + 1)
+
+        if pygobject:
+            self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
+        else:
+            self._star = gtk.ComboBoxEntry(comboModel)
+
+        self._star.set_entry_text_column(0)
+        self._star.get_child().set_width_chars(10)
+        self._star.set_tooltip_text(xstr("landing_star_tooltip"))
+        self._star.connect("changed", self._upperChangedComboBox)
+        self._star.set_sensitive(False)
+        table.attach(self._star, 2, 4, row, row + 1)
+        label.set_mnemonic_widget(self._star)
+
+        row += 1
+
+        label = gtk.Label(xstr("landing_transition"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row + 1)
+
+        if pygobject:
+            self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
+        else:
+            self._transition = gtk.ComboBoxEntry(comboModel)
+
+        self._transition.set_entry_text_column(0)
+        self._transition.get_child().set_width_chars(10)
+        self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
+        self._transition.connect("changed", self._upperChangedComboBox)
+        self._transition.set_sensitive(False)
+        table.attach(self._transition, 2, 4, row, row + 1)
+        label.set_mnemonic_widget(self._transition)
+
+        row += 1
+
+        label = gtk.Label(xstr("landing_runway"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row + 1)
+
+        self._runway = gtk.Entry()
+        self._runway.set_width_chars(10)
+        self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
+        self._runway.connect("changed", self._upperChanged)
+        table.attach(self._runway, 2, 4, row, row + 1)
+        label.set_mnemonic_widget(self._runway)
+
+        row += 1
+
+        label = gtk.Label(xstr("landing_approach"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row + 1)
+
+        self._approachType = gtk.Entry()
+        self._approachType.set_width_chars(10)
+        self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
+        self._approachType.connect("changed", self._upperChanged)
+        table.attach(self._approachType, 2, 4, row, row + 1)
+        label.set_mnemonic_widget(self._approachType)
+
+        row += 1
+
+        label = gtk.Label(xstr("landing_vref"))
+        label.set_use_markup(True)
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row + 1)
+
+        self._vref = IntegerEntry()
+        self._vref.set_width_chars(5)
+        self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
+        self._vref.connect("integer-changed", self._vrefChanged)
+        table.attach(self._vref, 3, 4, row, row + 1)
+        label.set_mnemonic_widget(self._vref)
+
+        self._vrefUnit = gtk.Label(xstr("label_knots"))
+        table.attach(self._vrefUnit, 4, 5, row, row + 1)
+
+        row += 1
+
+        self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
+        self._antiIceOn.set_use_underline(True)
+        self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
+        table.attach(self._antiIceOn, 3, 5, row, row + 1)
+
+        self.addCancelFlightButton()
+
+        self.addPreviousButton(clicked = self._backClicked)
+
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+        self._active = False
+
+    @property
+    def star(self):
+        """Get the STAR or None if none entered."""
+        text = self._star.get_child().get_text()
+        return text if self._star.get_active()!=0 and text and text!="N/A" \
+               else None
+
+    @property
+    def transition(self):
+        """Get the transition or None if none entered."""
+        text = self._transition.get_child().get_text()
+        return text if self._transition.get_active()!=0 and text and text!="N/A" \
+               else None
+
+    @property
+    def approachType(self):
+        """Get the approach type."""
+        return self._approachType.get_text()
+
+    @property
+    def runway(self):
+        """Get the runway."""
+        return self._runway.get_text()
+
+    @property
+    def vref(self):
+        """Return the landing reference speed."""
+        return self._vref.get_int()
+
+    @property
+    def antiIceOn(self):
+        """Get whether the anti-ice system has been turned on."""
+        return self._antiIceOn.get_active()
+
+    @antiIceOn.setter
+    def antiIceOn(self, value):
+        """Set the anti-ice indicator."""
+        self._antiIceOn.set_active(value)
+
+    def reset(self):
+        """Reset the page if the wizard is reset."""
+        super(LandingPage, self).reset()
+        self._vref.reset()
+        self._antiIceOn.set_active(False)
+        self._flightEnded = False
+        self._active = False
+
+    def activate(self):
+        """Called when the page is activated."""
+        self._updatingMETAR = True
+        self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
+        self._updatingMETAR = False
+
+        self._star.set_active(0)
+        self._star.set_sensitive(True)
+
+        self._transition.set_active(0)
+        self._transition.set_sensitive(True)
+
+        if self._wizard.landingRunway is None:
+            self._runway.set_text("")
+        else:
+            self._runway.set_text(self._wizard.landingRunway)
+        self._runway.set_sensitive(True)
+
+        self._approachType.set_text("")
+        self._approachType.set_sensitive(True)
+
+        self._vref.set_int(None)
+        self._vref.set_sensitive(True)
+
+        i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
+        speedUnit = xstr("label" + i18nSpeedUnit)
+        self._vrefUnit.set_text(speedUnit)
+
+        self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
+                                           i18nSpeedUnit))
+
+        self._updateForwardButton()
+
+        self._active = True
+
+    def flightEnded(self):
+        """Called when the flight has ended."""
+        super(LandingPage, self).flightEnded()
+        self._flightEnded = True
+        self._updateForwardButton()
+
+    def changeMETAR(self, metar):
+        """Change the METAR as a result of an edit on one of the other
+        pages."""
+        if self._active:
+            print "LandingPage.changeMETAR"
+            self._updatingMETAR = True
+            self._metar.get_buffer().set_text(metar, -1)
+            self._updatingMETAR = False
+
+            self._updateForwardButton()
+
+    def _updateForwardButton(self):
+        """Update the sensitivity of the forward button."""
+        sensitive = self._flightEnded and \
+                    self._metar.get_text()!="" and \
+                    (self.star is not None or
+                     self.transition is not None) and \
+                    self._runway.get_text()!="" and \
+                    self._approachType.get_text()!="" and \
+                    self.vref is not None
+        self._button.set_sensitive(sensitive)
+
+    def _upperChanged(self, entry):
+        """Called for entry widgets that must be converted to uppercase."""
+        entry.set_text(entry.get_text().upper())
+        self._updateForwardButton()
+
+    def _upperChangedComboBox(self, comboBox):
+        """Called for combo box widgets that must be converted to uppercase."""
+        if comboBox.get_active()==-1:
+            entry = comboBox.get_child()
+            entry.set_text(entry.get_text().upper())
+        self._updateForwardButton()
+
+    def _vrefChanged(self, widget, value):
+        """Called when the Vref has changed."""
+        self._updateForwardButton()
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _forwardClicked(self, button):
+        """Called when the forward button is clicked."""
+        wizard = self._wizard
+
+        aircraft = wizard.gui.flight.aircraft
+        aircraft.updateVRef()
+        aircraft.updateLandingAntiIce()
+        if wizard.gui.config.onlineGateSystem and \
+           wizard.loggedIn and not self._completed and \
+           wizard.bookedFlight.arrivalICAO=="LHBP" and \
+           not wizard.entranceExam:
+            wizard.getFleet(callback = self._fleetRetrieved, force = True)
+        elif wizard.entranceExam:
+            self._handleEntranceExamDone()
+        else:
+            wizard.nextPage()
+
+    def _fleetRetrieved(self, fleet):
+        """Callback for the fleet retrieval."""
+        self._wizard.nextPage()
+
+    def _metarChanged(self, entry):
+        """Called when the METAR has changed."""
+        print "LandingPage.metarChanged", self._updatingMETAR
+        if not self._updatingMETAR:
+            self._updateForwardButton()
+            self._wizard.metarChanged(entry.get_text(), self)
+
+    def _metarInserted(self, buffer, position, text, length):
+        """Called when new characters are inserted into the METAR.
+
+        It uppercases all characters."""
+        print "LandingPage.metarInserted", self._updatingMETAR
+        if not self._updatingMETAR:
+            self._updatingMETAR = True
+
+            buffer.delete_text(position, length)
+            buffer.insert_text(position, text.upper(), length)
+
+            self._updatingMETAR = False
+
+    def _handleEntranceExamDone(self):
+        """Handle the end of the entrance exam.
+
+        If the there was a NO-GO fault, notify the user that exam is a failure
+        and take them back to the student page. Otherwise congratulate, update
+        the database to reflect that the exam has been taken and go back to the
+        student page."""
+        self._wizard.jumpPage("chkfinish")
+
+#-----------------------------------------------------------------------------
+
+class PIREPSaveHelper(object):
+    """A helper to use for saving PIREPs."""
+    def __init__(self, wizard):
+        """Construct the helper."""
+        super(PIREPSaveHelper, self).__init__()
+
+        self._wizard = wizard
+
+        self._lastSavePath = None
+        self._savePIREPDialog = None
+
+    def addButton(self, page):
+        """Add a button to save the PIREP to the given page."""
+        return page.addButton(xstr("finish_save"), sensitive = False,
+                              clicked = self._saveClicked,
+                              tooltip = xstr("finish_save_tooltip"),
+                              clickedArg = page)
+
+    def autoSavePIREP(self, page):
+        """Perform the automatic saving of the PIREP."""
+        self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
+                                          self._getDefaultPIREPName())
+        self._lastSavePath = text2unicode(self._lastSavePath)
+        self._savePIREP(page, automatic = True)
+
+    def _getDefaultPIREPName(self):
+        """Get the default name of the PIREP."""
+        gui = self._wizard.gui
+
+        bookedFlight = gui.bookedFlight
+        tm = time.gmtime()
+
+        pilotID = self._wizard.pilotID
+        if pilotID: pilotID += " "
+        return "%s%s %02d%02d %s-%s.pirep" % \
+               (pilotID, str(bookedFlight.departureTime.date()),
+                tm.tm_hour, tm.tm_min,
+                bookedFlight.departureICAO, bookedFlight.arrivalICAO)
+
+    def _saveClicked(self, button, page):
+        """Called when the Save PIREP button is clicked."""
+        gui = self._wizard.gui
+
+        fileName = self._getDefaultPIREPName()
+
+        dialog = self._getSaveDialog()
+
+        if self._lastSavePath is None:
+            pirepDirectory = gui.config.pirepDirectory
+            if pirepDirectory is not None:
+                dialog.set_current_folder(pirepDirectory)
+        else:
+            dialog.set_current_folder(os.path.dirname(self._lastSavePath))
+
+        dialog.set_current_name(fileName)
+        result = dialog.run()
+        dialog.hide()
+
+        if result==RESPONSETYPE_OK:
+            self._lastSavePath = text2unicode(dialog.get_filename())
+            self._savePIREP(page)
+
+    def _savePIREP(self, page, automatic = False):
+        """Perform the saving of the PIREP."""
+
+        gui = self._wizard.gui
+
+        if automatic:
+            gui.beginBusy(xstr("finish_autosave_busy"))
+
+        pirep = PIREP(gui.flight)
+        error = pirep.save(self._lastSavePath)
+
+        if automatic:
+            gui.endBusy()
+
+        if error:
+            type = MESSAGETYPE_ERROR
+            message = xstr("finish_save_failed")
+            secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
+        else:
+            type = MESSAGETYPE_INFO
+            message = xstr("finish_save_done")
+            if automatic:
+                secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
+            else:
+                secondary = None
+            page.setPIREPSaved()
+
+        dialog = gtk.MessageDialog(parent = gui.mainWindow,
+                                   type = type, message_format = message)
+        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+        dialog.set_title(WINDOW_TITLE_BASE)
+        if secondary is not None:
+            dialog.format_secondary_markup(secondary)
+
+        dialog.run()
+        dialog.hide()
+
+    def _getSaveDialog(self):
+        """Get the PIREP saving dialog.
+
+        If it does not exist yet, create it."""
+        if self._savePIREPDialog is None:
+            gui = self._wizard.gui
+            dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
+                                           xstr("finish_save_title"),
+                                           action = FILE_CHOOSER_ACTION_SAVE,
+                                           buttons = (gtk.STOCK_CANCEL,
+                                                      RESPONSETYPE_CANCEL,
+                                                      gtk.STOCK_OK, RESPONSETYPE_OK),
+                                           parent = gui.mainWindow)
+            dialog.set_modal(True)
+            dialog.set_do_overwrite_confirmation(True)
+
+            filter = gtk.FileFilter()
+            filter.set_name(xstr("file_filter_pireps"))
+            filter.add_pattern("*.pirep")
+            dialog.add_filter(filter)
+
+            filter = gtk.FileFilter()
+            filter.set_name(xstr("file_filter_all"))
+            filter.add_pattern("*.*")
+            dialog.add_filter(filter)
+
+            self._savePIREPDialog = dialog
+
+        return self._savePIREPDialog
+
+#-----------------------------------------------------------------------------
+
+class FinishPage(Page):
+    """Flight finish page."""
+    def __init__(self, wizard, saveHelper):
+        """Construct the finish page."""
+        help = xstr("finish_help") + xstr("finish_help_goodtime")
+        super(FinishPage, self).__init__(wizard, "finish",
+                                         xstr("finish_title"), help)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(10, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        row = 0
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_rating"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._flightRating = gtk.Label()
+        self._flightRating.set_width_chars(8)
+        self._flightRating.set_alignment(0.0, 0.5)
+        self._flightRating.set_use_markup(True)
+        labelAlignment.add(self._flightRating)
+        table.attach(labelAlignment, 1, 2, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_dep_time"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._depTime = gtk.Label()
+        self._depTime.set_width_chars(13)
+        self._depTime.set_alignment(0.0, 0.5)
+        self._depTime.set_use_markup(True)
+        self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
+        labelAlignment.add(self._depTime)
+        table.attach(labelAlignment, 1, 2, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_flight_time"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._flightTime = gtk.Label()
+        self._flightTime.set_width_chars(10)
+        self._flightTime.set_alignment(0.0, 0.5)
+        self._flightTime.set_use_markup(True)
+        labelAlignment.add(self._flightTime)
+        table.attach(labelAlignment, 1, 2, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_block_time"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._blockTime = gtk.Label()
+        self._blockTime.set_width_chars(10)
+        self._blockTime.set_alignment(0.0, 0.5)
+        self._blockTime.set_use_markup(True)
+        labelAlignment.add(self._blockTime)
+        table.attach(labelAlignment, 1, 2, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_arr_time"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._arrTime = gtk.Label()
+        self._arrTime.set_width_chars(13)
+        self._arrTime.set_alignment(0.0, 0.5)
+        self._arrTime.set_use_markup(True)
+        self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
+        labelAlignment.add(self._arrTime)
+        table.attach(labelAlignment, 1, 2, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_distance"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._distanceFlown = gtk.Label()
+        self._distanceFlown.set_width_chars(10)
+        self._distanceFlown.set_alignment(0.0, 0.5)
+        self._distanceFlown.set_use_markup(True)
+        labelAlignment.add(self._distanceFlown)
+        table.attach(labelAlignment, 1, 2, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_fuel"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._fuelUsed = gtk.Label()
+        self._fuelUsed.set_width_chars(10)
+        self._fuelUsed.set_alignment(0.0, 0.5)
+        self._fuelUsed.set_use_markup(True)
+        labelAlignment.add(self._fuelUsed)
+        table.attach(labelAlignment, 1, 2, row, row+1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
+                                       yalign = 0.5, yscale = 0.0)
+        label = gtk.Label(xstr("finish_type"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        self._flightType = createFlightTypeComboBox()
+        self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
+        self._flightType.set_active(0)
+        self._flightType.connect("changed", self._flightTypeChanged)
+        flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        flightTypeAlignment.add(self._flightType)
+        table.attach(flightTypeAlignment, 1, 2, row, row+1)
+        label.set_mnemonic_widget(self._flightType)
+
+        row += 1
+
+        self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
+        self._onlineFlight.set_use_underline(True)
+        self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
+        onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        onlineFlightAlignment.add(self._onlineFlight)
+        table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
+
+        row += 1
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
+                                       yalign = 0.5, yscale = 0.0)
+        self._gateLabel = gtk.Label(xstr("finish_gate"))
+        self._gateLabel.set_use_underline(True)
+        labelAlignment.add(self._gateLabel)
+        table.attach(labelAlignment, 0, 1, row, row+1)
+
+        self._gatesModel = gtk.ListStore(str)
+
+        self._gate = gtk.ComboBox(model = self._gatesModel)
+        renderer = gtk.CellRendererText()
+        self._gate.pack_start(renderer, True)
+        self._gate.add_attribute(renderer, "text", 0)
+        self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
+        self._gate.connect("changed", self._gateChanged)
+        gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
+        gateAlignment.add(self._gate)
+        table.attach(gateAlignment, 1, 2, row, row+1)
+        self._gateLabel.set_mnemonic_widget(self._gate)
+
+        self.addButton(xstr("finish_newFlight"),
+                       sensitive = True,
+                       clicked = self._newFlightClicked,
+                       tooltip = xstr("finish_newFlight_tooltip"),
+                       padding = 16)
+
+        self.addPreviousButton(clicked = self._backClicked)
+
+        self._saveHelper = saveHelper
+        self._saveButton = saveHelper.addButton(self)
+
+        self._tooBigTimeDifference = False
+        self._deferredAutoSave = False
+        self._pirepSaved = False
+        self._pirepSent = False
+
+        self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
+                                          sensitive = False,
+                                          clicked = self._sendClicked,
+                                          tooltip = xstr("sendPIREP_tooltip"))
+
+        # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
+        # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
+        # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
+        # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
+        # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
+        # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
+        # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
+        # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
+
+    @property
+    def flightType(self):
+        """Get the flight type."""
+        index = self._flightType.get_active()
+        return None if index<0 else self._flightType.get_model()[index][1]
+
+    @property
+    def online(self):
+        """Get whether the flight was an online flight or not."""
+        return self._onlineFlight.get_active()
+
+    def activate(self):
+        """Activate the page."""
+        self._deferredAutoSave = False
+        self._pirepSaved = False
+        self._pirepSent = False
+
+        flight = self._wizard.gui._flight
+        rating = flight.logger.getRating()
+        if rating<0:
+            self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
+        else:
+            self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
+
+        flightLength = flight.flightTimeEnd - flight.flightTimeStart
+        self._flightTime.set_markup("<b>%s</b>" % \
+                                    (util.getTimeIntervalString(flightLength),))
+
+        blockLength = flight.blockTimeEnd - flight.blockTimeStart
+        self._blockTime.set_markup("<b>%s</b>" % \
+                                   (util.getTimeIntervalString(blockLength),))
+
+        self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
+                                       (flight.flownDistance,))
+
+        self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
+                                  (flight.startFuel - flight.endFuel,))
+
+        self._flightType.set_active(-1)
+        self._onlineFlight.set_active(self._wizard.loggedIn)
+
+        self._gatesModel.clear()
+        if self._wizard.gui.config.onlineGateSystem and \
+           self._wizard.loggedIn and \
+           self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
+           not self._wizard.entranceExam:
+            occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
+            for gate in lhbpGates.gates:
+                if gate.isAvailable(lhbpGates, occupiedGateNumbers):
+                    self._gatesModel.append([gate.number])
+            self._gateLabel.set_sensitive(True)
+            self._gate.set_sensitive(True)
+            self._gate.set_active(-1)
+        else:
+            self._gateLabel.set_sensitive(False)
+            self._gate.set_sensitive(False)
+
+        self._updateTimes()
+
+    def updateButtons(self):
+        """Update the sensitivity state of the buttons."""
+        gui = self._wizard.gui
+        faultsExplained = gui.faultsFullyExplained
+        timesCorrect = self.flightType is None or \
+                       not self._tooBigTimeDifference or \
+                       gui.hasComments or gui.hasDelayCode
+        sensitive = gui.flight is not None and \
+                    gui.flight.stage==const.STAGE_END and \
+                    self._flightType.get_active()>=0 and \
+                    (self._gatesModel.get_iter_first() is None or
+                     self._gate.get_active()>=0) and \
+                     faultsExplained and timesCorrect
+
+        self._updateHelp(faultsExplained, timesCorrect)
+
+        wasSensitive = self._saveButton.get_sensitive()
+
+        if gui.config.pirepAutoSave and sensitive and not wasSensitive:
+            if gui.isWizardActive():
+                self._saveHelper.autoSavePIREP(self)
+            else:
+                self._deferredAutoSave = True
+
+        if not sensitive:
+            self._deferredAutoSave = False
+
+        self._saveButton.set_sensitive(sensitive)
+        self._sendButton.set_sensitive(sensitive and
+                                       self._wizard.bookedFlight.id is not None)
+
+    def grabDefault(self):
+        """If the page has a default button, make it the default one."""
+        super(FinishPage, self).grabDefault()
+        if self._deferredAutoSave:
+            self._saveHelper.autoSavePIREP(self)
+            self._deferredAutoSave = False
+
+    def setPIREPSaved(self):
+        """Mark the PIREP as saved."""
+        self._pirepSaved = True
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _flightTypeChanged(self, comboBox):
+        """Called when the flight type has changed."""
+        self._updateTimes()
+
+    def _gateChanged(self, comboBox):
+        """Called when the arrival gate has changed."""
+        self.updateButtons()
+
+    def _newFlightClicked(self, button):
+        """Called when the new flight button is clicked."""
+        gui = self._wizard.gui
+        if not self._pirepSent and not self._pirepSaved:
+            dialog = gtk.MessageDialog(parent = gui.mainWindow,
+                                       type = MESSAGETYPE_QUESTION,
+                                       message_format = xstr("finish_newFlight_question"))
+
+            dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
+            dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
+
+            dialog.set_title(WINDOW_TITLE_BASE)
+            result = dialog.run()
+            dialog.hide()
+            if result!=RESPONSETYPE_YES:
+                return
+
+        gui.reset()
+
+    def _sendClicked(self, button):
+        """Called when the Send button is clicked."""
+        pirep = PIREP(self._wizard.gui.flight)
+        self._wizard.gui.sendPIREP(pirep,
+                                   callback = self._handlePIREPSent)
+
+    def _handlePIREPSent(self, returned, result):
+        """Callback for the PIREP sending result."""
+        self._pirepSent = returned and result.success
+        if self._wizard.gui.config.onlineGateSystem and \
+           self._wizard.loggedIn and not self._wizard.entranceExam and \
+           returned and result.success:
+            bookedFlight = self._wizard.bookedFlight
+            if bookedFlight.arrivalICAO=="LHBP":
+                iter = self._gate.get_active_iter()
+                gateNumber = None if iter is None \
+                             else self._gatesModel.get_value(iter, 0)
+
+                status = const.PLANE_PARKING if gateNumber is None \
+                         else const.PLANE_HOME
+            else:
+                gateNumber = None
+                status = const.PLANE_AWAY
+
+            self._wizard.updatePlane(self._planeUpdated,
+                                     bookedFlight.tailNumber,
+                                     status, gateNumber = gateNumber)
+
+    def _planeUpdated(self, success):
+        """Callback for the plane updating."""
+        pass
+
+    def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
+        """Format the departure or arrival time based on the given data as a
+        markup for a label."""
+        realTime = time.gmtime(realTimestamp)
+
+        if warning:
+            colour = "red" if error else "orange"
+            markupBegin = '<span foreground="%s">' % (colour,)
+            markupEnd = '</span>'
+        else:
+            markupBegin = markupEnd = ""
+
+        markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
+                 (markupBegin,
+                  realTime.tm_hour, realTime.tm_min,
+                  scheduledTime.hour, scheduledTime.minute,
+                  markupEnd)
+
+        return markup
+
+    def _updateTimes(self):
+        """Format the flight times and the help text according to the flight
+        type.
+
+        The buttons are also updated.
+        """
+        flight = self._wizard.gui._flight
+        bookedFlight = flight.bookedFlight
+
+        (departureWarning, departureError) = flight.blockTimeStartWrong
+        (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
+
+        if self.flightType==const.FLIGHTTYPE_VIP:
+            departureError = arrivalError = False
+
+        self._tooBigTimeDifference = departureError or arrivalError
+
+        self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
+                                                  flight.blockTimeStart,
+                                                  (departureWarning,
+                                                   departureError)))
+
+        self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
+                                                  flight.blockTimeEnd,
+                                                  (arrivalWarning,
+                                                   arrivalError)))
+
+        self.updateButtons()
+
+    def _updateHelp(self, faultsExplained, timesCorrect):
+        """Update the help text according to the actual situation."""
+        if not faultsExplained:
+            self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
+        elif not timesCorrect:
+            self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
+        else:
+            self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
+
+
+#-----------------------------------------------------------------------------
+
+class CheckFlightFinishPage(Page):
+    """Finish page for a check flight."""
+    def __init__(self, wizard, saveHelper):
+        """Construct the check flight finish page."""
+        super(CheckFlightFinishPage, self).__init__(wizard,
+                                                    "chkfinish",
+                                                    xstr("chkfinish_title"),
+                                                    "")
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 1.0, yscale = 1.0)
+        self._label = gtk.Label()
+        alignment.add(self._label)
+
+        self.setMainWidget(alignment)
+
+        self._saveHelper = saveHelper
+        self._saveButton = saveHelper.addButton(self)
+
+        self._button = self.addNextButton(sensitive = False,
+                                          clicked =  self._forwardClicked)
+
+    def activate(self):
+        """Activate the page."""
+        wizard = self._wizard
+        loginResult = wizard.loginResult
+        gui = wizard.gui
+        rating = gui.flight.logger.getRating()
+
+        if rating>=0:
+            loginResult.checkFlightStatus = True
+
+        firstOfficer = \
+          loginResult.entryExamPassed and loginResult.checkFlightStatus
+
+        if firstOfficer:
+            loginResult.rank = "FO"
+
+        if rating<0:
+            mainMessage = xstr("chkfinish_failed")
+        else:
+            mainMessage = xstr("chkfinish_passed_begin")
+            if firstOfficer:
+                mainMessage += xstr("chkfinish_passed_fo")
+            mainMessage += xstr("chkfinish_passed_end")
+
+        if firstOfficer:
+            nextMessage = xstr("chkfinish_next")
+        else:
+            nextMessage = xstr("chkfinish_next_student_begin")
+            if not loginResult.entryExamPassed and \
+               not loginResult.checkFlightStatus:
+                nextMessage += xstr("chkfinish_next_student_nothing")
+            elif loginResult.entryExamPassed and \
+                 not loginResult.checkFlightStatus:
+                nextMessage += xstr("chkfinish_next_student_no_flight")
+            elif not loginResult.entryExamPassed and \
+                 loginResult.checkFlightStatus:
+                nextMessage += xstr("chkfinish_next_student_no_exam")
+
+        self._label.set_text(mainMessage +
+                             xstr("chkfinish_savepirep") +
+                             nextMessage)
+        self._label.set_use_markup(True)
+        self._label.set_alignment(0.5, 0.0)
+
+        self._saveButton.set_sensitive(True)
+        self._button.set_sensitive(True)
+
+    def _forwardClicked(self, button):
+        """Jump to the student page if there are some tasks to do,
+        or to the flight selection page, if the pilot is allowed to perform
+        MAVA flights."""
+        wizard = self._wizard
+        gui = wizard.gui
+
+        loginResult = wizard.loginResult
+        if loginResult.checkFlightStatus:
+            gui.beginBusy(xstr("chkfinish_updateweb_busy"))
+            gui.webHandler.setCheckFlightPassed(self._checkFlightPassedSetCallback,
+                                                wizard.checkFlightAircraftType)
+        else:
+            self._resetGUI()
+
+    def _checkFlightPassedSetCallback(self, returned, result):
+        """Called when the check flight status has been set."""
+        gobject.idle_add(self._checkFlightPassedSet, returned, result)
+
+    def _checkFlightPassedSet(self, returned, result):
+        """Handle the result of an attempt to set the check flight status."""
+        gui = self._wizard.gui
+
+        gui.endBusy()
+
+        if returned:
+            self._resetGUI()
+        else:
+            dialog = gtk.MessageDialog(parent = gui.mainWindow,
+                                       type = MESSAGETYPE_ERROR,
+                                       message_format =
+                                       xstr("chkfinish_passedset_failed"))
+            dialog.set_title(WINDOW_TITLE_BASE + " - " +
+                             xstr("chkfinish_passedset_failed_title"))
+            dialog.format_secondary_markup(xstr("chkfinish_passedset_failed_secondary"))
+
+            dialog.add_button(xstr("button_ok"), 0)
+
+            dialog.run()
+            dialog.hide()
+
+    def _resetGUI(self):
+        """Reset the GUI."""
+        gui = self._wizard.gui
+        gui.reset()
+
+#-----------------------------------------------------------------------------
+
+class Wizard(gtk.VBox):
+    """The flight wizard."""
+    def __init__(self, gui):
+        """Construct the wizard."""
+        super(Wizard, self).__init__()
+
+        self.gui = gui
+
+        self._pages = []
+        self._currentPage = None
+
+        self._loginPage = LoginPage(self)
+        self._pages.append(self._loginPage)
+        self._flightSelectionPage = FlightSelectionPage(self)
+        self._pages.append(self._flightSelectionPage)
+        self._pages.append(GateSelectionPage(self))
+        self._pages.append(RegisterPage(self))
+        self._studentPage = StudentPage(self)
+        self._pages.append(self._studentPage)
+        self._pages.append(ConnectPage(self))
+        self._payloadPage = PayloadPage(self)
+        self._pages.append(self._payloadPage)
+        self._payloadIndex = len(self._pages)
+        self._pages.append(TimePage(self))
+        self._routePage = RoutePage(self)
+        self._pages.append(self._routePage)
+        self._simBriefSetupPage = SimBriefSetupPage(self)
+        self._pages.append(self._simBriefSetupPage)
+        self._simBriefingPage = SimBriefingPage(self)
+        self._pages.append(self._simBriefingPage)
+        self._pages.append(FuelPage(self))
+        self._departureBriefingPage = BriefingPage(self, True)
+        self._pages.append(self._departureBriefingPage)
+        self._arrivalBriefingPage = BriefingPage(self, False)
+        self._pages.append(self._arrivalBriefingPage)
+        self._arrivalBriefingIndex = len(self._pages)
+        self._takeoffPage = TakeoffPage(self)
+        self._pages.append(self._takeoffPage)
+        self._cruisePage = CruisePage(self)
+        self._pages.append(self._cruisePage)
+        self._landingPage = LandingPage(self)
+        self._pages.append(self._landingPage)
+
+        pirepSaveHelper = PIREPSaveHelper(self)
+
+        self._finishPage = FinishPage(self, pirepSaveHelper)
+        self._pages.append(self._finishPage)
+        self._pages.append(CheckFlightFinishPage(self, pirepSaveHelper))
+
+        self._requestedWidth = None
+        self._requestedHeight = None
+
+        self.connect("size-allocate", self._sizeAllocate)
+
+        for page in self._pages:
+            page.show_all()
+            page.setStyle()
+
+        self._initialize()
+
+    def _sizeAllocate(self, widget, allocation):
+        if self._requestedWidth is not None and \
+           self._requestedHeight is not None:
+           return
+
+        if self._currentPage is not None:
+            self.remove(self._pages[self._currentPage])
+
+        maxWidth = 0
+        maxHeight = 0
+        for page in self._pages:
+            self.add(page)
+            self.show_all()
+            pageSizeRequest = page.size_request()
+            width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
+            height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
+            maxWidth = max(maxWidth, width)
+            maxHeight = max(maxHeight, height)
+            self.remove(page)
+
+        if self._currentPage is not None:
+            self.add(self._pages[self._currentPage])
+
+        self._requestedWidth = maxWidth
+        self._requestedHeight = maxHeight
+        self.set_size_request(maxWidth, maxHeight)
+
+    @property
+    def pilotID(self):
+        """Get the pilot ID, if given."""
+        return self._loginPage.pilotID
+
+    @property
+    def entranceExam(self):
+        """Get whether an entrance exam is about to be taken."""
+        return self._loginResult is not None and self._loginResult.rank=="STU"
+
+    @property
+    def loggedIn(self):
+        """Indicate if there was a successful login."""
+        return self._loginResult is not None
+
+    @property
+    def loginResult(self):
+        """Get the login result."""
+        return self._loginResult
+
+    @property
+    def checkFlightAircraftType(self):
+        """Get the type of the aircraft used to perform the check flight."""
+        return self._studentPage.aircraftType
+
+    def setCurrentPage(self, index, finalize = False, fromPageShift = None):
+        """Set the current page to the one with the given index.
+
+        @param fromPageShift if given, the relative index of one of the
+        previous pages that should be used as the from-page of the next
+        page. E.g. if fromPageShift is 1, the previous page will be the
+        from-page."""
+        assert index < len(self._pages)
+
+        fromPage = self._currentPage
+        if fromPage is not None:
+            page = self._pages[fromPage]
+            if finalize and not page._completed:
+                page.complete()
+            self.remove(page)
+            if fromPageShift is not None:
+                fromPage -= fromPageShift
+
+        self._currentPage = index
+        page = self._pages[index]
+        self.add(page)
+        if page._fromPage is None:
+            page._fromPage = fromPage
+            page.initialize()
+        self.show_all()
+        if fromPage is not None:
+            self.grabDefault()
+
+    @property
+    def bookedFlight(self):
+        """Get the booked flight selected."""
+        return self._bookedFlight
+
+    @property
+    def numCrew(self):
+        """Get the number of crew members."""
+        return self._payloadPage.numCrew
+
+    @property
+    def numPassengers(self):
+        """Get the number of passengers."""
+        return self._payloadPage.numPassengers
+
+    @property
+    def bagWeight(self):
+        """Get the baggage weight."""
+        return self._payloadPage.bagWeight
+
+    @property
+    def cargoWeight(self):
+        """Get the cargo weight."""
+        return self._payloadPage.cargoWeight
+
+    @property
+    def mailWeight(self):
+        """Get the mail weight."""
+        return self._payloadPage.mailWeight
+
+    @property
+    def zfw(self):
+        """Get the calculated ZFW value."""
+        return 0 if self._bookedFlight is None \
+               else self._payloadPage.calculateZFW()
+
+    @property
+    def filedCruiseLevel(self):
+        """Get the filed cruise level."""
+        return self._routePage.filedCruiseLevel
+
+    @property
+    def filedCruiseAltitude(self):
+        """Get the filed cruise altitude."""
+        return self._routePage.filedCruiseLevel * 100
+
+    @property
+    def cruiseAltitude(self):
+        """Get the cruise altitude."""
+        level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
+                else self._routePage.filedCruiseLevel
+        return level * 100
+
+    @property
+    def loggableCruiseAltitude(self):
+        """Get the cruise altitude that can be logged."""
+        if self._cruisePage.activated:
+            return self._cruisePage.loggableCruiseLevel * 100
+        else:
+            return 0
+
+    @property
+    def route(self):
+        """Get the route."""
+        return self._routePage.route
+
+    @property
+    def alternate(self):
+        """Get the ICAO code of the alternate airport."""
+        return self._routePage.alternate
+
+    @property
+    def departureMETAR(self):
+        """Get the METAR of the departure airport."""
+        return self._departureBriefingPage.metar
+
+    @property
+    def arrivalMETAR(self):
+        """Get the METAR of the arrival airport."""
+        return self._arrivalBriefingPage.metar
+
+    @property
+    def departureRunway(self):
+        """Get the departure runway."""
+        return self._takeoffPage.runway
+
+    @property
+    def sid(self):
+        """Get the SID."""
+        return self._takeoffPage.sid
+
+    @property
+    def v1(self):
+        """Get the V1 speed."""
+        return self._takeoffPage.v1
+
+    @property
+    def vr(self):
+        """Get the Vr speed."""
+        return self._takeoffPage.vr
+
+    @property
+    def v2(self):
+        """Get the V2 speed."""
+        return self._takeoffPage.v2
+
+    @property
+    def derate(self):
+        """Get the derate value."""
+        return self._takeoffPage.derate
+
+    @property
+    def takeoffAntiIceOn(self):
+        """Get whether the anti-ice system was on during take-off."""
+        return self._takeoffPage.antiIceOn
+
+    @takeoffAntiIceOn.setter
+    def takeoffAntiIceOn(self, value):
+        """Set anti-ice on indicator."""
+        self._takeoffPage.antiIceOn = value
+
+    @property
+    def rtoIndicated(self):
+        """Get whether the pilot has indicated that an RTO has occured."""
+        return self._takeoffPage.rtoIndicated
+
+    @property
+    def arrivalRunway(self):
+        """Get the arrival runway."""
+        return self._landingPage.runway
+
+    @property
+    def star(self):
+        """Get the STAR."""
+        return self._landingPage.star
+
+    @property
+    def transition(self):
+        """Get the transition."""
+        return self._landingPage.transition
+
+    @property
+    def approachType(self):
+        """Get the approach type."""
+        return self._landingPage.approachType
+
+    @property
+    def vref(self):
+        """Get the Vref speed."""
+        return self._landingPage.vref
+
+    @property
+    def landingAntiIceOn(self):
+        """Get whether the anti-ice system was on during landing."""
+        return self._landingPage.antiIceOn
+
+    @landingAntiIceOn.setter
+    def landingAntiIceOn(self, value):
+        """Set anti-ice on indicator."""
+        self._landingPage.antiIceOn = value
+
+    @property
+    def flightType(self):
+        """Get the flight type."""
+        return self._finishPage.flightType
+
+    @property
+    def online(self):
+        """Get whether the flight was online or not."""
+        return self._finishPage.online
+
+    @property
+    def usingSimBrief(self):
+        """Indicate if we are using a SimBrief briefing or not."""
+        return self._usingSimBrief
+
+    @usingSimBrief.setter
+    def usingSimBrief(self, x):
+        """Set whether we are using a SimBrief briefing or not."""
+        self._usingSimBrief = x
+
+    def nextPage(self, finalize = True):
+        """Go to the next page."""
+        nextPageID = self._pages[self._currentPage].nextPageID
+        self.jumpPage(1 if nextPageID is None else nextPageID, finalize)
+
+    def jumpPage(self, countOrID, finalize = True, fromPageShift = None):
+        """Go to the page which is 'count' pages after the current one."""
+        if isinstance(countOrID, str):
+            targetIndex = self._getIndexOf(countOrID)
+        else:
+            targetIndex = self._currentPage + countOrID
+        self.setCurrentPage(targetIndex,
+                            finalize = finalize, fromPageShift = fromPageShift)
+
+    def grabDefault(self):
+        """Make the default button of the current page the default."""
+        self._pages[self._currentPage].grabDefault()
+
+    def connected(self, fsType, descriptor):
+        """Called when the connection could be made to the simulator."""
+        self.nextPage()
+
+    def reset(self, loginResult):
+        """Resets the wizard to go back to the login page."""
+        self._initialize(keepLoginResult = loginResult is None,
+                         loginResult = loginResult)
+
+    def setStage(self, stage):
+        """Set the flight stage to the given one."""
+        if stage!=const.STAGE_END:
+            self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
+
+        if stage==const.STAGE_TAKEOFF:
+            self._takeoffPage.allowForward()
+        elif stage==const.STAGE_LANDING:
+            if not self._arrivalBriefingPage.metarEdited:
+                print "Downloading arrival METAR again"
+                self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
+                                              [self._bookedFlight.arrivalICAO])
+
+        elif stage==const.STAGE_END:
+            for page in self._pages:
+                page.flightEnded()
+
+    def _initialize(self, keepLoginResult = False, loginResult = None):
+        """Initialize the wizard."""
+        if not keepLoginResult:
+            self._loginResult = loginResult
+
+        self._loginCallback = None
+
+        self._fleet = None
+        self._fleetCallback = None
+
+        self._bookedFlight = None
+        self._departureGate = "-"
+        self._fuelData = None
+        self._departureNOTAMs = None
+        self._departureMETAR = None
+        self._arrivalNOTAMs = None
+        self._arrivalMETAR = None
+        self._usingSimBrief = None
+        self.takeoffRunway = None
+        self.landingRunway = None
+
+        firstPage = 0 if self._loginResult is None else 1
+        for page in self._pages[firstPage:]:
+            page.reset()
+
+        self.setCurrentPage(firstPage)
+        #self.setCurrentPage(10)
+
+    def login(self, callback, pilotID, password):
+        """Called when the login button was clicked."""
+        self._loginCallback = callback
+        if pilotID is None:
+            loginResult = self._loginResult
+            assert loginResult is not None and loginResult.loggedIn
+            pilotID = loginResult.pilotID
+            password = loginResult.password
+            busyMessage = xstr("reload_busy")
+        else:
+            self._loginResult = None
+            busyMessage = xstr("login_busy")
+
+        self.gui.beginBusy(busyMessage)
+
+        self.gui.webHandler.login(self._loginResultCallback,
+                                  pilotID, password)
+
+    def reloadFlights(self, callback):
+        """Reload the flights from the MAVA server."""
+        self.login(callback, None, None)
+
+    def addFlight(self, bookedFlight):
+        """Add the given booked flight to the flight selection page."""
+        self._flightSelectionPage.addFlight(bookedFlight)
+
+    def reflyFlight(self, bookedFlight):
+        """Add the given booked flight to the flight selection page."""
+        self._removePendingFlight(bookedFlight)
+        self._flightSelectionPage._reflyFlight(bookedFlight)
+
+    def deleteFlight(self, bookedFlight):
+        """Remove the given flight from the pending flight list."""
+        self._removePendingFlight(bookedFlight)
+        self._flightSelectionPage._updatePending()
+
+    def cancelFlight(self, reloadCallback):
+        """Cancel the flight.
+
+        If it is an entry exam flight, we go back to the student page.
+        Otherwise we reload the flights."""
+        if self.entranceExam:
+            self.reset(None)
+            self.jumpPage("student")
+        else:
+            self.reloadFlights(reloadCallback)
+
+    def cruiseLevelChanged(self):
+        """Called when the cruise level is changed."""
+        return self.gui.cruiseLevelChanged()
+
+    def metarChanged(self, metar, originator):
+        """Called when a METER is changed on on of the pages.
+
+        originator is the page that originated the changed. It will be used to
+        determine which METAR (departure or arrival) has changed."""
+        metar = metar.upper()
+        if originator in [self._departureBriefingPage, self._takeoffPage]:
+            self.departureMETARChanged(metar, originator)
+        else:
+            self.arrivalMETARChanged(metar, originator)
+
+    def departureMETARChanged(self, metar, originator):
+        """Called when the departure METAR has been edited on one of the
+        pages.
+
+        originator is the page that originated the change. It will not be
+        called to set the METAR, while others will be."""
+        for page in [self._departureBriefingPage, self._takeoffPage]:
+            if page is not originator:
+                page.changeMETAR(metar)
+
+    def arrivalMETARChanged(self, metar, originator):
+        """Called when the arrival METAR has been edited on one of the
+        pages.
+
+        originator is the page that originated the change. It will not be
+        called to set the METAR, while others will be."""
+        for page in [self._arrivalBriefingPage, self._landingPage]:
+            if page is not originator:
+                page.changeMETAR(metar)
+
+    def _removePendingFlight(self, flight):
+        """Remove the given pending flight from the login result."""
+        for flights in [self._loginResult.reportedFlights,
+                        self._loginResult.rejectedFlights]:
+            for f in flights:
+                if f.id==flight.id:
+                    flights.remove(f)
+                    return
+
+    def _loginResultCallback(self, returned, result):
+        """The login result callback, called in the web handler's thread."""
+        gobject.idle_add(self._handleLoginResult, returned, result)
+
+    def _handleLoginResult(self, returned, result):
+        """Handle the login result."""
+        self.gui.endBusy()
+        isReload = self._loginResult is not None
+        if returned:
+            if result.loggedIn:
+                self._loginResult = result
+                self.gui.loginSuccessful()
+            else:
+                if isReload:
+                    message = xstr("reload_failed")
+                else:
+                    message = xstr("login_entranceExam_invalid"
+                                   if self.entranceExam else
+                                   xstr("login_invalid"))
+                dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
+                                           type = MESSAGETYPE_ERROR,
+                                           message_format = message)
+                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+                dialog.set_title(WINDOW_TITLE_BASE)
+                if isReload:
+                    secondary = xstr("reload_failed_sec")
+                else:
+                    secondary = xstr("login_entranceExam_invalid_sec"
+                                     if self.entranceExam else
+                                     xstr("login_invalid_sec"))
+                dialog.format_secondary_markup(secondary)
+                dialog.run()
+                dialog.hide()
+        else:
+            message = xstr("reload_failconn") if isReload \
+                      else xstr("login_failconn")
+            dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
+                                       type = MESSAGETYPE_ERROR,
+                                       message_format = message)
+            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+            dialog.set_title(WINDOW_TITLE_BASE)
+            secondary = xstr("reload_failconn_sec") if isReload \
+                        else xstr("login_failconn_sec")
+            dialog.format_secondary_markup(secondary)
+
+            dialog.run()
+            dialog.hide()
+
+        callback = self._loginCallback
+        self._loginCallback = None
+        callback(returned, result)
+
+    def getFleet(self, callback, force = False):
+        """Get the fleet via the GUI and call the given callback."""
+        self._fleetCallback = callback
+        self.gui.getFleet(callback = self._fleetRetrieved, force = force)
+
+    def _fleetRetrieved(self, fleet):
+        """Callback for the fleet retrieval."""
+        self._fleet = fleet
+        if self._fleetCallback is not None:
+            self._fleetCallback(fleet)
+        self._fleetCallback = None
+
+    def updatePlane(self, callback, tailNumber, status, gateNumber = None):
+        """Update the given plane's gate information."""
+        self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
+                             callback = callback)
+
+    def updateRTO(self):
+        """Update the RTO state.
+
+        The RTO checkbox will be enabled if the flight has an RTO state and the
+        comments field contains some text."""
+        flight = self.gui.flight
+        rtoEnabled = flight is not None and flight.hasRTO and \
+                     self.gui.hasComments
+        self._takeoffPage.setRTOEnabled(rtoEnabled)
+
+    def commentsChanged(self):
+        """Called when the comments have changed."""
+        self.updateRTO()
+        self._finishPage.updateButtons()
+
+    def delayCodesChanged(self):
+        """Called when the delay codes have changed."""
+        self._finishPage.updateButtons()
+
+    def faultExplanationsChanged(self):
+        """Called when the faults and their explanations have changed."""
+        self._finishPage.updateButtons()
+
+    def rtoToggled(self, indicated):
+        """Called when the RTO indication has changed."""
+        self.gui.rtoToggled(indicated)
+
+    def _connectSimulator(self, simulatorType):
+        """Connect to the simulator."""
+        self.gui.connectSimulator(self._bookedFlight, simulatorType)
+
+    def _arrivalMETARCallback(self, returned, result):
+        """Called when the METAR of the arrival airport is retrieved."""
+        gobject.idle_add(self._handleArrivalMETAR, returned, result)
+
+    def _handleArrivalMETAR(self, returned, result):
+        """Called when the METAR of the arrival airport is retrieved."""
+        icao = self._bookedFlight.arrivalICAO
+        if returned and icao in result.metars:
+            metar = result.metars[icao]
+            if metar!="":
+                self._arrivalBriefingPage.setMETAR(metar)
+
+    def _getIndexOf(self, pageID):
+        """Get the index for the given page ID.
+
+        It is an assertion failure if the ID is not found."""
+        for index in range(0, len(self._pages)):
+            page = self._pages[index]
+            if page.id==pageID:
+                return index
+        assert False
+
+#-----------------------------------------------------------------------------
Index: src/mlx/gui/flight.py
===================================================================
--- src/mlx/gui/flight.py	(revision 865)
+++ 	(revision )
@@ -1,5990 +1,0 @@
-# -*- encoding: utf-8 -*-
-
-from mlx.gui.common import *
-import mlx.gui.cef as cef
-from mlx.gui.flightlist import ColumnDescriptor, FlightList, PendingFlightsWindow
-
-import mlx.const as const
-import mlx.fs as fs
-import mlx.acft as acft
-from mlx.flight import Flight
-from mlx.checks import PayloadChecker
-from mlx.gates import lhbpGates
-import mlx.util as util
-from mlx.pirep import PIREP
-from mlx.i18n import xstr, getLanguage
-from mlx.sound import startSound
-import mlx.web as web
-
-import datetime
-import time
-import os
-import tempfile
-import threading
-import re
-import webbrowser
-
-#-----------------------------------------------------------------------------
-
-## @package mlx.gui.flight
-#
-# The flight "wizard".
-#
-# This module implements the main tab of the application, the flight
-# wizard. The wizard consists of \ref Page "pages", that come one after the
-# other. As some pages might be skipped, the pages dynamically store the index
-# of the previous page so that going back to it is simpler. The \ref
-# Page.activate "activate" function is called before a page is first shown
-# during a flight. This function should initialize the page's controls and fill
-# it with initial data. When a page is left for the first time, its \ref
-# Page.finalize "finalize" function is called. It should set those controls
-# insensitive, that will not be available if the user comes back to this page.
-#
-# Each page has a title at the top displayed in inverted colors and a big
-# font. There is a help text below it centered, that shortly describes what is
-# expected on the page. There can be two help texts: one shown when the page is
-# first displayed during a flight, another shown when the user goes back to the
-# page. The main content area is below this, also centered. Finally, there are
-# some buttons at the bottom on the right. As some buttons occur very
-# frequently, there are functions to add them (\ref Page.addCancelFlightButton
-# "addCancelFlightButton", \ref Page.addPreviousButton "addPreviousButton" and
-# \ref Page.addNextButton "addNextButton".
-#
-# The \ref Wizard class is the main class to collect the pages. It also stores
-# some data passed from one page to another and provides properties to access
-# data items set via the wizard pages.
-
-#-----------------------------------------------------------------------------
-
-comboModel = gtk.ListStore(gobject.TYPE_STRING)
-comboModel.append(("N/A",))
-comboModel.append(("VECTORS",))
-
-#-----------------------------------------------------------------------------
-
-class Page(gtk.Alignment):
-    """A page in the flight wizard."""
-    def __init__(self, wizard, id, title, help, completedHelp = None):
-        """Construct the page."""
-        super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
-                                   xscale = 1.0, yscale = 1.0)
-        self.set_padding(padding_top = 4, padding_bottom = 4,
-                         padding_left = 12, padding_right = 12)
-
-        frame = gtk.Frame()
-        self.add(frame)
-
-        self._vbox = gtk.VBox()
-        self._vbox.set_homogeneous(False)
-        frame.add(self._vbox)
-
-        eventBox = gtk.EventBox()
-
-        alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
-
-        titleLabel = gtk.Label(title)
-        titleLabel.modify_font(pango.FontDescription("bold 24"))
-        alignment.set_padding(padding_top = 4, padding_bottom = 4,
-                              padding_left = 6, padding_right = 0)
-
-        alignment.add(titleLabel)
-        eventBox.add(alignment)
-
-        self._vbox.pack_start(eventBox, False, False, 0)
-
-        self._titleEventBox = eventBox
-        self._titleLabel = titleLabel
-
-        mainBox = gtk.VBox()
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 16, padding_bottom = 16,
-                              padding_left = 16, padding_right = 16)
-        alignment.add(mainBox)
-        self._vbox.pack_start(alignment, True, True, 0)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.set_padding(padding_top = 0, padding_bottom = 16,
-                              padding_left = 0, padding_right = 0)
-
-        self._help = help
-        self._completedHelp = completedHelp
-
-        if self._completedHelp is None or \
-           len(help.splitlines())>=len(completedHelp.splitlines()):
-            longerHelp = help
-        else:
-            longerHelp = completedHelp
-
-        self._helpLabel = gtk.Label(longerHelp)
-        # FIXME: should be a constant in common
-        self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
-                                    else gtk.JUSTIFY_CENTER)
-        self._helpLabel.set_use_markup(True)
-        alignment.add(self._helpLabel)
-        mainBox.pack_start(alignment, False, False, 0)
-
-        self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                            xscale = 1.0, yscale = 1.0)
-        mainBox.pack_start(self._mainAlignment, True, True, 0)
-
-        buttonAlignment =  gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
-        buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
-                                    padding_left = 16, padding_right = 16)
-
-        self._buttonBox = gtk.HBox()
-        self._buttonBox.set_homogeneous(False)
-        self._defaultButton = None
-        buttonAlignment.add(self._buttonBox)
-
-        self._vbox.pack_start(buttonAlignment, False, False, 0)
-
-        self._wizard = wizard
-        self._id = id
-        self._nextPageID = None
-
-        self._cancelFlightButton = None
-
-        self._completed = False
-        self._fromPage = None
-
-    @property
-    def id(self):
-        """Get the identifier of the page."""
-        return self._id
-
-    @property
-    def nextPageID(self):
-        """Get the identifier of the next page, if set."""
-        return self._nextPageID
-
-    @nextPageID.setter
-    def nextPageID(self, nextPageID):
-        """Set the identifier of the next page."""
-        self._nextPageID = nextPageID
-
-    def setMainWidget(self, widget):
-        """Set the given widget as the main one."""
-        self._mainAlignment.add(widget)
-
-    def addButton(self, label, default = False, sensitive = True,
-                  tooltip = None, clicked = None, padding = 4,
-                  clickedArg = None):
-        """Add a button with the given label.
-
-        Return the button object created."""
-        button = gtk.Button(label)
-        self._buttonBox.pack_start(button, False, False, padding)
-        button.set_use_underline(True)
-        if default:
-            button.set_can_default(True)
-            self._defaultButton = button
-        button.set_sensitive(sensitive)
-        if tooltip is not None:
-            button.set_tooltip_text(tooltip)
-        if clicked is not None:
-            if clickedArg is None:
-                button.connect("clicked", clicked)
-            else:
-                button.connect("clicked", clicked, clickedArg)
-        return button
-
-    def addCancelFlightButton(self):
-        """Add the 'Cancel flight' button to the page."""
-        self._cancelFlightButton = \
-            self.addButton(xstr("button_cancelFlight"),
-                           sensitive = True,
-                           tooltip = xstr("button_cancelFlight_tooltip"),
-                           clicked = self._cancelFlight,
-                           padding = 16)
-        return self._cancelFlightButton
-
-    def addPreviousButton(self, sensitive = True, clicked = None):
-        """Add the 'Next' button to the page."""
-        return self.addButton(xstr("button_previous"),
-                              sensitive = sensitive,
-                              tooltip = xstr("button_previous_tooltip"),
-                              clicked = clicked)
-
-    def addNextButton(self, default = True, sensitive = True,
-                      clicked = None):
-        """Add the 'Next' button to the page."""
-        return self.addButton(xstr("button_next"),
-                              default = default,
-                              sensitive = sensitive,
-                              tooltip = xstr("button_next_tooltip"),
-                              clicked = clicked)
-
-    def setStyle(self):
-        """Set the styles of some of the items on the page."""
-        if pygobject:
-            context = self.get_style_context()
-            color = context.get_background_color(gtk.StateFlags.SELECTED)
-            self._titleEventBox.modify_bg(0, color.to_color())
-            color = context.get_color(gtk.StateFlags.SELECTED)
-            self._titleLabel.modify_fg(0, color.to_color())
-        else:
-            style = self.rc_get_style()
-            self._titleEventBox.modify_bg(0, style.bg[3])
-            self._titleLabel.modify_fg(0, style.fg[3])
-
-    def initialize(self):
-        """Initialize the page.
-
-        It sets up the primary help, and calls the activate() function."""
-        self._helpLabel.set_markup(self._help)
-        self._helpLabel.set_sensitive(True)
-        self.activate()
-
-    def activate(self):
-        """Called when this page becomes active.
-
-        This default implementation does nothing."""
-        pass
-
-    def setHelp(self, help):
-        """Set the help string."""
-        self._help = help
-        if not self._completed:
-            self._helpLabel.set_markup(self._help)
-            self._helpLabel.set_sensitive(True)
-
-    def complete(self):
-        """Called when the page is completed.
-
-        It greys out/changes the help text and then calls finalize()."""
-        self.finalize()
-        if self._completedHelp is None:
-            self._helpLabel.set_sensitive(False)
-        else:
-            self._helpLabel.set_markup(self._completedHelp)
-        self._completed = True
-
-    def finalize(self):
-        """Called when the page is finalized."""
-        pass
-
-    def grabDefault(self):
-        """If the page has a default button, make it the default one."""
-        if self._defaultButton is not None:
-            self._defaultButton.grab_default()
-
-    def reset(self):
-        """Reset the page if the wizard is reset."""
-        self._completed = False
-        self._fromPage = None
-        if self._cancelFlightButton is not None:
-            self._cancelFlightButton.set_sensitive(True)
-
-    def goBack(self):
-        """Go to the page we were invoked from."""
-        assert self._fromPage is not None
-
-        self._wizard.setCurrentPage(self._fromPage, finalize = False)
-
-    def flightEnded(self):
-        """Called when the flight has ended.
-
-        This default implementation disables the cancel flight button."""
-        if self._cancelFlightButton is not None:
-            self._cancelFlightButton.set_sensitive(False)
-
-    def _cancelFlight(self, button):
-        """Called when the Cancel flight button is clicked."""
-        self._wizard.gui.cancelFlight()
-
-#-----------------------------------------------------------------------------
-
-class LoginPage(Page):
-    """The login page."""
-    def __init__(self, wizard):
-        """Construct the login page."""
-        super(LoginPage, self).__init__(wizard, "login",
-                                        xstr("login"), xstr("loginHelp"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(3, 2)
-        table.set_row_spacings(4)
-        table.set_col_spacings(32)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("label_pilotID"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 0, 1)
-
-        self._pilotID = gtk.Entry()
-        self._pilotID.connect("changed", self._pilotIDChanged)
-        self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
-        table.attach(self._pilotID, 1, 2, 0, 1)
-        label.set_mnemonic_widget(self._pilotID)
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("label_password"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 1, 2)
-
-        self._password = gtk.Entry()
-        self._password.set_visibility(False)
-        self._password.connect("changed", self._setControls)
-        self._password.set_tooltip_text(xstr("login_password_tooltip"))
-        table.attach(self._password, 1, 2, 1, 2)
-        label.set_mnemonic_widget(self._password)
-
-        self._rememberButton = gtk.CheckButton(xstr("remember_password"))
-        self._rememberButton.set_use_underline(True)
-        self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
-        table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
-
-        self.addButton(xstr("button_login_register"),
-                       clicked = self._registerClicked,
-                       tooltip = xstr("button_login_register_tooltip"))
-
-        self.addButton(xstr("button_offline"),
-                       clicked = self._offlineClicked,
-                       tooltip = xstr("button_offline_tooltip"))
-
-        self._loginButton = self.addButton(xstr("button_login"), default = True)
-        self._loginButton.connect("clicked", self._loginClicked)
-        self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
-
-
-    @property
-    def pilotID(self):
-        """Get the pilot ID, if given."""
-        return self._pilotID.get_text()
-
-    def activate(self):
-        """Activate the page."""
-        config = self._wizard.gui.config
-        self._pilotID.set_text(config.pilotID)
-        self._password.set_text(config.password)
-        self._rememberButton.set_active(config.rememberPassword)
-        self._setControls(None)
-
-    def _pilotIDChanged(self, entry):
-        """Called when the pilot ID has changed.
-
-        It sets the text to upper-case and calls _setControls to update other
-        stuff."""
-        entry.set_text(entry.get_text().upper())
-        self._setControls(entry)
-
-    def _setControls(self, entry = None):
-        """Set the sensitivity of the various controls.
-
-        The login button is sensitive only if both the pilot ID and the
-        password fields contain values.
-
-        The password field is sensitive only, if the entrance exam checkbox is
-        not selected.
-
-        The remember password checkbox is sensitive only, if the password field
-        contains text.
-
-        The entrance exam checkbox is sensitive only, if the pilot ID is not
-        empty."""
-        pilotID = self._pilotID.get_text()
-        password = self._password.get_text()
-        self._rememberButton.set_sensitive(password!="")
-        self._loginButton.set_sensitive(pilotID!="" and password!="")
-
-    def _registerClicked(self, button):
-        """Called when the Register button was clicked."""
-        self._wizard.jumpPage("register")
-
-    def _offlineClicked(self, button):
-        """Called when the offline button was clicked."""
-        print "mlx.flight.LoginPage: offline flight selected"
-        self._wizard.nextPage()
-
-    def _loginClicked(self, button):
-        """Called when the login button was clicked."""
-        print "mlx.flight.LoginPage: logging in"
-        self._wizard.login(self._handleLoginResult,
-                           self._pilotID.get_text(),
-                           self._password.get_text())
-
-    def _handleLoginResult(self, returned, result):
-        """Handle the login result."""
-        self._loginButton.set_sensitive(True)
-        if returned and result.loggedIn:
-            config = self._wizard.gui.config
-
-            config.pilotID = self._pilotID.get_text()
-
-            rememberPassword = self._rememberButton.get_active()
-            config.password = result.password if rememberPassword else ""
-
-            config.rememberPassword = rememberPassword
-
-            config.save()
-            if result.rank=="STU":
-                self._wizard.jumpPage("student")
-            else:
-                self._wizard.nextPage()
-
-#-----------------------------------------------------------------------------
-
-class FlightSelectionPage(Page):
-    """The page to select the flight."""
-    def __init__(self, wizard):
-        """Construct the flight selection page."""
-        help = xstr("flightsel_help")
-        completedHelp = xstr("flightsel_chelp")
-        super(FlightSelectionPage, self).__init__(wizard, "flightsel",
-                                                  xstr("flightsel_title"),
-                                                  help, completedHelp = completedHelp)
-
-        mainBox = gtk.HBox()
-        mainBox.set_homogeneous(False)
-
-        leftVBox = gtk.VBox()
-
-        alignment = gtk.Alignment(xscale = 1.0)
-        alignment.set_size_request(150, 0)
-
-        leftVBox.pack_start(alignment, False, False, 0)
-
-        mainBox.pack_start(leftVBox, True, True, 0)
-
-        self._flightList = FlightList(popupMenuProducer =
-                                      self._createListPopupMenu,
-                                      widthRequest = 400)
-        self._flightList.connect("row-activated", self._rowActivated)
-        self._flightList.connect("selection-changed", self._selectionChanged)
-
-        mainBox.pack_start(self._flightList, False, False, 8)
-
-        flightButtonBox = gtk.VBox()
-
-        alignment = gtk.Alignment(xscale = 1.0)
-        alignment.set_size_request(150, 0)
-        flightButtonBox.pack_start(alignment, False, False, 0)
-
-        flightButtonWidthAlignment = gtk.Alignment(xscale=0.5, yscale=0.0,
-                                                   xalign=0.0, yalign=0.0)
-        flightButtonWidthBox = gtk.VBox()
-
-        self._saveButton = gtk.Button(xstr("flightsel_save"))
-        self._saveButton.set_use_underline(True)
-        self._saveButton.set_sensitive(False)
-        self._saveButton.set_tooltip_text(xstr("flightsel_save_tooltip"))
-        self._saveButton.connect("clicked", self._saveClicked)
-
-        flightButtonWidthBox.pack_start(self._saveButton, True, True, 4)
-
-        self._printButton = gtk.Button(xstr("flightsel_print"))
-        self._printButton.set_use_underline(True)
-        self._printButton.set_sensitive(False)
-        self._printButton.set_tooltip_text(xstr("flightsel_print_tooltip"))
-        self._printButton.connect("clicked", self._printClicked)
-
-        flightButtonWidthBox.pack_start(self._printButton, True, True, 4)
-
-        self._deleteButton = gtk.Button(xstr("flightsel_delete"))
-        self._deleteButton.set_use_underline(True)
-        self._deleteButton.set_sensitive(False)
-        self._deleteButton.set_tooltip_text(xstr("flightsel_delete_tooltip"))
-        self._deleteButton.connect("clicked", self._deleteClicked)
-
-        flightButtonWidthBox.pack_start(self._deleteButton, True, True, 4)
-
-        flightButtonWidthAlignment.add(flightButtonWidthBox)
-        flightButtonBox.pack_start(flightButtonWidthAlignment, False, False, 0)
-
-        mainBox.pack_start(flightButtonBox, True, True, 0)
-
-        self.setMainWidget(mainBox)
-
-        self._pendingButton = self.addButton(xstr("flightsel_pending"),
-                                             sensitive = False,
-                                             clicked = self._pendingClicked,
-                                             tooltip = xstr("flightsel_pending_tooltip"))
-
-        self._saveDialog = None
-
-        self._refreshButton = self.addButton(xstr("flightsel_refresh"),
-                                             sensitive = True,
-                                             clicked = self._refreshClicked,
-                                             tooltip = xstr("flightsel_refresh_tooltip"))
-
-        self._loadButton = self.addButton(xstr("flightsel_load"),
-                                          sensitive = True,
-                                          tooltip = xstr("flightsel_load_tooltip"))
-        self._loadButton.connect("clicked", self._loadButtonClicked)
-        self._loadDialog = None
-
-        self._button = self.addNextButton(sensitive = False,
-                                          clicked =  self._forwardClicked)
-
-        self._flights = []
-
-        self._pendingFlightsWindow = PendingFlightsWindow(self._wizard)
-        self._pendingFlightsWindowShown = False
-        self._pendingFlightsWindow.connect("delete-event",
-                                           self._deletePendingFlightsWindow)
-
-        self._printSettings = None
-
-    def activate(self):
-        """Fill the flight list."""
-        self._setupHelp()
-        self._flightList.set_sensitive(True)
-        self._loadButton.set_sensitive(True)
-        self._refreshButton.set_sensitive(self._wizard.loggedIn)
-        self._buildFlights()
-
-    def finalize(self):
-        """Finalize the page."""
-        self._flightList.set_sensitive(False)
-        self._loadButton.set_sensitive(False)
-        self._refreshButton.set_sensitive(False)
-
-    def _setupHelp(self):
-        """Setup the help string"""
-        help = ""
-
-        if self._wizard.loggedIn:
-            numReported = len(self._wizard.loginResult.reportedFlights)
-            numRejected = len(self._wizard.loginResult.rejectedFlights)
-            if numReported==0 and numRejected==0:
-                help = xstr("flightsel_prehelp_nopending")
-            elif numReported>0 and numRejected==0:
-                help = xstr("flightsel_prehelp_rep_0rej") % (numReported,)
-            elif numReported==0 and numRejected>0:
-                help = xstr("flightsel_prehelp_0rep_rej") % (numRejected,)
-            else:
-                help = xstr("flightsel_prehelp_rep_rej") % \
-                       (numReported, numRejected)
-
-        help += xstr("flightsel_help")
-
-        self.setHelp(help)
-
-    def _buildFlights(self):
-        """Rebuild the flights from the login result."""
-        self._flights = []
-        self._flightList.clear()
-        self._pendingFlightsWindow.clear()
-        loginResult = self._wizard.loginResult
-        if self._wizard.loggedIn:
-            for flight in loginResult.flights:
-                self.addFlight(flight)
-            for flight in loginResult.reportedFlights:
-                self._pendingFlightsWindow.addReportedFlight(flight)
-            for flight in loginResult.rejectedFlights:
-                self._pendingFlightsWindow.addRejectedFlight(flight)
-
-        self._updatePendingButton()
-
-    def addFlight(self, flight):
-        """Add the given file to the list of flights."""
-        self._flights.append(flight)
-        self._flightList.addFlight(flight)
-
-    def _reflyFlight(self, flight):
-        """Refly the given flight."""
-        self.addFlight(flight)
-        self._updatePending()
-
-    def _updatePending(self):
-        """Update the stuff depending on the set of pending flights."""
-        self._setupHelp()
-        self._updatePendingButton()
-
-    def _pendingClicked(self, button):
-        """Called when the Pending flights button is clicked."""
-        self._pendingFlightsWindow.show_all()
-        self._pendingFlightsWindowShown = True
-        self._updateNextButton()
-
-    def _deletePendingFlightsWindow(self, window, event):
-        """Called when the pending flights window is closed."""
-        self._pendingFlightsWindow.hide()
-        self._pendingFlightsWindowShown = False
-        self._updateNextButton()
-        return True
-
-    def _saveClicked(self, button):
-        """Called when the Save flight button is clicked."""
-        self._saveSelected()
-
-    def _saveSelected(self):
-        """Save the selected flight."""
-        flight = self._getSelectedFlight()
-        date = flight.departureTime.date()
-        name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
-               (date.year, date.month, date.day, flight.callsign,
-                flight.departureICAO, flight.arrivalICAO)
-
-        dialog = self._getSaveDialog()
-        dialog.set_current_name(name)
-        dialog.show_all()
-        response = dialog.run()
-        dialog.hide()
-
-        if response==RESPONSETYPE_OK:
-            fileName = text2unicode(dialog.get_filename())
-            print "Saving", fileName
-            try:
-                with open(fileName, "wt") as f:
-                    flight.writeIntoFile(f)
-            except Exception, e:
-                print "Failed to save flight:", util.utf2unicode(str(e))
-                dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
-                                           type = MESSAGETYPE_ERROR,
-                                           message_format =
-                                           xstr("flightsel_save_failed"))
-                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-                dialog.set_title(WINDOW_TITLE_BASE)
-                secondary = xstr("flightsel_save_failed_sec")
-                dialog.format_secondary_markup(secondary)
-                dialog.run()
-                dialog.hide()
-
-    def _printClicked(self, button):
-        """Called when the Print briefing button is clicked."""
-        self._printSelected()
-
-    def _printSelected(self):
-        """Print the briefing for the selected flight."""
-        wizard = self._wizard
-        flight = self._getSelectedFlight()
-
-        printOperation = gtk.PrintOperation()
-        if self._printSettings is not None:
-            printOperation.set_print_settings(self._printSettings)
-
-        printOperation.set_n_pages(1)
-        printOperation.set_show_progress(True)
-        printOperation.connect("draw_page", self._drawBriefing)
-
-        name = "MAVA Briefing %s %s %s" % (wizard.loginResult.pilotID,
-                                           flight.callsign,
-                                           flight.departureTime.strftime("%Y-%m-%d %H:%M"))
-        printOperation.set_job_name(name)
-        printOperation.set_export_filename(name)
-        printOperation.set_use_full_page(False)
-
-        result = printOperation.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG,
-                                    wizard.gui.mainWindow)
-
-        if result == gtk.PRINT_OPERATION_RESULT_APPLY:
-            self._printSettings = printOperation.get_print_settings()
-        elif result == gtk.PRINT_OPERATION_RESULT_ERROR:
-            errorDialog(xstr("flightsel_print_failed",
-                             wizard.gui.mainWindow,
-                             secondary = printOperation.get_error()))
-
-    def _drawBriefing(self, printOperation, context, pageNumber):
-        """Draw the briefing."""
-        wizard = self._wizard
-        loginResult = wizard.loginResult
-        flight=self._getSelectedFlight()
-
-        print "DPI", context.get_dpi_x(), context.get_dpi_y()
-
-        scale = context.get_dpi_x() / 72.0
-
-        cr = context.get_cairo_context()
-        cr.set_antialias(cairo.ANTIALIAS_GRAY)
-
-        cr.set_source_rgb(0.0, 0.0, 0.0)
-        cr.set_line_width(2.0 * scale)
-        cr.rectangle(0, 0, context.get_width(), context.get_height())
-        cr.stroke()
-
-        layout = cr.create_layout()
-        layout.set_text(u"Malév VA official briefing")
-        font = pango.FontDescription("sans")
-        font.set_size(int(32 * scale * pango.SCALE))
-        font.set_weight(pango.WEIGHT_NORMAL)
-        layout.set_font_description(font)
-
-        (_ink, (x0, y0, x1, y1)) = layout.get_extents()
-        width = float(x1 + 1 - x0) / pango.SCALE
-
-        y = 25 * scale
-
-        cr.move_to((context.get_width() - width)/2.0, y)
-        cr.set_line_width(0.1 * scale)
-        cr.layout_path(layout)
-        cr.stroke_preserve()
-        cr.fill()
-
-        y += float(y1 + 1 - y0) / pango.SCALE
-        y += 6 * scale
-
-        layout = cr.create_layout()
-        layout.set_text(u"%s (%s) részére" %
-                        (loginResult.pilotName, loginResult.pilotID))
-        font = pango.FontDescription("sans")
-        font.set_size(int(16 * scale * pango.SCALE))
-        font.set_weight(450)
-        layout.set_font_description(font)
-        (_ink, (x0, y0, x1, y1)) = layout.get_extents()
-        width = float(x1 + 1 - x0) / pango.SCALE
-
-        cr.move_to((context.get_width() - width)/2.0, y)
-        cr.set_line_width(0.1 * scale)
-        cr.layout_path(layout)
-        cr.stroke_preserve()
-        cr.fill()
-
-        y += float(y1 + 1 - y0) / pango.SCALE
-        y += 4 * scale
-
-        cr.move_to(0, y)
-        cr.line_to(context.get_width(), y)
-        cr.set_line_width(1.0 * scale)
-        cr.stroke()
-
-        y += 20 * scale
-
-        font = pango.FontDescription("sans")
-        font.set_size(int(7 * scale * pango.SCALE))
-        font.set_weight(150)
-
-        table = []
-        table.append(("Flight", flight.callsign))
-        table.append(("Date", flight.date))
-        table.append(("Aircraft",
-                      aircraftNames[flight.aircraftType] + ", Lajstrom: " +
-                      flight.tailNumber))
-        table.append(("DOW",
-                      str(acft.getClass(flight.aircraftType).dow) + " kgs"))
-        table.append(("From", flight.departureICAO))
-        table.append(("To", flight.arrivalICAO))
-        table.append(("ETD (UTC)", flight.departureTime.strftime("%H:%M:%S")))
-        table.append(("ETA (UTC)", flight.arrivalTime.strftime("%H:%M:%S")))
-        table.append(("Crew", str(flight.numCrew)))
-        table.append(("Pass", str(flight.numPassengers)))
-        table.append(("Bag", str(flight.bagWeight)))
-        table.append(("Mail", str(flight.mailWeight)))
-        table.append(("Route", flight.route))
-
-        tableY = y
-        tableX = 15 * scale
-        labelFill = 5 * scale
-        labelValueFill = 25 * scale
-        valueFill = 5 * scale
-        labelX = tableX + labelFill
-        tableWidth = context.get_width() * 55 / 100 - tableX
-
-        labelLayouts = []
-        maxLabelWidth = 0
-        totalHeight = 0
-        for (label, value) in table:
-            labelLayout = cr.create_layout()
-            labelLayout.set_text(label)
-            labelLayout.set_font_description(font)
-
-            (_ink, (x0, y0, x1, y1)) = labelLayout.get_extents()
-            maxLabelWidth = max(maxLabelWidth, x1 + 1 - x0)
-            labelHeight = y1 + 1 - y0
-
-            valueLayout = cr.create_layout()
-            valueLayout.set_text(value)
-            valueLayout.set_font_description(font)
-
-            labelLayouts.append((labelLayout, valueLayout, labelHeight))
-
-        maxLabelWidth = maxLabelWidth / pango.SCALE
-
-        valueX = labelX + labelValueFill + maxLabelWidth
-
-        layouts = []
-        valueWidth = tableWidth - \
-            (labelFill + maxLabelWidth + labelValueFill + valueFill)
-        for (labelLayout, valueLayout, labelHeight) in labelLayouts:
-            valueLayout.set_width(int(valueWidth * pango.SCALE))
-
-            (_ink, (x0, y0, x1, y1)) = valueLayout.get_extents()
-            valueHeight = y1 + 1 - y0
-
-            height = float(max(labelHeight, valueHeight))/pango.SCALE
-            layouts.append((labelLayout, valueLayout, height))
-
-        rowIndex = 0
-        for (labelLayout, valueLayout, height) in layouts:
-            if (rowIndex%2)==0:
-                cr.set_source_rgb(0.85, 0.85, 0.85)
-            else:
-                cr.set_source_rgb(0.9, 0.9, 0.9)
-
-            cr.rectangle(tableX, y-2*scale, tableWidth, height + 4 * scale)
-            cr.fill()
-
-            cr.set_source_rgb(0.0, 0.0, 0.0)
-
-            cr.move_to(labelX, y)
-            cr.set_line_width(0.1)
-            cr.layout_path(labelLayout)
-            cr.stroke_preserve()
-            cr.fill()
-
-            cr.move_to(valueX, y)
-            cr.set_line_width(0.1)
-            cr.layout_path(valueLayout)
-            cr.stroke_preserve()
-            cr.fill()
-
-            y += height
-            y += 4 * scale
-
-            rowIndex += 1
-
-        cr.set_source_rgb(0.0, 0.0, 0.0)
-        cr.set_line_width(1.0 * scale)
-        cr.rectangle(tableX, tableY - 2 * scale, tableWidth, y - tableY)
-        cr.stroke()
-
-        cr.move_to(valueX - 5 * scale, tableY - 2 * scale)
-        cr.line_to(valueX - 5 * scale, y - 2 * scale)
-        cr.stroke()
-
-    def _deleteClicked(self, button):
-        """Called when the Delete flight button is clicked."""
-        self._deleteSelected()
-
-    def _deleteSelected(self):
-        """Delete the selected flight."""
-        if askYesNo(xstr("flightsel_delete_confirm"),
-                    parent = self._wizard.gui.mainWindow):
-            flight = self._getSelectedFlight()
-            gui = self._wizard.gui
-            gui.beginBusy(xstr("flightsel_delete_busy"))
-
-            gui.webHandler.deleteFlights(self._deleteResultCallback,
-                                         [flight.id])
-
-    def _deleteResultCallback(self, returned, result):
-        """Called when the deletion result is available."""
-        gobject.idle_add(self._handleDeleteResult, returned, result)
-
-    def _handleDeleteResult(self, returned, result):
-        """Handle the delete result."""
-        gui = self._wizard.gui
-        gui.endBusy()
-
-        if returned:
-            indexes = self._flightList.selectedIndexes
-
-            flights = [self._flights[index] for index in indexes]
-
-            self._flightList.removeFlights(indexes)
-            for index in indexes[::-1]:
-                del self._flights[index]
-        else:
-            communicationErrorDialog()
-
-
-    def _refreshClicked(self, button):
-        """Called when the refresh button is clicked."""
-        self._wizard.reloadFlights(self._refreshCallback)
-
-    def _refreshCallback(self, returned, result):
-        """Callback for the refresh."""
-        if returned:
-            self._setupHelp()
-            if result.loggedIn:
-                self._buildFlights()
-
-    def _selectionChanged(self, flightList, indexes):
-        """Called when the selection is changed."""
-        self._saveButton.set_sensitive(len(indexes)==1)
-        self._printButton.set_sensitive(len(indexes)==1)
-        self._deleteButton.set_sensitive(len(indexes)==1)
-        self._updateNextButton()
-
-    def _updatePendingButton(self):
-        """Update the senstivity of the Pending button."""
-        self._pendingButton.set_sensitive(self._pendingFlightsWindow.hasFlights)
-
-    def _updateNextButton(self):
-        """Update the sensitivity of the Next button."""
-        sensitive = len(self._flightList.selectedIndexes)==1 and \
-          not self._pendingFlightsWindowShown
-        self._button.set_sensitive(sensitive)
-
-    def _loadButtonClicked(self, loadButton):
-        """Called when the load a flight button is clicked."""
-        dialog = self._getLoadDialog()
-        dialog.show_all()
-        response = dialog.run()
-        dialog.hide()
-
-        if response==RESPONSETYPE_OK:
-            fileName = text2unicode(dialog.get_filename())
-            print "Loading", fileName
-            bookedFlight = web.BookedFlight()
-            try:
-                with open(fileName, "rt") as f:
-                    bookedFlight.readFromFile(f)
-                self.addFlight(bookedFlight)
-            except Exception, e:
-                print "Failed to load flight:", util.utf2unicode(str(e))
-                dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
-                                           type = MESSAGETYPE_ERROR,
-                                           message_format =
-                                           xstr("flightsel_load_failed"))
-                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-                dialog.set_title(WINDOW_TITLE_BASE)
-                secondary = xstr("flightsel_load_failed_sec")
-                dialog.format_secondary_markup(secondary)
-                dialog.run()
-                dialog.hide()
-
-    def _forwardClicked(self, button):
-        """Called when the forward button was clicked."""
-        if self._completed:
-            self._wizard.jumpPage(self._nextID, finalize = False)
-        else:
-            self._flightSelected()
-
-    def _rowActivated(self, flightList, index):
-        """Called when a row is activated."""
-        if not self._completed:
-            self._flightSelected()
-
-    def _flightSelected(self):
-        """Called when a flight has been selected."""
-        flight = self._getSelectedFlight()
-        self._wizard._bookedFlight = flight
-        self._wizard.gui.enableFlightInfo(flight.aircraftType)
-
-        self._updateDepartureGate()
-
-    def _getSelectedFlight(self):
-        """Get the currently selected flight."""
-        indexes = self._flightList.selectedIndexes
-        assert(len(indexes)==1)
-        return self._flights[indexes[0]]
-
-    def _updateDepartureGate(self):
-        """Update the departure gate for the booked flight."""
-        flight = self._wizard._bookedFlight
-        if self._wizard.gui.config.onlineGateSystem and \
-           self._wizard.loggedIn and not self._wizard.entranceExam:
-            if flight.departureICAO=="LHBP":
-                self._wizard.getFleet(self._fleetRetrieved)
-            else:
-                self._wizard.updatePlane(self._planeUpdated,
-                                         flight.tailNumber,
-                                         const.PLANE_AWAY)
-        else:
-            self._nextID = "connect"
-            self._wizard.jumpPage("connect")
-
-    def _fleetRetrieved(self, fleet):
-        """Called when the fleet has been retrieved."""
-        if fleet is None:
-            self._nextID = "connect"
-            self._wizard.jumpPage("connect")
-        else:
-            plane = fleet[self._wizard._bookedFlight.tailNumber]
-            if plane is None:
-                self._nextID = "connect"
-                self._wizard.jumpPage("connect")
-            elif plane.gateNumber is not None and \
-                 not fleet.isGateConflicting(plane):
-                self._wizard._departureGate = plane.gateNumber
-                self._nextID = "connect"
-                self._wizard.jumpPage("connect")
-            else:
-                self._nextID = "gatesel"
-                self._wizard.jumpPage("gatesel")
-
-    def _planeUpdated(self, success):
-        """Callback for the plane updating."""
-        self._nextID = "connect"
-        self._wizard.jumpPage("connect")
-
-    def _getSaveDialog(self):
-        """Get the dialog to load a flight file."""
-        if self._saveDialog is not None:
-            return self._saveDialog
-
-        gui = self._wizard.gui
-        dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
-                                       xstr("flightsel_save_title"),
-                                       action = FILE_CHOOSER_ACTION_SAVE,
-                                       buttons = (gtk.STOCK_CANCEL,
-                                                  RESPONSETYPE_CANCEL,
-                                                  gtk.STOCK_OK, RESPONSETYPE_OK),
-                                       parent = gui.mainWindow)
-        dialog.set_modal(True)
-        dialog.set_do_overwrite_confirmation(True)
-
-        filter = gtk.FileFilter()
-        filter.set_name(xstr("flightsel_filter_flights"))
-        filter.add_pattern("*.vaflight")
-        dialog.add_filter(filter)
-
-        filter = gtk.FileFilter()
-        filter.set_name(xstr("file_filter_all"))
-        filter.add_pattern("*.*")
-        dialog.add_filter(filter)
-
-        self._saveDialog = dialog
-
-        return dialog
-
-    def _getLoadDialog(self):
-        """Get the dialog to load a flight file."""
-        if self._loadDialog is not None:
-            return self._loadDialog
-
-        gui = self._wizard.gui
-        dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
-                                       xstr("flightsel_load_title"),
-                                       action = FILE_CHOOSER_ACTION_OPEN,
-                                       buttons = (gtk.STOCK_CANCEL,
-                                                  RESPONSETYPE_CANCEL,
-                                                  gtk.STOCK_OK, RESPONSETYPE_OK),
-                                       parent = gui.mainWindow)
-        dialog.set_modal(True)
-
-        filter = gtk.FileFilter()
-        filter.set_name(xstr("flightsel_filter_flights"))
-        filter.add_pattern("*.vaflight")
-        dialog.add_filter(filter)
-
-        filter = gtk.FileFilter()
-        filter.set_name(xstr("file_filter_all"))
-        filter.add_pattern("*.*")
-        dialog.add_filter(filter)
-
-        self._loadDialog = dialog
-
-        return dialog
-
-    def _createListPopupMenu(self):
-        """Get the flight list popup menu."""
-        menu = gtk.Menu()
-
-        menuItem = gtk.MenuItem()
-        menuItem.set_label(xstr("flightsel_popup_select"))
-        menuItem.set_use_underline(True)
-        menuItem.connect("activate", self._popupSelect)
-        menuItem.show()
-
-        menu.append(menuItem)
-
-        menuItem = gtk.MenuItem()
-        menuItem.set_label(xstr("flightsel_popup_save"))
-        menuItem.set_use_underline(True)
-        menuItem.connect("activate", self._popupSave)
-        menuItem.show()
-
-        menu.append(menuItem)
-
-        menuItem = gtk.MenuItem()
-        menuItem.set_label(xstr("flightsel_popup_print"))
-        menuItem.set_use_underline(True)
-        menuItem.connect("activate", self._popupPrint)
-        menuItem.show()
-
-        menu.append(menuItem)
-
-        menuItem = gtk.MenuItem()
-        menuItem.set_label(xstr("flightsel_popup_delete"))
-        menuItem.set_use_underline(True)
-        menuItem.connect("activate", self._popupDelete)
-        menuItem.show()
-
-        menu.append(menuItem)
-
-        return menu
-
-    def _popupSelect(self, menuItem):
-        """Called when the Select menu item is activated in the popup menu."""
-        if not self._completed:
-            self._flightSelected()
-
-    def _popupSave(self, menuItem):
-        """Called when the Save menu item is activated in the popup menu."""
-        if not self._completed:
-            self._saveSelected()
-
-    def _popupPrint(self, menuItem):
-        """Called when the Print briefing menu item is activated in the popup menu."""
-        if not self._completed:
-            self._printSelected()
-
-    def _popupDelete(self, menuItem):
-        """Called when the Delete menu item is activated in the popup menu."""
-        if not self._completed:
-            self._deleteSelected()
-
-#-----------------------------------------------------------------------------
-
-class GateSelectionPage(Page):
-    """Page to select a free gate at LHBP.
-    This page should be displayed only if we have fleet information!."""
-    def __init__(self, wizard):
-        """Construct the gate selection page."""
-        super(GateSelectionPage, self).__init__(wizard, "gatesel",
-                                                xstr("gatesel_title"),
-                                                xstr("gatesel_help"))
-
-        self._listStore = gtk.ListStore(str)
-        self._gateList = gtk.TreeView(self._listStore)
-        column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
-                                    text = 0)
-        column.set_expand(True)
-        self._gateList.append_column(column)
-        self._gateList.set_headers_visible(False)
-        self._gateList.connect("row-activated", self._rowActivated)
-
-        gateSelection = self._gateList.get_selection()
-        gateSelection.connect("changed", self._selectionChanged)
-
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.add(self._gateList)
-        scrolledWindow.set_size_request(50, -1)
-        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC,
-                                  gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC)
-        scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
-                                       else gtk.SHADOW_IN)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
-        alignment.add(scrolledWindow)
-
-        self.setMainWidget(alignment)
-
-        self.addCancelFlightButton()
-
-        self.addPreviousButton(clicked = self._backClicked)
-
-        self._button = self.addNextButton(sensitive = False,
-                                          clicked = self._forwardClicked)
-
-    def activate(self):
-        """Fill the gate list."""
-        self._listStore.clear()
-        self._gateList.set_sensitive(True)
-        occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
-        for gate in lhbpGates.gates:
-            if gate.isAvailable(lhbpGates, occupiedGateNumbers):
-                self._listStore.append([gate.number])
-
-    def finalize(self):
-        """Finalize the page."""
-        self._gateList.set_sensitive(False)
-
-    def _selectionChanged(self, selection):
-        """Called when the selection is changed."""
-        self._button.set_sensitive(selection.count_selected_rows()==1)
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        """Called when the forward button is clicked."""
-        if not self._completed:
-            self._gateSelected()
-        else:
-            self._wizard.jumpPage("connect")
-
-    def _rowActivated(self, flightList, path, column):
-        """Called when a row is activated."""
-        if not self._completed:
-            self._gateSelected()
-
-    def _gateSelected(self):
-        """Called when a gate has been selected."""
-        selection = self._gateList.get_selection()
-        (listStore, iter) = selection.get_selected()
-        (gateNumber,) = listStore.get(iter, 0)
-
-        self._wizard._departureGate = gateNumber
-
-        self._wizard.updatePlane(self._planeUpdated,
-                                 self._wizard._bookedFlight.tailNumber,
-                                 const.PLANE_HOME, gateNumber)
-
-    def _planeUpdated(self, success):
-        """Callback for the plane updating call."""
-        if success is None or success:
-            self._wizard.jumpPage("connect")
-        else:
-            dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
-                                       type = MESSAGETYPE_ERROR,
-                                       message_format = xstr("gatesel_conflict"))
-            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-            dialog.set_title(WINDOW_TITLE_BASE)
-            dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
-            dialog.run()
-            dialog.hide()
-
-            self._wizard.getFleet(self._fleetRetrieved)
-
-    def _fleetRetrieved(self, fleet):
-        """Called when the fleet has been retrieved."""
-        if fleet is None:
-            self._wizard.jumpPage("connect")
-        else:
-            self.activate()
-
-#-----------------------------------------------------------------------------
-
-class RegisterPage(Page):
-    """A page to enter the registration data."""
-
-    # The minimal year of birth
-    _minYearOfBirth = 1900
-
-    # The maximal year of birth
-    _maxYearOfBirth = datetime.date.today().year
-
-    # The regular expression to check the e-mail address with
-    _emailAddressRE = re.compile("[^@]+@[^@]+\.[^@]+")
-
-    def __init__(self, wizard):
-        """Construct the registration page."""
-        super(RegisterPage, self).__init__(wizard, "register",
-                                           xstr("register_title"),
-                                           xstr("register_help"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(12, 3)
-        table.set_row_spacings(4)
-        table.set_col_spacings(32)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        row = 0
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_name1"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        self._name1 = gtk.Entry()
-        self._name1.connect("changed", self._updateButtons)
-        self._name1.set_tooltip_text(xstr("register_name1_tooltip"))
-        self._name1.set_width_chars(15)
-        table.attach(self._name1, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._name1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_name2"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        self._name2 = gtk.Entry()
-        self._name2.connect("changed", self._updateButtons)
-        self._name2.set_tooltip_text(xstr("register_name2_tooltip"))
-        self._name2.set_width_chars(15)
-        table.attach(self._name2, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._name2)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_year_of_birth"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 0.0, yscale = 0.0)
-
-        self._yearOfBirth = gtk.SpinButton()
-        self._yearOfBirth.set_increments(step = 1, page = 10)
-        self._yearOfBirth.set_range(min = RegisterPage._minYearOfBirth,
-                                    max = RegisterPage._maxYearOfBirth)
-        self._yearOfBirth.set_numeric(True)
-        self._yearOfBirth.set_tooltip_text(xstr("register_year_of_birth_tooltip"))
-        self._yearOfBirth.set_width_chars(5)
-        self._yearOfBirth.connect("changed", self._updateButtons)
-        self._yearOfBirth.connect("value-changed", self._updateButtons)
-        alignment.add(self._yearOfBirth)
-        table.attach(alignment, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._yearOfBirth)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_email"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        self._emailAddress = gtk.Entry()
-        self._emailAddress.connect("changed", self._updateButtons)
-        self._emailAddress.set_tooltip_text(xstr("register_email_tooltip"))
-        table.attach(self._emailAddress, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._emailAddress)
-
-        self._emailAddressPublic = gtk.CheckButton(xstr("register_email_public"))
-        self._emailAddressPublic.set_use_underline(True)
-        self._emailAddressPublic.set_tooltip_text(xstr("register_email_public_tooltip"))
-        table.attach(self._emailAddressPublic, 2, 3, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_vatsim_id"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 0.0, yscale = 0.0)
-        self._vatsimID = IntegerEntry()
-        self._vatsimID.connect("changed", self._updateButtons)
-        self._vatsimID.set_tooltip_text(xstr("register_vatsim_id_tooltip"))
-        self._vatsimID.set_width_chars(7)
-        alignment.add(self._vatsimID)
-        table.attach(alignment, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._vatsimID)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_ivao_id"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 0.0, yscale = 0.0)
-        self._ivaoID = IntegerEntry()
-        self._ivaoID.connect("changed", self._updateButtons)
-        self._ivaoID.set_tooltip_text(xstr("register_ivao_id_tooltip"))
-        self._ivaoID.set_width_chars(7)
-        alignment.add(self._ivaoID)
-        table.attach(alignment, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._ivaoID)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_phone_num"))
-        label.set_use_underline(True)
-        label.set_use_markup(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        self._phoneNumber = gtk.Entry()
-        self._phoneNumber.set_tooltip_text(xstr("register_phone_num_tooltip"))
-        table.attach(self._phoneNumber, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._phoneNumber)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_nationality"))
-        label.set_use_underline(True)
-        label.set_use_markup(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-
-        self._nationality = gtk.Entry()
-        self._nationality.set_tooltip_text(xstr("register_nationality_tooltip"))
-        table.attach(self._nationality, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._nationality)
-
-        placeholder = gtk.Label()
-        placeholder.set_text(xstr("register_password_mismatch"))
-        placeholder.set_use_markup(True)
-        placeholder.set_child_visible(False)
-        placeholder.hide()
-        table.attach(placeholder, 2, 3, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_password"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        self._password = gtk.Entry()
-        self._password.set_visibility(False)
-        self._password.connect("changed", self._updateButtons)
-        self._password.set_tooltip_text(xstr("register_password_tooltip"))
-        table.attach(self._password, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._password)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        self._passwordStatus = gtk.Label()
-        alignment.add(self._passwordStatus)
-        table.attach(alignment, 2, 3, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("register_password2"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        self._password2 = gtk.Entry()
-        self._password2.set_visibility(False)
-        self._password2.connect("changed", self._updateButtons)
-        self._password2.set_tooltip_text(xstr("register_password2_tooltip"))
-        table.attach(self._password2, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._password2)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        self._password2Status = gtk.Label()
-        alignment.add(self._password2Status)
-        table.attach(alignment, 2, 3, row, row+1)
-
-        row += 1
-
-        self._rememberButton = gtk.CheckButton(xstr("remember_password"))
-        self._rememberButton.set_use_underline(True)
-        self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
-        table.attach(self._rememberButton, 1, 2, row, row+1)
-
-        cancelButton = \
-          self.addButton(xstr("button_cancel"))
-        cancelButton.connect("clicked", self._cancelClicked)
-
-        self._registerButton = \
-          self.addButton(xstr("button_register"), default = True,
-                              tooltip = xstr("button_register_tooltip"))
-        self._registerButton.connect("clicked", self._registerClicked)
-
-        self._updateButtons()
-
-    @property
-    def name1(self):
-        """Get the first name component entered."""
-        return self._name1.get_text()
-
-    @property
-    def name2(self):
-        """Get the second name component entered."""
-        return self._name2.get_text()
-
-    @property
-    def yearOfBirth(self):
-        """Get the year of birth."""
-        yearOfBirthText = self._yearOfBirth.get_text()
-        return int(yearOfBirthText) if yearOfBirthText else 0
-
-    @property
-    def emailAddress(self):
-        """Get the e-mail address."""
-        return self._emailAddress.get_text()
-
-    @property
-    def emailAddressPublic(self):
-        """Get the whether the e-mail address is public."""
-        return self._emailAddressPublic.get_active()
-
-    @property
-    def vatsimID(self):
-        """Get the VATSIM ID."""
-        return self._vatsimID.get_int()
-
-    @property
-    def ivaoID(self):
-        """Get the IVAO ID."""
-        return self._ivaoID.get_int()
-
-    @property
-    def phoneNumber(self):
-        """Get the phone number."""
-        return self._phoneNumber.get_text()
-
-    @property
-    def nationality(self):
-        """Get the nationality."""
-        return self._nationality.get_text()
-
-    @property
-    def password(self):
-        """Get the password."""
-        return self._password.get_text()
-
-    @property
-    def rememberPassword(self):
-        """Get whether the password should be remembered."""
-        return self._rememberButton.get_active()
-
-    def activate(self):
-        """Setup the route from the booked flight."""
-        self._yearOfBirth.set_value(0)
-        self._yearOfBirth.set_text("")
-        self._updateButtons()
-
-    def _updateButtons(self, widget = None):
-        """Update the sensitive state of the buttons"""
-        yearOfBirth = self.yearOfBirth
-
-        emailAddress = self.emailAddress
-        emailAddressMatch = RegisterPage._emailAddressRE.match(emailAddress)
-
-        vatsimID = self.vatsimID
-        ivaoID = self.ivaoID
-
-        password = self.password
-        password2 = self._password2.get_text()
-        if not password:
-            self._passwordStatus.set_text("")
-        elif len(password)<5:
-            self._passwordStatus.set_text(xstr("register_password_too_short"))
-        else:
-            self._passwordStatus.set_text(xstr("register_password_ok"))
-        self._passwordStatus.set_use_markup(True)
-
-        if len(password)<5 or not password2:
-            self._password2Status.set_text("")
-        elif password!=password2:
-            self._password2Status.set_text(xstr("register_password_mismatch"))
-        else:
-            self._password2Status.set_text(xstr("register_password_ok"))
-        self._password2Status.set_use_markup(True)
-
-        sensitive = \
-            len(self.name1)>0 and len(self.name2)>0 and \
-            yearOfBirth>=RegisterPage._minYearOfBirth and \
-            yearOfBirth<=RegisterPage._maxYearOfBirth and \
-            emailAddressMatch is not None and \
-            (vatsimID>=800000 or ivaoID>=100000) and \
-            len(password)>=5 and password==password2
-
-        self._registerButton.set_sensitive(sensitive)
-
-    def _cancelClicked(self, button):
-        """Called when the Cancel button is clicked."""
-        self.goBack()
-
-    def _registerClicked(self, button):
-        """Called when the Register button is clicked."""
-        nameOrder = xstr("register_nameorder")
-
-        if nameOrder=="eastern":
-            surName = self.name1
-            firstName = self.name2
-        else:
-            surName = self.name2
-            firstName = self.name1
-
-        nationality = self.nationality.lower()
-
-        if getLanguage().lower()=="hu" or nationality.find("hung")!=-1 or \
-           nationality.find("magyar")!=-1:
-            requestedNameOrder = "eastern"
-        else:
-            requestedNameOrder = "western"
-
-        registrationData = web.Registration(surName, firstName,
-                                            requestedNameOrder,
-                                            self.yearOfBirth,
-                                            self.emailAddress,
-                                            self.emailAddressPublic,
-                                            self.vatsimID, self.ivaoID,
-                                            self.phoneNumber, self.nationality,
-                                            self.password)
-        print "Registering with data:"
-        print "  name:", self.name1, self.name2, registrationData.firstName, registrationData.surName, requestedNameOrder
-        print "  yearOfBirth:", self.yearOfBirth, registrationData.yearOfBirth
-        print "  emailAddress:", self.emailAddress, registrationData.emailAddress
-        print "  emailAddressPublic:", self.emailAddressPublic, registrationData.emailAddressPublic
-        print "  vatsimID:", self.vatsimID, registrationData.vatsimID
-        print "  ivaoID:", self.ivaoID, registrationData.ivaoID
-        print "  phoneNumber:", self.phoneNumber, registrationData.phoneNumber
-        print "  nationality:", self.nationality, registrationData.nationality
-
-        gui = self._wizard.gui
-        gui.beginBusy(xstr("register_busy"))
-        gui.webHandler.register(self._registerResultCallback, registrationData)
-
-    def _registerResultCallback(self, returned, result):
-        """Called when the registration result is available."""
-        gobject.idle_add(self._handleRegisterResult, returned, result)
-
-    def _handleRegisterResult(self, returned, result):
-        """Handle the registration result."""
-        gui = self._wizard.gui
-
-        gui.endBusy()
-
-        print "Registration result:"
-        print "  returned:", returned
-        if returned:
-            print "  registered:", result.registered
-            if result.registered:
-                print "  pilotID", result.pilotID
-                print "  loggedIn", result.loggedIn
-            print "  emailAlreadyRegistered:", result.emailAlreadyRegistered
-            print "  invalidData:", result.invalidData
-
-        registrationOK = returned and result.registered
-
-        message = xstr("register_ok") if registrationOK \
-                  else xstr("register_failed")
-        secondaryMessage = None
-        if registrationOK:
-            if result.loggedIn:
-                secondaryMessage = xstr("register_info") % (result.pilotID,)
-            else:
-                secondaryMessage = xstr("register_nologin") % (result.pilotID,)
-            messageType = MESSAGETYPE_INFO
-
-            config = gui.config
-            config.pilotID = result.pilotID
-            config.rememberPassword = self.rememberPassword
-            if config.rememberPassword:
-                config.password = self.password
-            else:
-                config.password = ""
-
-            config.save()
-        elif returned and result.emailAlreadyRegistered:
-            secondaryMessage = xstr("register_email_already")
-            messageType = MESSAGETYPE_ERROR
-        elif returned and result.invalidData:
-            secondaryMessage = xstr("register_invalid_data")
-            messageType = MESSAGETYPE_ERROR
-        else:
-            secondaryMessage = xstr("register_error")
-            messageType = MESSAGETYPE_ERROR
-
-        dialog = gtk.MessageDialog(parent = gui.mainWindow,
-                                   type = messageType,
-                                   message_format = message)
-        dialog.set_title(WINDOW_TITLE_BASE + " - " +
-                         xstr("register_result_title"))
-        dialog.format_secondary_markup(secondaryMessage)
-
-        dialog.add_button(xstr("button_ok"), 0)
-
-        dialog.run()
-        dialog.hide()
-
-        if registrationOK:
-            if result.loggedIn:
-                self._wizard._loginResult = result
-                self._wizard.nextPage()
-            else:
-                self._wizard.jumpPage("login")
-
-#-----------------------------------------------------------------------------
-
-class StudentPage(Page):
-    """A page displayed to students after logging in."""
-    _entryExamStatusQueryInterval = 60*1000
-
-    def __init__(self, wizard):
-        """Construct the student page."""
-        super(StudentPage, self).__init__(wizard, "student",
-                                          xstr("student_title"),
-                                          xstr("student_help"))
-
-
-        self._getEntryExamStatusCancelled = False
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.5, yscale = 0.0)
-
-        table = gtk.Table(6, 4)
-        table.set_row_spacings(4)
-        table.set_col_spacings(0)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        row = 0
-
-        labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
-                                       xscale=0.0, yscale = 0.0)
-        label = gtk.Label(xstr("student_entry_exam_status"))
-        label.set_alignment(0.0, 0.5)
-        labelAlignment.add(label)
-        labelAlignment.resize_children()
-        table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
-
-        alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
-                                  xscale=1.0, yscale = 0.0)
-        self._entryExamStatus = gtk.Label()
-        self._entryExamStatus.set_use_markup(True)
-        self._entryExamStatus.set_alignment(0.0, 0.5)
-        alignment.add(self._entryExamStatus)
-        alignment.resize_children()
-        table.attach(alignment, 1, 4, row, row + 1)
-
-        row += 1
-
-        buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
-        button = self._entryExamButton = gtk.Button(xstr("student_entry_exam"))
-        button.set_use_underline(True)
-        button.connect("clicked", self._entryExamClicked)
-        button.set_tooltip_text(xstr("student_entry_exam_tooltip"))
-
-        buttonAlignment.add(button)
-        table.attach(buttonAlignment, 0, 4, row, row + 1, xoptions = FILL,
-                     ypadding = 4)
-
-        row += 3
-
-        labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
-                                       xscale=0.0, yscale = 0.0)
-        label = gtk.Label(xstr("student_check_flight_status"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
-
-        alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
-                                  xscale=1.0, yscale = 0.0)
-        self._checkFlightStatus = gtk.Label()
-        self._checkFlightStatus.set_use_markup(True)
-        self._checkFlightStatus.set_alignment(0.0, 0.5)
-        alignment.add(self._checkFlightStatus)
-        table.attach(alignment, 1, 4, row, row + 1)
-
-        row += 1
-
-        alignment = gtk.Alignment(xalign=0.0, xscale=1.0)
-
-        hbox = gtk.HBox()
-        hbox.set_homogeneous(False)
-        hbox.set_spacing(0)
-
-        aircraftTypesModel = gtk.ListStore(str, int)
-        for aircraftType in web.BookedFlight.checkFlightTypes:
-            aircraftTypesModel.append([aircraftNames[aircraftType],
-                                       aircraftType])
-
-        aircraftTypeAlignment = gtk.Alignment(xalign = 0.0, xscale = 1.0)
-
-        self._aircraftType = gtk.ComboBox(model = aircraftTypesModel)
-        renderer = gtk.CellRendererText()
-        self._aircraftType.pack_start(renderer, True)
-        self._aircraftType.add_attribute(renderer, "text", 0)
-        self._aircraftType.set_tooltip_text(xstr("student_check_flight_type_tooltip"))
-        self._aircraftType.set_active(0)
-
-        aircraftTypeAlignment.add(self._aircraftType)
-
-        hbox.pack_start(aircraftTypeAlignment, False, False, 0)
-
-        buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
-        button = self._checkFlightButton = gtk.Button(xstr("student_check_flight"))
-        button.set_use_underline(True)
-        button.connect("clicked", self._checkFlightClicked)
-        button.set_tooltip_text(xstr("student_check_flight_tooltip"))
-
-        hbox.pack_start(button, True, True, 0)
-
-        alignment.add(hbox)
-        table.attach(alignment, 0, 4, row, row + 1, xoptions = FILL)
-
-    @property
-    def aircraftType(self):
-        """Get the type of the aircraft used to perform the check flight."""
-        index = self._aircraftType.get_active()
-        return self._aircraftType.get_model()[index][1]
-
-    def activate(self):
-        """Activate the student page."""
-        print "StudentPage.activate"
-        self._getEntryExamStatusCancelled = False
-
-        loginResult = self._wizard.loginResult
-        self._entryExamLink = loginResult.entryExamLink
-
-        self._updateEntryExamStatus(loginResult.entryExamPassed)
-        self._getEntryExamStatus()
-
-        # FIXME: call with real value
-        self._updateCheckFlightStatus(self._wizard.loginResult.checkFlightStatus)
-
-    def finalize(self):
-        """Finalize the page."""
-        print "StudentPage.finalize"
-        self._getEntryExamStatusCancelled = True
-
-    def _entryExamClicked(self, button):
-        """Called when the entry exam button is clicked."""
-        webbrowser.open(self._entryExamLink)
-
-    def _getEntryExamStatus(self):
-        """Initiate the query of the entry exam status after the interval."""
-        if not self._getEntryExamStatusCancelled:
-            gobject.timeout_add(StudentPage._entryExamStatusQueryInterval,
-                                lambda: self._wizard.gui.webHandler. \
-                                getEntryExamStatus(self._entryExamStatusCallback))
-
-    def _entryExamStatusCallback(self, returned, result):
-        """Called when the entry exam status is available."""
-        gobject.idle_add(self._handleEntryExamStatus, returned, result)
-
-    def _handleEntryExamStatus(self, returned, result):
-        """Called when the entry exam status is availabe."""
-        print "_handleEntryExamStatus", returned, result
-        if returned and not self._getEntryExamStatusCancelled:
-            self._entryExamLink = result.entryExamLink
-            self._updateEntryExamStatus(result.entryExamPassed)
-            if result.madeFO:
-                self._madeFO()
-            else:
-                self._getEntryExamStatus()
-
-    def _updateEntryExamStatus(self, passed):
-        """Update the entry exam status display and button."""
-        self._entryExamStatus.set_text(xstr("student_entry_exam_passed")
-                                       if passed else
-                                       xstr("student_entry_exam_not_passed"))
-        self._entryExamStatus.set_use_markup(True)
-        self._entryExamButton.set_sensitive(not passed)
-        self._wizard._loginResult.entryExamPassed = passed
-
-    def _checkFlightClicked(self, button):
-        """Called when the check flight button is clicked."""
-        aircraftType = self.aircraftType
-        self._wizard._bookedFlight = \
-            web.BookedFlight.forCheckFlight(aircraftType)
-        self._wizard.gui.enableFlightInfo(aircraftType)
-        self._wizard.jumpPage("connect")
-
-    def _updateCheckFlightStatus(self, passed):
-        """Update the status of the check flight."""
-        self._aircraftType.set_sensitive(not passed)
-        self._checkFlightStatus.set_text(xstr("student_check_flight_passed")
-                                         if passed else
-                                         xstr("student_check_flight_not_passed"))
-        self._checkFlightStatus.set_use_markup(True)
-        self._checkFlightButton.set_sensitive(not passed)
-
-    def _madeFO(self):
-        """Handle the event when the pilot has become a first officer."""
-        wizard = self._wizard
-        loginResult = wizard.loginResult
-        loginResult.rank = "FO"
-
-        gui = wizard.gui
-
-        dialog = gtk.MessageDialog(parent = gui.mainWindow,
-                                   type = MESSAGETYPE_INFO,
-                                   message_format = xstr("student_fo"))
-
-        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-        dialog.set_title(WINDOW_TITLE_BASE)
-        secondary = xstr("student_fo_secondary")
-        dialog.format_secondary_markup(secondary)
-        dialog.run()
-        dialog.hide()
-
-        gui.reset()
-
-#-----------------------------------------------------------------------------
-
-class ConnectPage(Page):
-    """Page which displays the departure airport and gate (if at LHBP)."""
-    def __init__(self, wizard):
-        """Construct the connect page."""
-        help = "Load the aircraft below into the simulator and park it\n" \
-               "at the given airport, at the gate below, if present.\n\n" \
-               "Then press the Connect button to connect to the simulator."
-        completedHelp = "The basic data of your flight can be read below."
-        super(ConnectPage, self).__init__(wizard, "connect",
-                                          xstr("connect_title"),
-                                          xstr("connect_help"),
-                                          completedHelp = xstr("connect_chelp"))
-
-        self._selectSimulator = os.name=="nt" or "FORCE_SELECT_SIM" in os.environ
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(7 if self._selectSimulator else 5, 2)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(True)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("connect_flightno"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 0, 1)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._flightNumber = gtk.Label()
-        self._flightNumber.set_width_chars(9)
-        self._flightNumber.set_alignment(0.0, 0.5)
-        labelAlignment.add(self._flightNumber)
-        table.attach(labelAlignment, 1, 2, 0, 1)
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("connect_acft"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 1, 2)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._aircraft = gtk.Label()
-        self._aircraft.set_width_chars(25)
-        self._aircraft.set_alignment(0.0, 0.5)
-        labelAlignment.add(self._aircraft)
-        table.attach(labelAlignment, 1, 2, 1, 2)
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("connect_tailno"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 2, 3)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._tailNumber = gtk.Label()
-        self._tailNumber.set_width_chars(10)
-        self._tailNumber.set_alignment(0.0, 0.5)
-        labelAlignment.add(self._tailNumber)
-        table.attach(labelAlignment, 1, 2, 2, 3)
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("connect_airport"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 3, 4)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._departureICAO = gtk.Label()
-        self._departureICAO.set_width_chars(6)
-        self._departureICAO.set_alignment(0.0, 0.5)
-        labelAlignment.add(self._departureICAO)
-        table.attach(labelAlignment, 1, 2, 3, 4)
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("connect_gate"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 4, 5)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._departureGate = gtk.Label()
-        self._departureGate.set_width_chars(5)
-        self._departureGate.set_alignment(0.0, 0.5)
-        labelAlignment.add(self._departureGate)
-        table.attach(labelAlignment, 1, 2, 4, 5)
-
-        if self._selectSimulator:
-            labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0, yalign=0.5)
-            label = gtk.Label(xstr("connect_sim"))
-            labelAlignment.add(label)
-            table.attach(labelAlignment, 0, 1, 5, 7)
-
-            selectAlignment = gtk.Alignment(xalign=0.0, xscale=0.0, yalign=0.5)
-
-            selectBox = gtk.HBox()
-            if pygobject:
-                self._selectMSFS = \
-                  gtk.RadioButton.new_with_mnemonic_from_widget(None,
-                                                                xstr("connect_sim_msfs"))
-            else:
-                self._selectMSFS = gtk.RadioButton(None,
-                                                   xstr("connect_sim_msfs"))
-
-            selectBox.pack_start(self._selectMSFS, False, False, 0);
-
-            if pygobject:
-                self._selectXPlane = \
-                  gtk.RadioButton.new_with_mnemonic_from_widget(self._selectMSFS,
-                                                                xstr("connect_sim_xplane"))
-            else:
-                self._selectXPlane = gtk.RadioButton(self._selectMSFS,
-                                                     xstr("connect_sim_xplane"))
-
-            selectBox.pack_start(self._selectXPlane, False, False, 8);
-
-            selectAlignment.add(selectBox)
-            table.attach(selectAlignment, 1, 2, 5, 7)
-
-
-        self.addCancelFlightButton()
-
-        self._previousButton = \
-          self.addPreviousButton(clicked = self._backClicked)
-
-        self._button = self.addButton(xstr("button_connect"), default = True,
-                                      tooltip = xstr("button_connect_tooltip"))
-        self._clickedID = self._button.connect("clicked", self._connectClicked)
-
-    def activate(self):
-        """Setup the departure information."""
-        self._button.set_label(xstr("button_connect"))
-        self._button.set_use_underline(True)
-        self._button.set_tooltip_text(xstr("button_connect_tooltip"))
-        self._button.disconnect(self._clickedID)
-        self._clickedID = self._button.connect("clicked", self._connectClicked)
-
-        bookedFlight = self._wizard._bookedFlight
-
-        self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
-
-        aircraftType = aircraftNames[bookedFlight.aircraftType]
-        self._aircraft.set_markup("<b>" + aircraftType + "</b>")
-
-        self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
-
-        icao = bookedFlight.departureICAO
-        self._departureICAO.set_markup("<b>" + icao + "</b>")
-        gate = self._wizard._departureGate
-        if gate!="-":
-            gate = "<b>" + gate + "</b>"
-        self._departureGate.set_markup(gate)
-
-        if self._selectSimulator:
-            config = self._wizard.gui.config
-            self._selectMSFS.set_active(config.defaultMSFS)
-            self._selectXPlane.set_active(not config.defaultMSFS)
-
-        self._previousButton.set_sensitive(not self._wizard.entranceExam)
-
-    def finalize(self):
-        """Finalize the page."""
-        self._button.set_label(xstr("button_next"))
-        self._button.set_use_underline(True)
-        self._button.set_tooltip_text(xstr("button_next_tooltip"))
-        self._button.disconnect(self._clickedID)
-        self._clickedID = self._button.connect("clicked", self._forwardClicked)
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _connectClicked(self, button):
-        """Called when the Connect button is pressed."""
-        if self._selectSimulator:
-            simulatorType = const.SIM_MSFS9 if self._selectMSFS.get_active() \
-                                            else const.SIM_XPLANE10
-        else:
-            simulatorType = const.SIM_MSFS9 if os.name=="nt" \
-              else const.SIM_XPLANE10
-
-        config = self._wizard.gui.config
-        config.defaultMSFS = simulatorType == const.SIM_MSFS9
-        config.save()
-
-        self._wizard._connectSimulator(simulatorType)
-
-    def _forwardClicked(self, button):
-        """Called when the Forward button is pressed."""
-        self._wizard.nextPage()
-
-#-----------------------------------------------------------------------------
-
-class PayloadPage(Page):
-    """Page to allow setting up the payload."""
-    def __init__(self, wizard):
-        """Construct the page."""
-        super(PayloadPage, self).__init__(wizard, "payload",
-                                          xstr("payload_title"),
-                                          xstr("payload_help"),
-                                          completedHelp = xstr("payload_chelp"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(7, 3)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        label = gtk.Label(xstr("payload_crew"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 0, 1)
-
-        self._numCrew = IntegerEntry(defaultValue = 0)
-        self._numCrew.set_width_chars(6)
-        self._numCrew.connect("integer-changed", self._weightChanged)
-        self._numCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
-        table.attach(self._numCrew, 1, 2, 0, 1)
-        label.set_mnemonic_widget(self._numCrew)
-
-        label = gtk.Label(xstr("payload_pax"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 1, 2)
-
-        self._numPassengers = IntegerEntry(defaultValue = 0)
-        self._numPassengers.set_width_chars(6)
-        self._numPassengers.connect("integer-changed", self._weightChanged)
-        self._numPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
-        table.attach(self._numPassengers, 1, 2, 1, 2)
-        label.set_mnemonic_widget(self._numPassengers)
-
-        label = gtk.Label(xstr("payload_bag"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 2, 3)
-
-        self._bagWeight = IntegerEntry(defaultValue = 0)
-        self._bagWeight.set_width_chars(6)
-        self._bagWeight.connect("integer-changed", self._weightChanged)
-        self._bagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
-        table.attach(self._bagWeight, 1, 2, 2, 3)
-        label.set_mnemonic_widget(self._bagWeight)
-
-        table.attach(gtk.Label("kg"), 2, 3, 2, 3)
-
-        label = gtk.Label(xstr("payload_cargo"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 3, 4)
-
-        self._cargoWeight = IntegerEntry(defaultValue = 0)
-        self._cargoWeight.set_width_chars(6)
-        self._cargoWeight.connect("integer-changed", self._weightChanged)
-        self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
-        table.attach(self._cargoWeight, 1, 2, 3, 4)
-        label.set_mnemonic_widget(self._cargoWeight)
-
-        table.attach(gtk.Label("kg"), 2, 3, 3, 4)
-
-        label = gtk.Label(xstr("payload_mail"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 4, 5)
-
-        self._mailWeight = IntegerEntry(defaultValue = 0)
-        self._mailWeight.set_width_chars(6)
-        self._mailWeight.connect("integer-changed", self._weightChanged)
-        self._mailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
-        table.attach(self._mailWeight, 1, 2, 4, 5)
-        label.set_mnemonic_widget(self._mailWeight)
-
-        table.attach(gtk.Label("kg"), 2, 3, 4, 5)
-
-        label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
-        label.set_alignment(0.0, 0.5)
-        label.set_use_markup(True)
-        table.attach(label, 0, 1, 5, 6)
-
-        self._calculatedZFW = gtk.Label()
-        self._calculatedZFW.set_width_chars(6)
-        self._calculatedZFW.set_alignment(1.0, 0.5)
-        table.attach(self._calculatedZFW, 1, 2, 5, 6)
-
-        table.attach(gtk.Label("kg"), 2, 3, 5, 6)
-
-        self._zfwButton = gtk.Button(xstr("payload_fszfw"))
-        self._zfwButton.set_use_underline(True)
-        self._zfwButton.connect("clicked", self._zfwRequested)
-        self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
-        table.attach(self._zfwButton, 0, 1, 6, 7)
-
-        self._simulatorZFW = gtk.Label("-")
-        self._simulatorZFW.set_width_chars(6)
-        self._simulatorZFW.set_alignment(1.0, 0.5)
-        table.attach(self._simulatorZFW, 1, 2, 6, 7)
-        self._simulatorZFWValue = None
-
-        table.attach(gtk.Label("kg"), 2, 3, 6, 7)
-
-        self.addCancelFlightButton()
-        self._backButton = self.addPreviousButton(clicked = self._backClicked)
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-    @property
-    def numCrew(self):
-        """The number of the crew members on the flight."""
-        return self._numCrew.get_int()
-
-    @property
-    def numPassengers(self):
-        """The number of the passengers on the flight."""
-        return self._numPassengers.get_int()
-
-    @property
-    def bagWeight(self):
-        """Get the bag weight entered."""
-        return self._bagWeight.get_int()
-
-    @property
-    def cargoWeight(self):
-        """Get the cargo weight entered."""
-        return self._cargoWeight.get_int()
-
-    @property
-    def mailWeight(self):
-        """Get the bag weight entered."""
-        return self._mailWeight.get_int()
-
-    def activate(self):
-        """Setup the information."""
-        bookedFlight = self._wizard._bookedFlight
-
-        self._numCrew.set_int(bookedFlight.numCrew)
-        self._numCrew.set_sensitive(True)
-        self._numPassengers.set_int(bookedFlight.numPassengers)
-        self._numPassengers.set_sensitive(True)
-
-        self._bagWeight.set_int(bookedFlight.bagWeight)
-        self._bagWeight.set_sensitive(True)
-        self._cargoWeight.set_int(bookedFlight.cargoWeight)
-        self._cargoWeight.set_sensitive(True)
-        self._mailWeight.set_int(bookedFlight.mailWeight)
-        self._mailWeight.set_sensitive(True)
-
-        self._simulatorZFW.set_text("-")
-        self._simulatorZFWValue = None
-        self._zfwButton.set_sensitive(True)
-        self._updateCalculatedZFW()
-
-    def finalize(self):
-        """Finalize the payload page."""
-        self._numCrew.set_sensitive(False)
-        self._numPassengers.set_sensitive(False)
-        self._bagWeight.set_sensitive(False)
-        self._cargoWeight.set_sensitive(False)
-        self._mailWeight.set_sensitive(False)
-        self._wizard.gui.initializeWeightHelp()
-
-    def calculateZFW(self):
-        """Calculate the ZFW value."""
-        zfw = self._wizard.gui._flight.aircraft.dow
-        zfw += (self._numCrew.get_int() + self._numPassengers.get_int()) * 82
-        zfw += self._bagWeight.get_int()
-        zfw += self._cargoWeight.get_int()
-        zfw += self._mailWeight.get_int()
-        return zfw
-
-    def _updateCalculatedZFW(self):
-        """Update the calculated ZFW"""
-        zfw = self.calculateZFW()
-
-        markupBegin = "<b>"
-        markupEnd = "</b>"
-        if self._simulatorZFWValue is not None and \
-           PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
-            markupBegin += '<span foreground="red">'
-            markupEnd = "</span>" + markupEnd
-        self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
-
-    def _weightChanged(self, entry, weight):
-        """Called when one of the weight values or humanm counts has changed."""
-        self._updateCalculatedZFW()
-
-    def _zfwRequested(self, button):
-        """Called when the ZFW is requested from the simulator."""
-        self._zfwButton.set_sensitive(False)
-        self._backButton.set_sensitive(False)
-        self._button.set_sensitive(False)
-        gui = self._wizard.gui
-        gui.beginBusy(xstr("payload_zfw_busy"))
-        gui.simulator.requestZFW(self._handleZFW)
-
-    def _handleZFW(self, zfw):
-        """Called when the ZFW value is retrieved."""
-        gobject.idle_add(self._processZFW, zfw)
-
-    def _processZFW(self, zfw):
-        """Process the given ZFW value received from the simulator."""
-        self._wizard.gui.endBusy()
-        self._zfwButton.set_sensitive(True)
-        self._backButton.set_sensitive(True)
-        self._button.set_sensitive(True)
-        self._simulatorZFWValue = zfw
-        self._simulatorZFW.set_text("%.0f" % (zfw,))
-        self._updateCalculatedZFW()
-
-    def _forwardClicked(self, button):
-        """Called when the forward button is clicked."""
-        self._wizard.nextPage()
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-#-----------------------------------------------------------------------------
-
-class TimePage(Page):
-    """Page displaying the departure and arrival times and allows querying the
-    current time from the flight simulator."""
-    def __init__(self, wizard):
-        super(TimePage, self).__init__(wizard, "time",
-                                       xstr("time_title"),
-                                       xstr("time_help"),
-                                       completedHelp = xstr("time_chelp"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(3, 2)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        label = gtk.Label(xstr("time_departure"))
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 0, 1)
-
-        self._departure = gtk.Label()
-        self._departure.set_alignment(0.0, 0.5)
-        table.attach(self._departure, 1, 2, 0, 1)
-
-        label = gtk.Label(xstr("time_arrival"))
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 1, 2)
-
-        self._arrival = gtk.Label()
-        self._arrival.set_alignment(0.0, 0.5)
-        table.attach(self._arrival, 1, 2, 1, 2)
-
-        self._timeButton = gtk.Button(xstr("time_fs"))
-        self._timeButton.set_use_underline(True)
-        self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
-        self._timeButton.connect("clicked", self._timeRequested)
-        table.attach(self._timeButton, 0, 1, 2, 3)
-
-        self._simulatorTime = gtk.Label("-")
-        self._simulatorTime.set_alignment(0.0, 0.5)
-        table.attach(self._simulatorTime, 1, 2, 2, 3)
-
-        self.addCancelFlightButton()
-
-        self._backButton = self.addPreviousButton(clicked = self._backClicked)
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-    def activate(self):
-        """Activate the page."""
-        self._timeButton.set_sensitive(True)
-        bookedFlight = self._wizard._bookedFlight
-        self._departure.set_text(str(bookedFlight.departureTime.time()))
-        self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
-        self._simulatorTime.set_text("-")
-
-    def _timeRequested(self, button):
-        """Request the time from the simulator."""
-        self._timeButton.set_sensitive(False)
-        self._backButton.set_sensitive(False)
-        self._button.set_sensitive(False)
-        self._wizard.gui.beginBusy(xstr("time_busy"))
-        self._wizard.gui.simulator.requestTime(self._handleTime)
-
-    def _handleTime(self, timestamp):
-        """Handle the result of a time retrieval."""
-        gobject.idle_add(self._processTime, timestamp)
-
-    def _processTime(self, timestamp):
-        """Process the given time."""
-        self._wizard.gui.endBusy()
-        self._timeButton.set_sensitive(True)
-        self._backButton.set_sensitive(True)
-        self._button.set_sensitive(True)
-        tm = time.gmtime(timestamp)
-        t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
-        self._simulatorTime.set_text(str(t))
-
-        ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
-        dt = self._wizard._bookedFlight.departureTime.time()
-        dts = dt.hour * 3600 + dt.minute * 60 + dt.second
-        diff = dts-ts
-
-        markupBegin = ""
-        markupEnd = ""
-        if diff < 0:
-            markupBegin = '<b><span foreground="red">'
-            markupEnd = '</span></b>'
-        elif diff < 3*60 or diff > 30*60:
-            markupBegin = '<b><span foreground="orange">'
-            markupEnd = '</span></b>'
-
-        self._departure.set_markup(markupBegin + str(dt) + markupEnd)
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        """Called when the forward button is clicked."""
-        if not self._completed:
-            gui = self._wizard.gui
-            gui.beginBusy(xstr("fuel_get_busy"))
-
-            gui.simulator.getFuel(self._handleFuel)
-        else:
-            self._wizard.nextPage()
-
-    def _handleFuel(self, fuelData):
-        """Callback for the fuel query operation."""
-        gobject.idle_add(self._processFuel, fuelData)
-
-    def _processFuel(self, fuelData):
-        """Process the given fuel data."""
-        self._wizard.gui.endBusy()
-        self._wizard._fuelData = fuelData
-        self._wizard.nextPage()
-
-#-----------------------------------------------------------------------------
-
-class RoutePage(Page):
-    """The page containing the route and the flight level."""
-    def __init__(self, wizard):
-        """Construct the page."""
-        super(RoutePage, self).__init__(wizard, "route",
-                                        xstr("route_title"),
-                                        xstr("route_help"),
-                                        completedHelp = xstr("route_chelp"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        mainBox = gtk.VBox()
-        alignment.add(mainBox)
-        self.setMainWidget(alignment)
-
-        levelBox = gtk.HBox()
-
-        label = gtk.Label(xstr("route_level"))
-        label.set_use_underline(True)
-        levelBox.pack_start(label, True, True, 0)
-
-        self._cruiseLevel = gtk.SpinButton()
-        self._cruiseLevel.set_increments(step = 10, page = 100)
-        self._cruiseLevel.set_range(min = 0, max = 500)
-        self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
-        self._cruiseLevel.set_numeric(True)
-        self._cruiseLevel.connect("changed", self._cruiseLevelChanged)
-        self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
-        label.set_mnemonic_widget(self._cruiseLevel)
-
-        levelBox.pack_start(self._cruiseLevel, False, False, 8)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(levelBox)
-
-        mainBox.pack_start(alignment, False, False, 0)
-
-
-        routeBox = gtk.VBox()
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        label = gtk.Label(xstr("route_route"))
-        label.set_use_underline(True)
-        alignment.add(label)
-        routeBox.pack_start(alignment, True, True, 0)
-
-        routeWindow = gtk.ScrolledWindow()
-        routeWindow.set_size_request(400, 80)
-        routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
-                                    else gtk.SHADOW_IN)
-        routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
-                               else gtk.POLICY_AUTOMATIC,
-                               gtk.PolicyType.AUTOMATIC if pygobject
-                               else gtk.POLICY_AUTOMATIC)
-
-        self._uppercasingRoute = False
-
-        self._route = gtk.TextView()
-        self._route.set_tooltip_text(xstr("route_route_tooltip"))
-        self._route.set_wrap_mode(WRAP_WORD)
-        self._route.get_buffer().connect("changed", self._routeChanged)
-        self._route.get_buffer().connect_after("insert-text", self._routeInserted)
-        routeWindow.add(self._route)
-
-        label.set_mnemonic_widget(self._route)
-        routeBox.pack_start(routeWindow, True, True, 0)
-
-        mainBox.pack_start(routeBox, True, True, 8)
-
-        alternateBox = gtk.HBox()
-
-        label = gtk.Label(xstr("route_altn"))
-        label.set_use_underline(True)
-        alternateBox.pack_start(label, True, True, 0)
-
-        self._alternate = gtk.Entry()
-        self._alternate.set_width_chars(6)
-        self._alternate.connect("changed", self._alternateChanged)
-        self._alternate.set_tooltip_text(xstr("route_altn_tooltip"))
-        label.set_mnemonic_widget(self._alternate)
-
-        alternateBox.pack_start(self._alternate, False, False, 8)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(alternateBox)
-
-        mainBox.pack_start(alignment, False, False, 0)
-
-        self.addCancelFlightButton()
-
-        self._backButton = self.addPreviousButton(clicked = self._backClicked)
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-    @property
-    def filedCruiseLevel(self):
-        """Get the filed cruise level."""
-        return self._cruiseLevel.get_value_as_int()
-
-    @property
-    def route(self):
-        """Get the route."""
-        return self._getRoute()
-
-    @property
-    def alternate(self):
-        """Get the ICAO code of the alternate airport."""
-        return self._alternate.get_text()
-
-    def activate(self):
-        """Setup the route from the booked flight."""
-        self._cruiseLevel.set_value(0)
-        self._cruiseLevel.set_text("")
-        self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
-        self._alternate.set_text("")
-        self._updateForwardButton()
-
-    def _getRoute(self):
-        """Get the text of the route."""
-        buffer = self._route.get_buffer()
-        return buffer.get_text(buffer.get_start_iter(),
-                               buffer.get_end_iter(), True)
-
-    def _updateForwardButton(self):
-        """Update the sensitivity of the forward button."""
-        cruiseLevelText = self._cruiseLevel.get_text()
-        cruiseLevel = int(cruiseLevelText) if cruiseLevelText else 0
-        alternate = self._alternate.get_text()
-        self._button.set_sensitive(cruiseLevel>=50 and self._getRoute()!="" and
-                                   (len(alternate)==4 or self._wizard.entranceExam))
-
-    def _cruiseLevelChanged(self, *arg):
-        """Called when the cruise level has changed."""
-        self._updateForwardButton()
-
-    def _routeChanged(self, textBuffer):
-        """Called when the route has changed."""
-        if not self._uppercasingRoute:
-            self._updateForwardButton()
-
-    def _routeInserted(self, textBuffer, iter, text, length):
-        """Called when new characters are inserted into the route.
-
-        It uppercases all characters."""
-        if not self._uppercasingRoute:
-            self._uppercasingRoute = True
-
-            iter1 = iter.copy()
-            iter1.backward_chars(length)
-            textBuffer.delete(iter, iter1)
-
-            textBuffer.insert(iter, text.upper())
-
-            self._uppercasingRoute = False
-
-    def _alternateChanged(self, entry):
-        """Called when the alternate airport has changed."""
-        entry.set_text(entry.get_text().upper())
-        self._updateForwardButton()
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        """Called when the Forward button is clicked."""
-        if self._wizard.gui.flight.aircraft.simBriefData is None:
-            self._wizard.usingSimBrief = False
-        if self._wizard.gui.config.useSimBrief and \
-           self._wizard.usingSimBrief is not False:
-            self._wizard.jumpPage("simbrief_setup")
-        else:
-            self._wizard.usingSimBrief = False
-            self._wizard.jumpPage("fuel")
-
-#-----------------------------------------------------------------------------
-
-class SimBriefCredentialsDialog(gtk.Dialog):
-    """A dialog window to ask for SimBrief credentials."""
-    def __init__(self, gui, userName, password, rememberPassword):
-        """Construct the dialog."""
-        super(SimBriefCredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
-                                                        xstr("simbrief_credentials_title"),
-                                                        gui.mainWindow,
-                                                        DIALOG_MODAL)
-        self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
-        self.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-
-        contentArea = self.get_content_area()
-
-        contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                         xscale = 0.0, yscale = 0.0)
-        contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
-                                     padding_left = 8, padding_right = 8)
-
-        contentArea.pack_start(contentAlignment, False, False, 0)
-
-        contentVBox = gtk.VBox()
-        contentAlignment.add(contentVBox)
-
-        label = gtk.Label(xstr("simbrief_login_failed"))
-        label.set_alignment(0.0, 0.0)
-
-        contentVBox.pack_start(label, False, False, 0)
-
-        tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
-                                   padding_left = 0, padding_right = 0)
-
-        table = gtk.Table(3, 2)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-
-        tableAlignment.add(table)
-        contentVBox.pack_start(tableAlignment, True, True, 0)
-
-        label = gtk.Label(xstr("simbrief_username"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 0, 1)
-
-        self._userName = gtk.Entry()
-        self._userName.set_width_chars(16)
-        #self._userName.connect("changed",
-        #                       lambda button: self._updateForwardButton())
-        self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
-        self._userName.set_text(userName)
-        table.attach(self._userName, 1, 2, 0, 1)
-        label.set_mnemonic_widget(self._userName)
-
-        label = gtk.Label(xstr("simbrief_password"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 1, 2)
-
-        self._password = gtk.Entry()
-        self._password.set_visibility(False)
-        #self._password.connect("changed",
-        #                       lambda button: self._updateForwardButton())
-        self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
-        self._password.set_text(password)
-        table.attach(self._password, 1, 2, 1, 2)
-        label.set_mnemonic_widget(self._password)
-
-        self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
-        self._rememberButton.set_use_underline(True)
-        self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
-        self._rememberButton.set_active(rememberPassword)
-        table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
-
-    @property
-    def userName(self):
-        """Get the user name entered."""
-        return self._userName.get_text()
-
-    @property
-    def password(self):
-        """Get the password entered."""
-        return self._password.get_text()
-
-    @property
-    def rememberPassword(self):
-        """Get whether the password is to be remembered."""
-        return self._rememberButton.get_active()
-
-    def run(self):
-        """Run the dialog."""
-        self.show_all()
-
-        response = super(SimBriefCredentialsDialog, self).run()
-
-        self.hide()
-
-        return response
-
-#-----------------------------------------------------------------------------
-
-class SimBriefSetupPage(Page):
-    """Page for setting up some parameters for SimBrief."""
-    monthNum2Name = [
-        "JAN",
-        "FEB",
-        "MAR",
-        "APR",
-        "MAY",
-        "JUN",
-        "JUL",
-        "AUG",
-        "SEP",
-        "OCT",
-        "NOV",
-        "DEC"
-        ]
-
-    progress2Message = {
-        cef.SIMBRIEF_PROGRESS_SEARCHING_BROWSER: "simbrief_progress_searching_browser",
-        cef.SIMBRIEF_PROGRESS_LOADING_FORM: "simbrief_progress_loading_form",
-        cef.SIMBRIEF_PROGRESS_FILLING_FORM: "simbrief_progress_filling_form",
-        cef.SIMBRIEF_PROGRESS_WAITING_LOGIN: "simbrief_progress_waiting_login",
-        cef.SIMBRIEF_PROGRESS_LOGGING_IN: "simbrief_progress_logging_in",
-        cef.SIMBRIEF_PROGRESS_WAITING_RESULT: "simbrief_progress_waiting_result",
-        cef.SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING: "simbrief_progress_retrieving_briefing"
-        }
-
-    result2Message = {
-        cef.SIMBRIEF_RESULT_ERROR_OTHER: "simbrief_result_error_other",
-        cef.SIMBRIEF_RESULT_ERROR_NO_FORM: "simbrief_result_error_no_form",
-        cef.SIMBRIEF_RESULT_ERROR_NO_POPUP: "simbrief_result_error_no_popup",
-        cef.SIMBRIEF_RESULT_ERROR_LOGIN_FAILED: "simbrief_result_error_login_failed"
-        }
-
-    @staticmethod
-    def getHTMLFilePath():
-        """Get the path of the HTML file to contain the generated flight
-        plan."""
-        if os.name=="nt":
-            return os.path.join(tempfile.gettempdir(),
-                                "mlx_simbrief" +
-                                (".secondary" if secondaryInstallation else "") +
-                                ".html")
-        else:
-            import pwd
-            return os.path.join(tempfile.gettempdir(),
-                                "mlx_simbrief." + pwd.getpwuid(os.getuid())[0] + "" +
-                                (".secondary" if secondaryInstallation else "") +
-                                ".html")
-
-    def __init__(self, wizard):
-        """Construct the setup page."""
-
-        super(SimBriefSetupPage, self).__init__(wizard, "simbrief_setup",
-                                                xstr("simbrief_setup_title"),
-                                                xstr("simbrief_setup_help"),
-                                                xstr("simbrief_setup_chelp"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(9, 3)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        label = gtk.Label(xstr("simbrief_username"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 0, 1)
-
-        self._userName = gtk.Entry()
-        self._userName.set_width_chars(16)
-        self._userName.connect("changed",
-                               lambda button: self._updateForwardButton())
-        self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
-        table.attach(self._userName, 1, 2, 0, 1)
-        label.set_mnemonic_widget(self._userName)
-
-        label = gtk.Label(xstr("simbrief_password"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 1, 2)
-
-        self._password = gtk.Entry()
-        self._password.set_visibility(False)
-        self._password.connect("changed",
-                               lambda button: self._updateForwardButton())
-        self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
-        table.attach(self._password, 1, 2, 1, 2)
-        label.set_mnemonic_widget(self._password)
-
-        self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
-        self._rememberButton.set_use_underline(True)
-        self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
-        table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
-
-        label = gtk.Label(xstr("simbrief_extra_fuel"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 3, 4)
-
-        self._extraFuel = IntegerEntry(defaultValue = 0)
-        self._extraFuel.set_width_chars(6)
-        self._extraFuel.set_tooltip_text(xstr("simbrief_extra_fuel_tooltip"))
-        table.attach(self._extraFuel, 1, 2, 3, 4)
-        label.set_mnemonic_widget(self._extraFuel)
-
-        table.attach(gtk.Label("kg"), 2, 3, 3, 4)
-
-        label = gtk.Label(xstr("simbrief_takeoff_runway"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 4, 5)
-
-        self._takeoffRunway = gtk.Entry()
-        self._takeoffRunway.set_width_chars(10)
-        self._takeoffRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
-        self._takeoffRunway.connect("changed", self._upperChanged)
-        table.attach(self._takeoffRunway, 1, 2, 4, 5)
-        label.set_mnemonic_widget(self._takeoffRunway)
-
-        label = gtk.Label(xstr("simbrief_landing_runway"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 5, 6)
-
-        self._landingRunway = gtk.Entry()
-        self._landingRunway.set_width_chars(10)
-        self._landingRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
-        self._landingRunway.connect("changed", self._upperChanged)
-        table.attach(self._landingRunway, 1, 2, 5, 6)
-        label.set_mnemonic_widget(self._landingRunway)
-
-        label = gtk.Label(xstr("simbrief_climb_profile"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 6, 7)
-
-        self._climbProfile = gtk.ComboBox()
-        renderer = gtk.CellRendererText()
-        self._climbProfile.pack_start(renderer, True)
-        self._climbProfile.add_attribute(renderer, "text", 0)
-        self._climbProfile.set_tooltip_text(xstr("simbrief_climb_profile_tooltip"))
-        table.attach(self._climbProfile, 1, 2, 6, 7)
-        label.set_mnemonic_widget(self._climbProfile)
-
-        label = gtk.Label(xstr("simbrief_cruise_profile"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 7, 8)
-
-        self._cruiseProfile = gtk.ComboBox()
-        renderer = gtk.CellRendererText()
-        self._cruiseProfile.pack_start(renderer, True)
-        self._cruiseProfile.add_attribute(renderer, "text", 0)
-        self._cruiseProfile.set_tooltip_text(xstr("simbrief_cruise_profile_tooltip"))
-        table.attach(self._cruiseProfile, 1, 2, 7, 8)
-        label.set_mnemonic_widget(self._cruiseProfile)
-
-        label = gtk.Label(xstr("simbrief_descent_profile"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 8, 9)
-
-        self._descentProfile = gtk.ComboBox()
-        renderer = gtk.CellRendererText()
-        self._descentProfile.pack_start(renderer, True)
-        self._descentProfile.add_attribute(renderer, "text", 0)
-        self._descentProfile.set_tooltip_text(xstr("simbrief_descent_profile_tooltip"))
-        table.attach(self._descentProfile, 1, 2, 8, 9)
-        label.set_mnemonic_widget(self._descentProfile)
-
-        self.addCancelFlightButton()
-
-        self._backButton = self.addPreviousButton(clicked = self._backClicked)
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-    def activate(self):
-        """Activate the SimBrief setup page"""
-        config = self._wizard.gui.config
-
-        self._userName.set_text(config.simBriefUserName)
-        self._userName.set_sensitive(True)
-
-        self._password.set_text(config.simBriefPassword)
-        self._password.set_sensitive(True)
-
-        self._rememberButton.set_active(config.rememberSimBriefPassword)
-        self._rememberButton.set_sensitive(True)
-
-        self._extraFuel.set_int(0)
-        self._extraFuel.set_sensitive(True)
-
-        self._takeoffRunway.set_text("")
-        self._takeoffRunway.set_sensitive(True)
-
-        self._landingRunway.set_text("")
-        self._landingRunway.set_sensitive(True)
-
-        simBriefData = self._wizard.gui.flight.aircraft.simBriefData
-        for (control, profiles) in [(self._climbProfile,
-                                     simBriefData.climbProfiles),
-                                    (self._cruiseProfile,
-                                     simBriefData.cruiseProfiles),
-                                    (self._descentProfile,
-                                     simBriefData.descentProfiles)]:
-            model = gtk.ListStore(str)
-            for profile in profiles:
-                model.append([profile])
-            control.set_model(model)
-            control.set_sensitive(True)
-
-        self._climbProfile.set_active(0)
-        self._cruiseProfile.set_active(0)
-        self._descentProfile.set_active(0)
-
-        self._updateForwardButton()
-
-    def _updateForwardButton(self):
-        """Update the sensitivity of the forward button."""
-        self._button.set_sensitive(len(self._userName.get_text())>0 and
-                                   len(self._password.get_text())>0)
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        if self._completed:
-            self._wizard.nextPage()
-        else:
-            config = self._wizard.gui.config
-
-            config.simBriefUserName = self._userName.get_text()
-
-            rememberPassword = self._rememberButton.get_active()
-            config.simBriefPassword = \
-              self._password.get_text() if rememberPassword else ""
-            config.rememberSimBriefPassword = rememberPassword
-
-            config.save()
-
-            plan = self._getPlan()
-            print "plan:", plan
-
-            takeoffRunway = self._takeoffRunway.get_text()
-            if takeoffRunway:
-                self._wizard.takeoffRunway = takeoffRunway
-
-            landingRunway = self._landingRunway.get_text()
-            if landingRunway:
-                self._wizard.landingRunway = landingRunway
-
-            self._userName.set_sensitive(False)
-            self._password.set_sensitive(False)
-            self._rememberButton.set_sensitive(False)
-            self._extraFuel.set_sensitive(False)
-            self._takeoffRunway.set_sensitive(False)
-            self._landingRunway.set_sensitive(False)
-
-            self._climbProfile.set_sensitive(False)
-            self._cruiseProfile.set_sensitive(False)
-            self._descentProfile.set_sensitive(False)
-
-            self._wizard.gui.beginBusy(xstr("simbrief_calling"))
-
-            cef.callSimBrief(plan,
-                             self._getCredentials,
-                             self._simBriefProgress,
-                             SimBriefSetupPage.getHTMLFilePath())
-
-            startSound(const.SOUND_NOTAM)
-
-    def _getCredentials(self, count):
-        """Get the credentials.
-
-        If count is 0, the user name and password entered into the setup page
-        are returned. Otherwise a dialog box is displayed informing the user of
-        invalid credentials and requesting another set of them."""
-        print "_getCredentials", count
-        if count==0:
-            return (self._userName.get_text(), self._password.get_text())
-        else:
-            gui = self._wizard.gui
-            config = gui.config
-
-            dialog = SimBriefCredentialsDialog(gui,
-                                               config.simBriefUserName,
-                                               config.simBriefPassword,
-                                               config.rememberSimBriefPassword)
-            response = dialog.run()
-
-            if response==RESPONSETYPE_OK:
-                userName = dialog.userName
-                self._userName.set_text(userName)
-                password = dialog.password
-                self._password.set_text(password)
-                rememberPassword = dialog.rememberPassword
-
-                config.simBriefUserName = userName
-
-                config.simBriefPassword = \
-                    password if rememberPassword else ""
-                config.rememberSimBriefPassword = rememberPassword
-
-                config.save()
-
-                return (userName, password)
-            else:
-                return (None, None)
-
-    def _simBriefProgress(self, progress, result, flightInfo):
-        """The real SimBrief progress handler."""
-        print "_simBriefProgress", progress, result, flightInfo
-        if result==cef.SIMBRIEF_RESULT_NONE:
-            message = SimBriefSetupPage.progress2Message.get(progress,
-                                                             "simbrief_progress_unknown")
-            self._wizard.gui.updateBusyState(xstr(message))
-        else:
-            self._wizard.gui.endBusy()
-
-            if result==cef.SIMBRIEF_RESULT_OK:
-                self._wizard.departureMETARChanged(flightInfo["orig_metar"],
-                                                   self)
-                self._wizard.arrivalMETARChanged(flightInfo["dest_metar"], self)
-                self._wizard.nextPage()
-            else:
-                message = SimBriefSetupPage.result2Message.get(result,
-                                                               "simbrief_result_unknown")
-                dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
-                                           type = MESSAGETYPE_ERROR,
-                                           message_format =
-                                           xstr(message) + "\n"+
-                                           xstr("simbrief_cancelled"))
-
-                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-                dialog.set_title(WINDOW_TITLE_BASE)
-                secondary = xstr("flightsel_save_failed_sec")
-                dialog.format_secondary_markup(secondary)
-                dialog.run()
-                dialog.hide()
-
-                self._wizard.usingSimBrief = False
-                self._wizard.jumpPage("fuel", fromPageShift = 1)
-
-    def _getPlan(self):
-        """Get the flight plan data for SimBrief."""
-        plan = {
-            "airline": "MAH",
-            "selcal": "XXXX",
-            "fuelfactor": "P000",
-            "contpct": "0.05",
-            "resvrule": "45",
-            "taxiout": "10",
-            "taxiin": "10",
-            "civalue": "AUTO"
-            }
-
-        wizard = self._wizard
-        gui = wizard.gui
-
-        loginResult = wizard.loginResult
-        plan["cpt"] = loginResult.pilotName
-        plan["pid"] = loginResult.pilotID
-
-        bookedFlight = wizard.bookedFlight
-        plan["fltnum"] = wizard.bookedFlight.callsign[2:]
-        plan["type"] = const.icaoCodes[bookedFlight.aircraftType]
-        plan["orig"] = bookedFlight.departureICAO
-        plan["dest"] = bookedFlight.arrivalICAO
-        plan["reg"] = bookedFlight.tailNumber
-        plan["fin"] = bookedFlight.tailNumber[3:]
-        plan["pax"] = str(bookedFlight.numPassengers)
-
-        departureTime = bookedFlight.departureTime
-        plan["date"] = "%d%s%d" % (departureTime.day,
-                                   SimBriefSetupPage.monthNum2Name[departureTime.month-1],
-                                   departureTime.year%100)
-        plan["deph"] = str(departureTime.hour)
-        plan["depm"] = str(departureTime.minute)
-
-        arrivalTime = bookedFlight.arrivalTime
-        plan["steh"] = str(arrivalTime.hour)
-        plan["stem"] = str(arrivalTime.minute)
-
-        plan["manualzfw"] = str(wizard.zfw / 1000.0)
-        plan["cargo"] = str((wizard.bagWeight + wizard.cargoWeight + wizard.mailWeight)/1000.0)
-
-        plan["route"] = wizard.route
-        plan["fl"] = str(wizard.filedCruiseAltitude)
-        plan["altn"] = wizard.alternate
-
-        plan["addedfuel"] = str(self._extraFuel.get_int() / 1000.0)
-        plan["origrwy"] = self._takeoffRunway.get_text()
-        plan["destrwy"] = self._landingRunway.get_text()
-
-        for (key, control) in [("climb", self._climbProfile),
-                               ("cruise", self._cruiseProfile),
-                               ("descent", self._descentProfile)]:
-            model = control.get_model()
-            active = control.get_active_iter()
-            value = model.get_value(active, 0)
-            plan[key] = value
-
-        return plan
-
-    def _upperChanged(self, entry, arg = None):
-        """Called when the value of some entry widget has changed and the value
-        should be converted to uppercase."""
-        entry.set_text(entry.get_text().upper())
-
-#-----------------------------------------------------------------------------
-
-class SimBriefingPage(Page):
-    """Page to display the SimBrief HTML briefing."""
-    class BrowserLifeSpanHandler(object):
-        """The life-span handler of a browser."""
-        def __init__(self, simBriefingPage):
-            """Construct the life-span handler for the given page."""
-            self._simBriefingPage = simBriefingPage
-
-        def OnBeforeClose(self, browser):
-            """Called before closing the browser."""
-            self._simBriefingPage._invalidateBrowser()
-
-    def __init__(self, wizard):
-        """Construct the setup page."""
-
-        super(SimBriefingPage, self).__init__(wizard, "simbrief_result",
-                                              xstr("simbrief_result_title"), "")
-
-        self._alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                       xscale = 1.0, yscale = 1.0)
-
-        self._container = cef.getContainer()
-        self._alignment.add(self._container)
-
-        self.setMainWidget(self._alignment)
-
-        self._browser = None
-
-        self.addCancelFlightButton()
-
-        self.addPreviousButton(clicked = self._backClicked)
-
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-        self._button.set_label(xstr("briefing_button"))
-        self._button.set_has_tooltip(False)
-        self._button.set_use_stock(False)
-
-    def activate(self):
-        """Activate the SimBrief flight plan page"""
-        if self._browser is None:
-            self._startBrowser()
-        else:
-            self._browser.Reload()
-
-    def grabDefault(self):
-        """If the page has a default button, make it the default one."""
-        super(SimBriefingPage, self).grabDefault()
-
-        if self._browser is None:
-            self._startBrowser()
-
-    def _backClicked(self, button):
-        """Called when the Back button has been pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        """Called when the Forward button has been pressed."""
-        if not self._completed:
-            self._button.set_label(xstr("button_next"))
-            self._button.set_tooltip_text(xstr("button_next_tooltip"))
-            self._wizard.usingSimBrief = True
-            self.complete()
-
-        self._wizard.nextPage()
-
-    def _startBrowser(self):
-        """Start the browser.
-
-        If a container is needed, create one."""
-        if self._container is None:
-            self._container = cef.getContainer()
-            self._alignment.add(self._container)
-
-        url = "file://" + SimBriefSetupPage.getHTMLFilePath()
-        self._browser = cef.startInContainer(self._container, url)
-
-        lifeSpanHandler = SimBriefingPage.BrowserLifeSpanHandler(self)
-        self._browser.SetClientHandler(lifeSpanHandler)
-
-    def _invalidateBrowser(self):
-        """Invalidate the browser (and associated stuff)."""
-        self._alignment.remove(self._container)
-        self._container = None
-        self._browser = None
-
-#-----------------------------------------------------------------------------
-
-class FuelTank(gtk.VBox):
-    """Widget for the fuel tank."""
-    def __init__(self, fuelTank, name, capacity, currentWeight):
-        """Construct the widget for the tank with the given name."""
-        super(FuelTank, self).__init__()
-
-        self._enabled = True
-        self.fuelTank = fuelTank
-        self.capacity = capacity
-        self.currentWeight = currentWeight
-        self.expectedWeight = currentWeight
-
-        label = gtk.Label("<b>" + name + "</b>")
-        label.set_use_markup(True)
-        label.set_use_underline(True)
-        label.set_justify(JUSTIFY_CENTER)
-        label.set_alignment(0.5, 1.0)
-        self.pack_start(label, False, False, 4)
-
-        self._tankFigure = gtk.EventBox()
-        self._tankFigure.set_size_request(38, -1)
-        self._tankFigure.set_visible_window(False)
-        self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
-
-        if pygobject:
-            self._tankFigure.connect("draw", self._drawTankFigure)
-        else:
-            self._tankFigure.connect("expose_event", self._drawTankFigure)
-        self._tankFigure.connect("button_press_event", self._buttonPressed)
-        self._tankFigure.connect("motion_notify_event", self._motionNotify)
-        self._tankFigure.connect("scroll-event", self._scrolled)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 1.0)
-        alignment.add(self._tankFigure)
-
-        self.pack_start(alignment, True, True, 4)
-
-        self._expectedButton = gtk.SpinButton()
-        self._expectedButton.set_numeric(True)
-        self._expectedButton.set_range(0, self.capacity)
-        self._expectedButton.set_increments(10, 100)
-        self._expectedButton.set_value(currentWeight)
-        self._expectedButton.set_alignment(1.0)
-        self._expectedButton.set_width_chars(5)
-        self._expectedButton.connect("value-changed", self._expectedChanged)
-
-        label.set_mnemonic_widget(self._expectedButton)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 1.0)
-        alignment.add(self._expectedButton)
-        self.pack_start(alignment, False, False, 4)
-
-    def setCurrent(self, currentWeight):
-        """Set the current weight."""
-        self.currentWeight = currentWeight
-        self._redraw()
-
-    def isCorrect(self):
-        """Determine if the contents of the fuel tank are as expected"""
-        return abs(self.expectedWeight - self.currentWeight)<=1
-
-    def disable(self):
-        """Disable the fuel tank."""
-        self._expectedButton.set_sensitive(False)
-        self._enabled = False
-
-    def _redraw(self):
-        """Redraw the tank figure."""
-        self._tankFigure.queue_draw()
-
-    def _drawTankFigure(self, tankFigure, eventOrContext):
-        """Draw the tank figure."""
-        triangleSize = 5
-
-        context = eventOrContext if pygobject else tankFigure.window.cairo_create()
-        (xOffset, yOffset) = (0, 0) if pygobject \
-                             else (tankFigure.allocation.x, tankFigure.allocation.y)
-
-        width = tankFigure.get_allocated_width() if pygobject \
-                else tankFigure.allocation.width
-        height = tankFigure.get_allocated_height() if pygobject \
-                 else tankFigure.allocation.height
-
-        rectangleX0 = triangleSize
-        rectangleY0 = triangleSize
-        rectangleX1 = width - 1 - triangleSize
-        rectangleY1 = height - 1 - triangleSize
-        rectangleLineWidth = 2.0
-
-        context.set_source_rgb(0.0, 0.0, 0.0)
-        context.set_line_width(rectangleLineWidth)
-        context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
-                          yOffset + rectangleY0 + rectangleLineWidth/2,
-                          rectangleX1 - rectangleX0 - rectangleLineWidth,
-                          rectangleY1 - rectangleY0 - rectangleLineWidth)
-        context.stroke()
-
-        rectangleInnerLeft   = rectangleX0 + rectangleLineWidth
-        rectangleInnerRight  = rectangleX1 - rectangleLineWidth
-        self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
-        self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
-
-        rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
-        rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
-
-        context.set_source_rgb(1.0, 0.9, 0.6)
-        currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
-        currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
-        context.rectangle(xOffset + rectangleInnerLeft,
-                          yOffset + rectangleInnerTop +
-                          rectangleInnerHeight - currentHeight,
-                          rectangleInnerWidth, currentHeight)
-        context.fill()
-
-        expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
-        expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
-
-        context.set_line_width(1.5)
-        context.set_source_rgb(0.0, 0.85, 0.85)
-        context.move_to(xOffset + rectangleX0, yOffset + expectedY)
-        context.line_to(xOffset + rectangleX1, yOffset + expectedY)
-        context.stroke()
-
-        context.set_line_width(0.0)
-        context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
-        context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
-        context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
-        context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
-        context.fill()
-
-        context.set_line_width(0.0)
-        context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
-        context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
-        context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
-        context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
-        context.fill()
-
-        return True
-
-    def _setExpectedFromY(self, y):
-        """Set the expected weight from the given Y-coordinate."""
-        level = (self._rectangleInnerBottom - y) / \
-                (self._rectangleInnerBottom - self._rectangleInnerTop)
-        level = min(1.0, max(0.0, level))
-        self._expectedButton.set_value(level * self.capacity)
-
-    def _buttonPressed(self, tankFigure, event):
-        """Called when a button is pressed in the figure.
-
-        The expected level will be set there."""
-        if self._enabled and event.button==1:
-            self._setExpectedFromY(event.y)
-
-    def _motionNotify(self, tankFigure, event):
-        """Called when the mouse pointer moves within the area of a tank figure."""
-        if self._enabled and event.state==BUTTON1_MASK:
-            self._setExpectedFromY(event.y)
-
-    def _scrolled(self, tankFigure, event):
-        """Called when a scroll event is received."""
-        if self._enabled:
-            increment = 1 if event.state==CONTROL_MASK \
-                        else 100 if event.state==SHIFT_MASK \
-                        else 10 if event.state==0 else 0
-            if increment!=0:
-                if event.direction==SCROLL_DOWN:
-                    increment *= -1
-                self._expectedButton.spin(SPIN_USER_DEFINED, increment)
-
-    def _expectedChanged(self, spinButton):
-        """Called when the expected value has changed."""
-        self.expectedWeight = spinButton.get_value_as_int()
-        self._redraw()
-
-#-----------------------------------------------------------------------------
-
-class FuelPage(Page):
-    """The page containing the fuel tank filling."""
-    _pumpStep = 0.02
-
-    def __init__(self, wizard):
-        """Construct the page."""
-        super(FuelPage, self).__init__(wizard, "fuel",
-                                       xstr("fuel_title"),
-                                       xstr("fuel_help_pre") +
-                                       xstr("fuel_help_post"),
-                                       completedHelp = xstr("fuel_chelp"))
-
-        self._fuelTanks = []
-        self._fuelTable = None
-        self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                            xscale = 0.0, yscale = 1.0)
-        self.setMainWidget(self._fuelAlignment)
-
-        tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
-        self._setupTanks(tankData)
-
-        self.addCancelFlightButton()
-
-        self._backButton = self.addPreviousButton(clicked = self._backClicked)
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-        self._pumpIndex = 0
-
-    def activate(self):
-        """Activate the page."""
-        self._setupTanks(self._wizard._fuelData)
-
-        aircraft = self._wizard.gui.flight.aircraft
-        minLandingFuel = aircraft.minLandingFuel
-        recommendedLandingFuel = aircraft.recommendedLandingFuel
-
-        middleHelp = "" if minLandingFuel is None else \
-            (xstr("fuel_help_min") % (minLandingFuel,)) \
-            if recommendedLandingFuel is None else \
-            (xstr("fuel_help_min_rec") % (minLandingFuel,
-                                          recommendedLandingFuel))
-        self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
-
-    def finalize(self):
-        """Finalize the page."""
-        for fuelTank in self._fuelTanks:
-            fuelTank.disable()
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        """Called when the forward button is clicked."""
-        if not self._completed:
-            self._pumpIndex = 0
-            self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
-            self._pump()
-        elif self._wizard.usingSimBrief:
-            self._wizard.jumpPage("takeoff")
-        else:
-            self._wizard.jumpPage("briefing1")
-
-    def _setupTanks(self, tankData):
-        """Setup the tanks for the given data."""
-        numTanks = len(tankData)
-        if self._fuelTable is not None:
-            self._fuelAlignment.remove(self._fuelTable)
-
-        self._fuelTanks = []
-        self._fuelTable = gtk.Table(numTanks, 1)
-        self._fuelTable.set_col_spacings(16)
-        index = 0
-        for (tank, current, capacity) in tankData:
-            fuelTank = FuelTank(tank,
-                                xstr("fuel_tank_" +
-                                     const.fuelTank2string(tank)),
-                                capacity, current)
-            self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
-            self._fuelTanks.append(fuelTank)
-            index += 1
-
-        self._fuelAlignment.add(self._fuelTable)
-        self.show_all()
-
-    def _pump(self):
-        """Perform one step of pumping.
-
-        It is checked, if the current tank's contents are of the right
-        quantity. If not, it is filled one step further to the desired
-        contents. Otherwise the next tank is started. If all tanks are are
-        filled, the next page is selected."""
-        numTanks = len(self._fuelTanks)
-
-        fuelTank = None
-        while self._pumpIndex < numTanks:
-            fuelTank = self._fuelTanks[self._pumpIndex]
-            if fuelTank.isCorrect():
-                self._pumpIndex += 1
-                fuelTank = None
-            else:
-                break
-
-        if fuelTank is None:
-            self._wizard.gui.endBusy()
-            if self._wizard.usingSimBrief:
-                self._wizard.gui.startMonitoring()
-                self._wizard.jumpPage("takeoff")
-            else:
-                bookedFlight = self._wizard._bookedFlight
-                self._wizard.gui.beginBusy(xstr("route_down_notams"))
-                self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
-                                                      bookedFlight.departureICAO,
-                                                      bookedFlight.arrivalICAO)
-                startSound(const.SOUND_NOTAM)
-        else:
-            currentLevel = fuelTank.currentWeight / fuelTank.capacity
-            expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
-            if currentLevel<expectedLevel:
-                currentLevel += FuelPage._pumpStep
-                if currentLevel>expectedLevel: currentLevel = expectedLevel
-            else:
-                currentLevel -= FuelPage._pumpStep
-                if currentLevel<expectedLevel: currentLevel = expectedLevel
-            fuelTank.setCurrent(currentLevel * fuelTank.capacity)
-            self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
-                                                      currentLevel)])
-            gobject.timeout_add(50, self._pump)
-
-    def _notamsCallback(self, returned, result):
-        """Callback for the NOTAMs."""
-        gobject.idle_add(self._handleNOTAMs, returned, result)
-
-    def _handleNOTAMs(self, returned, result):
-        """Handle the NOTAMs."""
-        if returned:
-            self._wizard._departureNOTAMs = result.departureNOTAMs
-            self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
-        else:
-            self._wizard._departureNOTAMs = None
-            self._wizard._arrivalNOTAMs = None
-
-        bookedFlight = self._wizard._bookedFlight
-        self._wizard.gui.beginBusy(xstr("route_down_metars"))
-        self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
-                                              [bookedFlight.departureICAO,
-                                               bookedFlight.arrivalICAO])
-
-    def _metarsCallback(self, returned, result):
-        """Callback for the METARs."""
-        gobject.idle_add(self._handleMETARs, returned, result)
-
-    def _handleMETARs(self, returned, result):
-        """Handle the METARs."""
-        self._wizard._departureMETAR = None
-        self._wizard._arrivalMETAR = None
-        bookedFlight = self._wizard._bookedFlight
-        if returned:
-            if bookedFlight.departureICAO in result.metars:
-                self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
-            if bookedFlight.arrivalICAO in result.metars:
-                self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
-
-        self._wizard.gui.endBusy()
-        self._backButton.set_sensitive(True)
-        self._button.set_sensitive(True)
-        self._wizard.nextPage()
-
-#-----------------------------------------------------------------------------
-
-class BriefingPage(Page):
-    """Page for the briefing."""
-    def __init__(self, wizard, departure):
-        """Construct the briefing page."""
-        self._departure = departure
-
-        number = 1 if departure else 2
-
-        title = xstr("briefing_title") % (number,
-                                          xstr("briefing_departure")
-                                          if departure
-                                          else xstr("briefing_arrival"))
-        super(BriefingPage, self).__init__(wizard,
-                                           "briefing%d" % (number,),
-                                           title, xstr("briefing_help"),
-                                           completedHelp = xstr("briefing_chelp"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 1.0, yscale = 1.0)
-
-        mainBox = gtk.VBox()
-        alignment.add(mainBox)
-        self.setMainWidget(alignment)
-
-        self._notamsFrame = gtk.Frame()
-        self._notamsFrame.set_label(xstr("briefing_notams_init"))
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.set_size_request(-1, 128)
-        # FIXME: these constants should be in common
-        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC,
-                                  gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC)
-        self._notams = gtk.TextView()
-        self._notams.set_editable(False)
-        self._notams.set_accepts_tab(False)
-        self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
-        scrolledWindow.add(self._notams)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 0,
-                              padding_left = 0, padding_right = 0)
-        alignment.add(scrolledWindow)
-        self._notamsFrame.add(alignment)
-        mainBox.pack_start(self._notamsFrame, True, True, 4)
-
-        self._metarFrame = gtk.Frame()
-        self._metarFrame.set_label(xstr("briefing_metar_init"))
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.set_size_request(-1, 32)
-        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC,
-                                  gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC)
-
-        self._updatingMETAR = False
-
-        self._metar = gtk.TextView()
-        self._metar.set_accepts_tab(False)
-        self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
-        self._metar.get_buffer().connect("changed", self._metarChanged)
-        self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
-        scrolledWindow.add(self._metar)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 0,
-                              padding_left = 0, padding_right = 0)
-        alignment.add(scrolledWindow)
-        self._metarFrame.add(alignment)
-        mainBox.pack_start(self._metarFrame, True, True, 4)
-        self.metarEdited = False
-
-        self.addCancelFlightButton()
-
-        self.addPreviousButton(clicked = self._backClicked)
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-    @property
-    def metar(self):
-        """Get the METAR on the page."""
-        buffer = self._metar.get_buffer()
-        return buffer.get_text(buffer.get_start_iter(),
-                               buffer.get_end_iter(), True)
-
-    def setMETAR(self, metar):
-        """Set the METAR."""
-        self._metar.get_buffer().set_text(metar)
-        self.metarEdited = False
-
-    def changeMETAR(self, metar):
-        """Change the METAR as a result of an edit on one of the other
-        pages."""
-        self._updatingMETAR = True
-        self._metar.get_buffer().set_text(metar)
-        self._updatingMETAR = False
-
-        self._updateButton()
-        self.metarEdited = True
-
-    def activate(self):
-        """Activate the page."""
-        if not self._departure:
-            self._button.set_label(xstr("briefing_button"))
-            self._button.set_has_tooltip(False)
-            self._button.set_use_stock(False)
-
-        bookedFlight = self._wizard._bookedFlight
-
-        icao = bookedFlight.departureICAO if self._departure \
-               else bookedFlight.arrivalICAO
-        notams = self._wizard._departureNOTAMs if self._departure \
-                 else self._wizard._arrivalNOTAMs
-        metar = self._wizard._departureMETAR if self._departure \
-                 else self._wizard._arrivalMETAR
-
-        self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
-        buffer = self._notams.get_buffer()
-        if notams is None:
-            buffer.set_text(xstr("briefing_notams_failed"))
-        elif not notams:
-            buffer.set_text(xstr("briefing_notams_missing"))
-        else:
-            s = ""
-            for notam in notams:
-                s += str(notam)
-                s += "-------------------- * --------------------\n"
-            buffer.set_text(s)
-
-        self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
-        buffer = self._metar.get_buffer()
-        self._updatingMETAR = True
-        if metar is None:
-            buffer.set_text("")
-            self.setHelp(xstr("briefing_help_nometar"))
-        else:
-            buffer.set_text(metar)
-        self._updatingMETAR = False
-        self._updateButton()
-
-        label = self._metarFrame.get_label_widget()
-        label.set_use_underline(True)
-        label.set_mnemonic_widget(self._metar)
-
-        self.metarEdited = False
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        """Called when the forward button is clicked."""
-        if not self._departure:
-            if not self._completed:
-                self._wizard.gui.startMonitoring()
-                self._button.set_label(xstr("button_next"))
-                self._button.set_tooltip_text(xstr("button_next_tooltip"))
-                self.complete()
-
-        self._wizard.nextPage()
-
-    def _metarChanged(self, buffer):
-        """Called when the METAR has changed."""
-        print "BriefingPage.metarChanged", self._updatingMETAR
-        if not self._updatingMETAR:
-            self.metarEdited = True
-            self._updateButton()
-            metar = buffer.get_text(buffer.get_start_iter(),
-                                buffer.get_end_iter(), True)
-            self._wizard.metarChanged(metar, self)
-
-    def _metarInserted(self, textBuffer, iter, text, length):
-        """Called when new characters are inserted into the METAR.
-
-        It uppercases all characters."""
-        print "BriefingPage.metarInserted", self._updatingMETAR
-        if not self._updatingMETAR:
-            self._updatingMETAR = True
-
-            iter1 = iter.copy()
-            iter1.backward_chars(length)
-            textBuffer.delete(iter, iter1)
-
-            textBuffer.insert(iter, text.upper())
-
-            self._updatingMETAR = False
-
-    def _updateButton(self):
-        """Update the sensitivity of the Next button based on the contents of
-        the METAR field."""
-        buffer = self._metar.get_buffer()
-        self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
-                                                   buffer.get_end_iter(),
-                                                   True)!="")
-
-
-#-----------------------------------------------------------------------------
-
-class TakeoffPage(Page):
-    """Page for entering the takeoff data."""
-    def __init__(self, wizard):
-        """Construct the takeoff page."""
-        super(TakeoffPage, self).__init__(wizard, "takeoff",
-                                          xstr("takeoff_title"),
-                                          xstr("takeoff_help"),
-                                          completedHelp = xstr("takeoff_chelp"))
-
-        self._forwardAllowed = False
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(9, 24)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        row = 0
-
-        label = gtk.Label(xstr("takeoff_metar"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row+1)
-
-        self._metar = gtk.Entry()
-        self._metar.set_width_chars(40)
-        self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
-        self._metar.connect("changed", self._metarChanged)
-        self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
-        table.attach(self._metar, 1, 24, row, row+1)
-        label.set_mnemonic_widget(self._metar)
-
-        self._updatingMETAR = False
-
-        row += 1
-
-        label = gtk.Label(xstr("takeoff_runway"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row+1)
-
-        self._runway = gtk.Entry()
-        self._runway.set_width_chars(10)
-        self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
-        self._runway.connect("changed", self._upperChanged)
-        table.attach(self._runway, 1, 3, row, row+1)
-        label.set_mnemonic_widget(self._runway)
-
-        row += 1
-
-        label = gtk.Label(xstr("takeoff_sid"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row+1)
-
-        if pygobject:
-            self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
-        else:
-            self._sid = gtk.ComboBoxEntry(comboModel)
-
-        self._sid.set_entry_text_column(0)
-        self._sid.get_child().set_width_chars(10)
-        self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
-        self._sid.connect("changed", self._upperChangedComboBox)
-        table.attach(self._sid, 1, 3, row, row+1)
-        label.set_mnemonic_widget(self._sid)
-
-        row += 1
-
-        label = gtk.Label(xstr("takeoff_v1"))
-        label.set_use_markup(True)
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row+1)
-
-        self._v1 = IntegerEntry()
-        self._v1.set_width_chars(4)
-        self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
-        self._v1.connect("integer-changed", self._valueChanged)
-        table.attach(self._v1, 2, 3, row, row+1)
-        label.set_mnemonic_widget(self._v1)
-
-        self._v1Unit = gtk.Label(xstr("label_knots"))
-        self._v1Unit.set_alignment(0.0, 0.5)
-        table.attach(self._v1Unit, 3, 4, row, row+1)
-
-        row += 1
-
-        label = gtk.Label(xstr("takeoff_vr"))
-        label.set_use_markup(True)
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row+1)
-
-        self._vr = IntegerEntry()
-        self._vr.set_width_chars(4)
-        self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
-        self._vr.connect("integer-changed", self._valueChanged)
-        table.attach(self._vr, 2, 3, row, row+1)
-        label.set_mnemonic_widget(self._vr)
-
-        self._vrUnit = gtk.Label(xstr("label_knots"))
-        self._vrUnit.set_alignment(0.0, 0.5)
-        table.attach(self._vrUnit, 3, 4, row, row+1)
-
-        row += 1
-
-        label = gtk.Label(xstr("takeoff_v2"))
-        label.set_use_markup(True)
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row+1)
-
-        self._v2 = IntegerEntry()
-        self._v2.set_width_chars(4)
-        self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
-        self._v2.connect("integer-changed", self._valueChanged)
-        table.attach(self._v2, 2, 3, row, row+1)
-        label.set_mnemonic_widget(self._v2)
-
-        self._v2Unit = gtk.Label(xstr("label_knots"))
-        self._v2Unit.set_alignment(0.0, 0.5)
-        table.attach(self._v2Unit, 3, 4, row, row+1)
-
-        row += 1
-
-        self._derateType = acft.DERATE_NONE
-
-        self._derateLabel = gtk.Label()
-        self._derateLabel.set_use_underline(True)
-        self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
-        self._derateLabel.set_alignment(0.0, 0.5)
-        table.attach(self._derateLabel, 0, 1, row, row+1)
-
-        self._derate = gtk.Alignment()
-        table.attach(self._derate, 2, 4, row, row+1)
-        self._derateWidget = None
-        self._derateEntry = None
-        self._derateUnit = None
-        self._derateButtons = None
-
-        row += 1
-
-        self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
-        self._antiIceOn.set_use_underline(True)
-        self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
-        table.attach(self._antiIceOn, 2, 4, row, row+1)
-
-        row += 1
-
-        self._rto = gtk.CheckButton(xstr("takeoff_rto"))
-        self._rto.set_use_underline(True)
-        self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
-        self._rto.connect("toggled", self._rtoToggled)
-        table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
-
-        self.addCancelFlightButton()
-
-        self.addPreviousButton(clicked = self._backClicked)
-
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-        self._active = False
-
-    @property
-    def runway(self):
-        """Get the runway."""
-        return self._runway.get_text()
-
-    @property
-    def sid(self):
-        """Get the SID."""
-        text = self._sid.get_child().get_text()
-        return text if self._sid.get_active()!=0 and text and text!="N/A" \
-               else None
-
-    @property
-    def v1(self):
-        """Get the v1 speed."""
-        return self._v1.get_int()
-
-    @property
-    def vr(self):
-        """Get the vr speed."""
-        return self._vr.get_int()
-
-    @property
-    def v2(self):
-        """Get the v2 speed."""
-        return self._v2.get_int()
-
-    @property
-    def derate(self):
-        """Get the derate value, if any."""
-        if self._derateWidget is None:
-            return None
-        if self._derateType==acft.DERATE_BOEING:
-            derate = self._derateEntry.get_text()
-            return derate if derate else None
-        elif self._derateType==acft.DERATE_EPR:
-            derate = self._derateWidget.get_text()
-            return derate if derate else None
-        elif self._derateType==acft.DERATE_TUPOLEV:
-            return acft.DERATE_TUPOLEV_NOMINAL \
-                   if self._derateButtons[0].get_active() \
-                   else acft.DERATE_TUPOLEV_TAKEOFF
-        elif self._derateType==acft.DERATE_B462:
-            return self._derateWidget.get_active()
-        else:
-            return None
-
-    @property
-    def antiIceOn(self):
-        """Get whether the anti-ice system has been turned on."""
-        return self._antiIceOn.get_active()
-
-    @antiIceOn.setter
-    def antiIceOn(self, value):
-        """Set the anti-ice indicator."""
-        self._antiIceOn.set_active(value)
-
-    @property
-    def rtoIndicated(self):
-        """Get whether the pilot has indicated if there was an RTO."""
-        return self._rto.get_active()
-
-    def activate(self):
-        """Activate the page."""
-        print "TakeoffPage.activate"
-
-        self._updatingMETAR = True
-        self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
-        self._updatingMETAR = False
-
-        if self._wizard.takeoffRunway is None:
-            self._runway.set_text("")
-        else:
-            self._runway.set_text(self._wizard.takeoffRunway)
-        self._runway.set_sensitive(True)
-        self._sid.set_active(0)
-        self._sid.set_sensitive(True)
-        self._v1.set_int(None)
-        self._v1.set_sensitive(True)
-        self._vr.set_int(None)
-        self._vr.set_sensitive(True)
-        self._v2.set_int(None)
-        self._v2.set_sensitive(True)
-
-        i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
-        speedUnit = xstr("label" + i18nSpeedUnit)
-        self._v1Unit.set_text(speedUnit)
-        self._vrUnit.set_text(speedUnit)
-        self._v2Unit.set_text(speedUnit)
-
-        self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
-        self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
-        self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
-
-        self._derateType = self._wizard.gui.flight.aircraft.derateType
-
-        self._setupDerateWidget()
-
-        self._rto.set_active(False)
-        self._rto.set_sensitive(False)
-
-        self._button.set_sensitive(False)
-        self._forwardAllowed = False
-
-        self._active = True
-
-    def allowForward(self):
-        """Allow going to the next page."""
-        print "TakeoffPage.allowForward"
-        self._forwardAllowed = True
-        self._updateForwardButton()
-
-    def reset(self):
-        """Reset the page if the wizard is reset."""
-        print "TakeoffPage.reset"
-
-        super(TakeoffPage, self).reset()
-        self._v1.reset()
-        self._vr.reset()
-        self._v2.reset()
-        self._hasDerate = False
-        self._antiIceOn.set_active(False)
-        self._active = False
-
-    def setRTOEnabled(self, enabled):
-        """Set the RTO checkbox enabled or disabled."""
-        if not enabled:
-            self._rto.set_active(False)
-        self._rto.set_sensitive(enabled)
-
-    def changeMETAR(self, metar):
-        """Change the METAR as a result of an edit on one of the other
-        pages."""
-        if self._active:
-            print "TakeoffPage.changeMETAR"
-            self._updatingMETAR = True
-            self._metar.get_buffer().set_text(metar, -1)
-            self._updatingMETAR = False
-
-            self._updateForwardButton()
-
-    def _updateForwardButton(self):
-        """Update the sensitivity of the forward button based on some conditions."""
-        sensitive = self._forwardAllowed and \
-                    self._metar.get_text()!="" and \
-                    self._runway.get_text()!="" and \
-                    self.sid is not None and \
-                    self.v1 is not None and \
-                    self.vr is not None and \
-                    self.v2 is not None and \
-                    self.v1 <= self.vr and \
-                    self.vr <= self.v2 and \
-                    (self._derateType==acft.DERATE_NONE or
-                     self.derate is not None)
-
-        print "TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive
-        if self._forwardAllowed:
-            print "  METAR: ", self._metar.get_text()
-            print "  runway: ", self._runway.get_text()
-            print "  SID:", self.sid
-            print "  V1:", self.v1
-            print "  VR:", self.vr
-            print "  V2:", self.v2
-            print "  derateType:", self._derateType
-            print "  derate:", self.derate
-
-        self._button.set_sensitive(sensitive)
-
-    def _valueChanged(self, widget, arg = None):
-        """Called when the value of some widget has changed."""
-        print "TakeoffPage._valueChanged"
-
-        self._updateForwardButton()
-
-    def _upperChanged(self, entry, arg = None):
-        """Called when the value of some entry widget has changed and the value
-        should be converted to uppercase."""
-        print "TakeoffPage._upperChanged"
-        entry.set_text(entry.get_text().upper())
-        self._valueChanged(entry, arg)
-
-    def _upperChangedComboBox(self, comboBox):
-        """Called for combo box widgets that must be converted to uppercase."""
-        entry = comboBox.get_child()
-        if comboBox.get_active()==-1:
-            entry.set_text(entry.get_text().upper())
-        self._valueChanged(entry)
-
-    def _derateChanged(self, entry):
-        """Called when the value of the derate is changed."""
-        print "TakeoffPage._derateChanged"
-        self._updateForwardButton()
-
-    def _rtoToggled(self, button):
-        """Called when the RTO check button is toggled."""
-        self._wizard.rtoToggled(button.get_active())
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        """Called when the forward button is clicked."""
-        aircraft = self._wizard.gui.flight.aircraft
-        aircraft.updateV1R2()
-        if self.derate is not None:
-            aircraft.updateDerate()
-        aircraft.updateTakeoffAntiIce()
-        self._wizard.nextPage()
-
-    def _setupDerateWidget(self):
-        """Setup the derate widget."""
-        if self._derateWidget is not None:
-            self._derate.remove(self._derateWidget)
-
-        if self._derateType==acft.DERATE_BOEING:
-            self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
-            self._derateLabel.set_use_underline(True)
-            self._derateLabel.set_sensitive(True)
-
-            self._derateEntry = gtk.Entry()
-            self._derateEntry.set_width_chars(7)
-            self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
-            self._derateEntry.set_alignment(1.0)
-            self._derateEntry.connect("changed", self._derateChanged)
-            self._derateLabel.set_mnemonic_widget(self._derateEntry)
-
-            self._derateUnit = gtk.Label("%")
-            self._derateUnit.set_alignment(0.0, 0.5)
-
-            self._derateWidget = gtk.Table(3, 1)
-            self._derateWidget.set_row_spacings(4)
-            self._derateWidget.set_col_spacings(16)
-            self._derateWidget.set_homogeneous(False)
-
-            self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
-            self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
-
-            self._derate.add(self._derateWidget)
-        elif self._derateType==acft.DERATE_EPR:
-            self._derateLabel.set_text("_EPR:")
-            self._derateLabel.set_use_underline(True)
-            self._derateLabel.set_sensitive(True)
-
-            self._derateWidget = gtk.Entry()
-            self._derateWidget.set_width_chars(7)
-            self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
-            self._derateWidget.set_alignment(1.0)
-            self._derateWidget.connect("changed", self._derateChanged)
-            self._derateLabel.set_mnemonic_widget(self._derateWidget)
-
-            self._derate.add(self._derateWidget)
-        elif self._derateType==acft.DERATE_TUPOLEV:
-            self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
-            self._derateLabel.set_use_underline(True)
-            self._derateLabel.set_sensitive(True)
-
-            if pygobject:
-                nominal = gtk.RadioButton.\
-                  new_with_label_from_widget(None,
-                                             xstr("takeoff_derate_tupolev_nominal"))
-            else:
-                nominal = gtk.RadioButton(None,
-                                          xstr("takeoff_derate_tupolev_nominal"))
-            nominal.set_use_underline(True)
-            nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
-            nominal.connect("toggled", self._derateChanged)
-
-            if pygobject:
-                takeoff = gtk.RadioButton.\
-                  new_with_label_from_widget(nominal,
-                                             xstr("takeoff_derate_tupolev_takeoff"))
-            else:
-                takeoff = gtk.RadioButton(nominal,
-                                          xstr("takeoff_derate_tupolev_takeoff"))
-
-            takeoff.set_use_underline(True)
-            takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
-            takeoff.connect("toggled", self._derateChanged)
-
-            self._derateButtons = [nominal, takeoff]
-
-            self._derateWidget = gtk.HBox()
-            self._derateWidget.pack_start(nominal, False, False, 4)
-            self._derateWidget.pack_start(takeoff, False, False, 4)
-
-            self._derate.add(self._derateWidget)
-        elif self._derateType==acft.DERATE_B462:
-            self._derateLabel.set_text("")
-
-            self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
-            self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
-            self._derateWidget.set_use_underline(True)
-            self._derate.add(self._derateWidget)
-        else:
-            self._derateWidget = None
-            self._derateLabel.set_text("")
-            self._derateLabel.set_sensitive(False)
-
-    def _metarChanged(self, entry):
-        """Called when the METAR has changed."""
-        print "TakeoffPage.metarChanged", self._updatingMETAR
-        if not self._updatingMETAR:
-            self._updateForwardButton()
-            self._wizard.metarChanged(entry.get_text(), self)
-
-    def _metarInserted(self, buffer, position, text, length):
-        """Called when new characters are inserted into the METAR.
-
-        It uppercases all characters."""
-        print "TakeoffPage.metarInserted", self._updatingMETAR
-        if not self._updatingMETAR:
-            self._updatingMETAR = True
-
-            buffer.delete_text(position, length)
-            buffer.insert_text(position, text.upper(), length)
-
-            self._updatingMETAR = False
-
-#-----------------------------------------------------------------------------
-
-class CruisePage(Page):
-    """The page containing the flight level that might change during flight."""
-    def __init__(self, wizard):
-        """Construct the page."""
-        super(CruisePage, self).__init__(wizard, "cruise",
-                                         xstr("cruise_title"),
-                                         xstr("cruise_help"))
-
-        self._loggable = False
-        self._loggedCruiseLevel = 240
-        self._activated = False
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
-                                  xscale = 0.0, yscale = 1.0)
-
-        mainBox = gtk.VBox()
-        alignment.add(mainBox)
-        self.setMainWidget(alignment)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 0.0, yscale = 0.0)
-        mainBox.pack_start(alignment, False, False, 16)
-
-        levelBox = gtk.HBox()
-
-        label = gtk.Label(xstr("route_level"))
-        label.set_use_underline(True)
-        levelBox.pack_start(label, True, True, 0)
-
-        self._cruiseLevel = gtk.SpinButton()
-        self._cruiseLevel.set_increments(step = 10, page = 100)
-        self._cruiseLevel.set_range(min = 50, max = 500)
-        self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
-        self._cruiseLevel.set_numeric(True)
-        self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
-        label.set_mnemonic_widget(self._cruiseLevel)
-
-        levelBox.pack_start(self._cruiseLevel, False, False, 8)
-
-        self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
-        self._updateButton.set_use_underline(True)
-        self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
-        self._updateButton.connect("clicked", self._updateButtonClicked)
-
-        levelBox.pack_start(self._updateButton, False, False, 16)
-
-        mainBox.pack_start(levelBox, False, False, 0)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 0.0, yscale = 1.0)
-        mainBox.pack_start(alignment, True, True, 0)
-
-        self.addCancelFlightButton()
-
-        self._backButton = self.addPreviousButton(clicked = self._backClicked)
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-    @property
-    def activated(self):
-        """Determine if the page is already activated or not."""
-        return self._activated
-
-    @property
-    def cruiseLevel(self):
-        """Get the cruise level."""
-        return self._loggedCruiseLevel
-
-    @property
-    def loggableCruiseLevel(self):
-        """Get the cruise level which should be logged."""
-        return self._cruiseLevel.get_value_as_int()
-
-    def setLoggable(self, loggable):
-        """Set whether the cruise altitude can be logged."""
-        self._loggable = loggable
-        self._updateButtons()
-
-    def activate(self):
-        """Setup the route from the booked flight."""
-        self._loggedCruiseLevel = self._wizard.filedCruiseLevel
-        self._cruiseLevel.set_value(self._loggedCruiseLevel)
-        self._activated = True
-
-    def reset(self):
-        """Reset the page."""
-        self._loggable = False
-        self._activated = False
-        super(CruisePage, self).reset()
-
-    def _updateButtons(self):
-        """Update the sensitivity of the buttons."""
-        self._updateButton.set_sensitive(self._loggable and
-                                         self.loggableCruiseLevel!=
-                                         self._loggedCruiseLevel)
-
-    def _cruiseLevelChanged(self, spinButton):
-        """Called when the cruise level has changed."""
-        self._updateButtons()
-
-    def _updateButtonClicked(self, button):
-        """Called when the update button is clicked."""
-        if self._wizard.cruiseLevelChanged():
-            self._loggedCruiseLevel = self.loggableCruiseLevel
-            self._updateButtons()
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        """Called when the Forward button is clicked."""
-        self._wizard.nextPage()
-
-#-----------------------------------------------------------------------------
-
-class LandingPage(Page):
-    """Page for entering landing data."""
-    def __init__(self, wizard):
-        """Construct the landing page."""
-        super(LandingPage, self).__init__(wizard, "landing",
-                                          xstr("landing_title"),
-                                          xstr("landing_help"),
-                                          completedHelp = xstr("landing_chelp"))
-
-        self._flightEnded = False
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(7, 24)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        row = 0
-
-        label = gtk.Label(xstr("landing_metar"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row+1)
-
-        self._metar = gtk.Entry()
-        self._metar.set_width_chars(40)
-        self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
-        self._metar.connect("changed", self._metarChanged)
-        self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
-        table.attach(self._metar, 2, 24, row, row+1)
-        label.set_mnemonic_widget(self._metar)
-
-        self._updatingMETAR = False
-
-        row += 1
-
-        label = gtk.Label(xstr("landing_star"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row + 1)
-
-        if pygobject:
-            self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
-        else:
-            self._star = gtk.ComboBoxEntry(comboModel)
-
-        self._star.set_entry_text_column(0)
-        self._star.get_child().set_width_chars(10)
-        self._star.set_tooltip_text(xstr("landing_star_tooltip"))
-        self._star.connect("changed", self._upperChangedComboBox)
-        self._star.set_sensitive(False)
-        table.attach(self._star, 2, 4, row, row + 1)
-        label.set_mnemonic_widget(self._star)
-
-        row += 1
-
-        label = gtk.Label(xstr("landing_transition"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row + 1)
-
-        if pygobject:
-            self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
-        else:
-            self._transition = gtk.ComboBoxEntry(comboModel)
-
-        self._transition.set_entry_text_column(0)
-        self._transition.get_child().set_width_chars(10)
-        self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
-        self._transition.connect("changed", self._upperChangedComboBox)
-        self._transition.set_sensitive(False)
-        table.attach(self._transition, 2, 4, row, row + 1)
-        label.set_mnemonic_widget(self._transition)
-
-        row += 1
-
-        label = gtk.Label(xstr("landing_runway"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row + 1)
-
-        self._runway = gtk.Entry()
-        self._runway.set_width_chars(10)
-        self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
-        self._runway.connect("changed", self._upperChanged)
-        table.attach(self._runway, 2, 4, row, row + 1)
-        label.set_mnemonic_widget(self._runway)
-
-        row += 1
-
-        label = gtk.Label(xstr("landing_approach"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row + 1)
-
-        self._approachType = gtk.Entry()
-        self._approachType.set_width_chars(10)
-        self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
-        self._approachType.connect("changed", self._upperChanged)
-        table.attach(self._approachType, 2, 4, row, row + 1)
-        label.set_mnemonic_widget(self._approachType)
-
-        row += 1
-
-        label = gtk.Label(xstr("landing_vref"))
-        label.set_use_markup(True)
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row + 1)
-
-        self._vref = IntegerEntry()
-        self._vref.set_width_chars(5)
-        self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
-        self._vref.connect("integer-changed", self._vrefChanged)
-        table.attach(self._vref, 3, 4, row, row + 1)
-        label.set_mnemonic_widget(self._vref)
-
-        self._vrefUnit = gtk.Label(xstr("label_knots"))
-        table.attach(self._vrefUnit, 4, 5, row, row + 1)
-
-        row += 1
-
-        self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
-        self._antiIceOn.set_use_underline(True)
-        self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
-        table.attach(self._antiIceOn, 3, 5, row, row + 1)
-
-        self.addCancelFlightButton()
-
-        self.addPreviousButton(clicked = self._backClicked)
-
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-        self._active = False
-
-    @property
-    def star(self):
-        """Get the STAR or None if none entered."""
-        text = self._star.get_child().get_text()
-        return text if self._star.get_active()!=0 and text and text!="N/A" \
-               else None
-
-    @property
-    def transition(self):
-        """Get the transition or None if none entered."""
-        text = self._transition.get_child().get_text()
-        return text if self._transition.get_active()!=0 and text and text!="N/A" \
-               else None
-
-    @property
-    def approachType(self):
-        """Get the approach type."""
-        return self._approachType.get_text()
-
-    @property
-    def runway(self):
-        """Get the runway."""
-        return self._runway.get_text()
-
-    @property
-    def vref(self):
-        """Return the landing reference speed."""
-        return self._vref.get_int()
-
-    @property
-    def antiIceOn(self):
-        """Get whether the anti-ice system has been turned on."""
-        return self._antiIceOn.get_active()
-
-    @antiIceOn.setter
-    def antiIceOn(self, value):
-        """Set the anti-ice indicator."""
-        self._antiIceOn.set_active(value)
-
-    def reset(self):
-        """Reset the page if the wizard is reset."""
-        super(LandingPage, self).reset()
-        self._vref.reset()
-        self._antiIceOn.set_active(False)
-        self._flightEnded = False
-        self._active = False
-
-    def activate(self):
-        """Called when the page is activated."""
-        self._updatingMETAR = True
-        self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
-        self._updatingMETAR = False
-
-        self._star.set_active(0)
-        self._star.set_sensitive(True)
-
-        self._transition.set_active(0)
-        self._transition.set_sensitive(True)
-
-        if self._wizard.landingRunway is None:
-            self._runway.set_text("")
-        else:
-            self._runway.set_text(self._wizard.landingRunway)
-        self._runway.set_sensitive(True)
-
-        self._approachType.set_text("")
-        self._approachType.set_sensitive(True)
-
-        self._vref.set_int(None)
-        self._vref.set_sensitive(True)
-
-        i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
-        speedUnit = xstr("label" + i18nSpeedUnit)
-        self._vrefUnit.set_text(speedUnit)
-
-        self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
-                                           i18nSpeedUnit))
-
-        self._updateForwardButton()
-
-        self._active = True
-
-    def flightEnded(self):
-        """Called when the flight has ended."""
-        super(LandingPage, self).flightEnded()
-        self._flightEnded = True
-        self._updateForwardButton()
-
-    def changeMETAR(self, metar):
-        """Change the METAR as a result of an edit on one of the other
-        pages."""
-        if self._active:
-            print "LandingPage.changeMETAR"
-            self._updatingMETAR = True
-            self._metar.get_buffer().set_text(metar, -1)
-            self._updatingMETAR = False
-
-            self._updateForwardButton()
-
-    def _updateForwardButton(self):
-        """Update the sensitivity of the forward button."""
-        sensitive = self._flightEnded and \
-                    self._metar.get_text()!="" and \
-                    (self.star is not None or
-                     self.transition is not None) and \
-                    self._runway.get_text()!="" and \
-                    self._approachType.get_text()!="" and \
-                    self.vref is not None
-        self._button.set_sensitive(sensitive)
-
-    def _upperChanged(self, entry):
-        """Called for entry widgets that must be converted to uppercase."""
-        entry.set_text(entry.get_text().upper())
-        self._updateForwardButton()
-
-    def _upperChangedComboBox(self, comboBox):
-        """Called for combo box widgets that must be converted to uppercase."""
-        if comboBox.get_active()==-1:
-            entry = comboBox.get_child()
-            entry.set_text(entry.get_text().upper())
-        self._updateForwardButton()
-
-    def _vrefChanged(self, widget, value):
-        """Called when the Vref has changed."""
-        self._updateForwardButton()
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _forwardClicked(self, button):
-        """Called when the forward button is clicked."""
-        wizard = self._wizard
-
-        aircraft = wizard.gui.flight.aircraft
-        aircraft.updateVRef()
-        aircraft.updateLandingAntiIce()
-        if wizard.gui.config.onlineGateSystem and \
-           wizard.loggedIn and not self._completed and \
-           wizard.bookedFlight.arrivalICAO=="LHBP" and \
-           not wizard.entranceExam:
-            wizard.getFleet(callback = self._fleetRetrieved, force = True)
-        elif wizard.entranceExam:
-            self._handleEntranceExamDone()
-        else:
-            wizard.nextPage()
-
-    def _fleetRetrieved(self, fleet):
-        """Callback for the fleet retrieval."""
-        self._wizard.nextPage()
-
-    def _metarChanged(self, entry):
-        """Called when the METAR has changed."""
-        print "LandingPage.metarChanged", self._updatingMETAR
-        if not self._updatingMETAR:
-            self._updateForwardButton()
-            self._wizard.metarChanged(entry.get_text(), self)
-
-    def _metarInserted(self, buffer, position, text, length):
-        """Called when new characters are inserted into the METAR.
-
-        It uppercases all characters."""
-        print "LandingPage.metarInserted", self._updatingMETAR
-        if not self._updatingMETAR:
-            self._updatingMETAR = True
-
-            buffer.delete_text(position, length)
-            buffer.insert_text(position, text.upper(), length)
-
-            self._updatingMETAR = False
-
-    def _handleEntranceExamDone(self):
-        """Handle the end of the entrance exam.
-
-        If the there was a NO-GO fault, notify the user that exam is a failure
-        and take them back to the student page. Otherwise congratulate, update
-        the database to reflect that the exam has been taken and go back to the
-        student page."""
-        self._wizard.jumpPage("chkfinish")
-
-#-----------------------------------------------------------------------------
-
-class PIREPSaveHelper(object):
-    """A helper to use for saving PIREPs."""
-    def __init__(self, wizard):
-        """Construct the helper."""
-        super(PIREPSaveHelper, self).__init__()
-
-        self._wizard = wizard
-
-        self._lastSavePath = None
-        self._savePIREPDialog = None
-
-    def addButton(self, page):
-        """Add a button to save the PIREP to the given page."""
-        return page.addButton(xstr("finish_save"), sensitive = False,
-                              clicked = self._saveClicked,
-                              tooltip = xstr("finish_save_tooltip"),
-                              clickedArg = page)
-
-    def autoSavePIREP(self, page):
-        """Perform the automatic saving of the PIREP."""
-        self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
-                                          self._getDefaultPIREPName())
-        self._lastSavePath = text2unicode(self._lastSavePath)
-        self._savePIREP(page, automatic = True)
-
-    def _getDefaultPIREPName(self):
-        """Get the default name of the PIREP."""
-        gui = self._wizard.gui
-
-        bookedFlight = gui.bookedFlight
-        tm = time.gmtime()
-
-        pilotID = self._wizard.pilotID
-        if pilotID: pilotID += " "
-        return "%s%s %02d%02d %s-%s.pirep" % \
-               (pilotID, str(bookedFlight.departureTime.date()),
-                tm.tm_hour, tm.tm_min,
-                bookedFlight.departureICAO, bookedFlight.arrivalICAO)
-
-    def _saveClicked(self, button, page):
-        """Called when the Save PIREP button is clicked."""
-        gui = self._wizard.gui
-
-        fileName = self._getDefaultPIREPName()
-
-        dialog = self._getSaveDialog()
-
-        if self._lastSavePath is None:
-            pirepDirectory = gui.config.pirepDirectory
-            if pirepDirectory is not None:
-                dialog.set_current_folder(pirepDirectory)
-        else:
-            dialog.set_current_folder(os.path.dirname(self._lastSavePath))
-
-        dialog.set_current_name(fileName)
-        result = dialog.run()
-        dialog.hide()
-
-        if result==RESPONSETYPE_OK:
-            self._lastSavePath = text2unicode(dialog.get_filename())
-            self._savePIREP(page)
-
-    def _savePIREP(self, page, automatic = False):
-        """Perform the saving of the PIREP."""
-
-        gui = self._wizard.gui
-
-        if automatic:
-            gui.beginBusy(xstr("finish_autosave_busy"))
-
-        pirep = PIREP(gui.flight)
-        error = pirep.save(self._lastSavePath)
-
-        if automatic:
-            gui.endBusy()
-
-        if error:
-            type = MESSAGETYPE_ERROR
-            message = xstr("finish_save_failed")
-            secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
-        else:
-            type = MESSAGETYPE_INFO
-            message = xstr("finish_save_done")
-            if automatic:
-                secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
-            else:
-                secondary = None
-            page.setPIREPSaved()
-
-        dialog = gtk.MessageDialog(parent = gui.mainWindow,
-                                   type = type, message_format = message)
-        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-        dialog.set_title(WINDOW_TITLE_BASE)
-        if secondary is not None:
-            dialog.format_secondary_markup(secondary)
-
-        dialog.run()
-        dialog.hide()
-
-    def _getSaveDialog(self):
-        """Get the PIREP saving dialog.
-
-        If it does not exist yet, create it."""
-        if self._savePIREPDialog is None:
-            gui = self._wizard.gui
-            dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
-                                           xstr("finish_save_title"),
-                                           action = FILE_CHOOSER_ACTION_SAVE,
-                                           buttons = (gtk.STOCK_CANCEL,
-                                                      RESPONSETYPE_CANCEL,
-                                                      gtk.STOCK_OK, RESPONSETYPE_OK),
-                                           parent = gui.mainWindow)
-            dialog.set_modal(True)
-            dialog.set_do_overwrite_confirmation(True)
-
-            filter = gtk.FileFilter()
-            filter.set_name(xstr("file_filter_pireps"))
-            filter.add_pattern("*.pirep")
-            dialog.add_filter(filter)
-
-            filter = gtk.FileFilter()
-            filter.set_name(xstr("file_filter_all"))
-            filter.add_pattern("*.*")
-            dialog.add_filter(filter)
-
-            self._savePIREPDialog = dialog
-
-        return self._savePIREPDialog
-
-#-----------------------------------------------------------------------------
-
-class FinishPage(Page):
-    """Flight finish page."""
-    def __init__(self, wizard, saveHelper):
-        """Construct the finish page."""
-        help = xstr("finish_help") + xstr("finish_help_goodtime")
-        super(FinishPage, self).__init__(wizard, "finish",
-                                         xstr("finish_title"), help)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(10, 2)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        row = 0
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_rating"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._flightRating = gtk.Label()
-        self._flightRating.set_width_chars(8)
-        self._flightRating.set_alignment(0.0, 0.5)
-        self._flightRating.set_use_markup(True)
-        labelAlignment.add(self._flightRating)
-        table.attach(labelAlignment, 1, 2, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_dep_time"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._depTime = gtk.Label()
-        self._depTime.set_width_chars(13)
-        self._depTime.set_alignment(0.0, 0.5)
-        self._depTime.set_use_markup(True)
-        self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
-        labelAlignment.add(self._depTime)
-        table.attach(labelAlignment, 1, 2, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_flight_time"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._flightTime = gtk.Label()
-        self._flightTime.set_width_chars(10)
-        self._flightTime.set_alignment(0.0, 0.5)
-        self._flightTime.set_use_markup(True)
-        labelAlignment.add(self._flightTime)
-        table.attach(labelAlignment, 1, 2, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_block_time"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._blockTime = gtk.Label()
-        self._blockTime.set_width_chars(10)
-        self._blockTime.set_alignment(0.0, 0.5)
-        self._blockTime.set_use_markup(True)
-        labelAlignment.add(self._blockTime)
-        table.attach(labelAlignment, 1, 2, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_arr_time"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._arrTime = gtk.Label()
-        self._arrTime.set_width_chars(13)
-        self._arrTime.set_alignment(0.0, 0.5)
-        self._arrTime.set_use_markup(True)
-        self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
-        labelAlignment.add(self._arrTime)
-        table.attach(labelAlignment, 1, 2, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_distance"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._distanceFlown = gtk.Label()
-        self._distanceFlown.set_width_chars(10)
-        self._distanceFlown.set_alignment(0.0, 0.5)
-        self._distanceFlown.set_use_markup(True)
-        labelAlignment.add(self._distanceFlown)
-        table.attach(labelAlignment, 1, 2, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_fuel"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        self._fuelUsed = gtk.Label()
-        self._fuelUsed.set_width_chars(10)
-        self._fuelUsed.set_alignment(0.0, 0.5)
-        self._fuelUsed.set_use_markup(True)
-        labelAlignment.add(self._fuelUsed)
-        table.attach(labelAlignment, 1, 2, row, row+1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
-                                       yalign = 0.5, yscale = 0.0)
-        label = gtk.Label(xstr("finish_type"))
-        label.set_use_underline(True)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        self._flightType = createFlightTypeComboBox()
-        self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
-        self._flightType.set_active(0)
-        self._flightType.connect("changed", self._flightTypeChanged)
-        flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        flightTypeAlignment.add(self._flightType)
-        table.attach(flightTypeAlignment, 1, 2, row, row+1)
-        label.set_mnemonic_widget(self._flightType)
-
-        row += 1
-
-        self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
-        self._onlineFlight.set_use_underline(True)
-        self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
-        onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
-        onlineFlightAlignment.add(self._onlineFlight)
-        table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
-
-        row += 1
-
-        labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
-                                       yalign = 0.5, yscale = 0.0)
-        self._gateLabel = gtk.Label(xstr("finish_gate"))
-        self._gateLabel.set_use_underline(True)
-        labelAlignment.add(self._gateLabel)
-        table.attach(labelAlignment, 0, 1, row, row+1)
-
-        self._gatesModel = gtk.ListStore(str)
-
-        self._gate = gtk.ComboBox(model = self._gatesModel)
-        renderer = gtk.CellRendererText()
-        self._gate.pack_start(renderer, True)
-        self._gate.add_attribute(renderer, "text", 0)
-        self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
-        self._gate.connect("changed", self._gateChanged)
-        gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
-        gateAlignment.add(self._gate)
-        table.attach(gateAlignment, 1, 2, row, row+1)
-        self._gateLabel.set_mnemonic_widget(self._gate)
-
-        self.addButton(xstr("finish_newFlight"),
-                       sensitive = True,
-                       clicked = self._newFlightClicked,
-                       tooltip = xstr("finish_newFlight_tooltip"),
-                       padding = 16)
-
-        self.addPreviousButton(clicked = self._backClicked)
-
-        self._saveHelper = saveHelper
-        self._saveButton = saveHelper.addButton(self)
-
-        self._tooBigTimeDifference = False
-        self._deferredAutoSave = False
-        self._pirepSaved = False
-        self._pirepSent = False
-
-        self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
-                                          sensitive = False,
-                                          clicked = self._sendClicked,
-                                          tooltip = xstr("sendPIREP_tooltip"))
-
-        # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
-        # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
-        # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
-        # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
-        # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
-        # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
-        # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
-        # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
-
-    @property
-    def flightType(self):
-        """Get the flight type."""
-        index = self._flightType.get_active()
-        return None if index<0 else self._flightType.get_model()[index][1]
-
-    @property
-    def online(self):
-        """Get whether the flight was an online flight or not."""
-        return self._onlineFlight.get_active()
-
-    def activate(self):
-        """Activate the page."""
-        self._deferredAutoSave = False
-        self._pirepSaved = False
-        self._pirepSent = False
-
-        flight = self._wizard.gui._flight
-        rating = flight.logger.getRating()
-        if rating<0:
-            self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
-        else:
-            self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
-
-        flightLength = flight.flightTimeEnd - flight.flightTimeStart
-        self._flightTime.set_markup("<b>%s</b>" % \
-                                    (util.getTimeIntervalString(flightLength),))
-
-        blockLength = flight.blockTimeEnd - flight.blockTimeStart
-        self._blockTime.set_markup("<b>%s</b>" % \
-                                   (util.getTimeIntervalString(blockLength),))
-
-        self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
-                                       (flight.flownDistance,))
-
-        self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
-                                  (flight.startFuel - flight.endFuel,))
-
-        self._flightType.set_active(-1)
-        self._onlineFlight.set_active(self._wizard.loggedIn)
-
-        self._gatesModel.clear()
-        if self._wizard.gui.config.onlineGateSystem and \
-           self._wizard.loggedIn and \
-           self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
-           not self._wizard.entranceExam:
-            occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
-            for gate in lhbpGates.gates:
-                if gate.isAvailable(lhbpGates, occupiedGateNumbers):
-                    self._gatesModel.append([gate.number])
-            self._gateLabel.set_sensitive(True)
-            self._gate.set_sensitive(True)
-            self._gate.set_active(-1)
-        else:
-            self._gateLabel.set_sensitive(False)
-            self._gate.set_sensitive(False)
-
-        self._updateTimes()
-
-    def updateButtons(self):
-        """Update the sensitivity state of the buttons."""
-        gui = self._wizard.gui
-        faultsExplained = gui.faultsFullyExplained
-        timesCorrect = self.flightType is None or \
-                       not self._tooBigTimeDifference or \
-                       gui.hasComments or gui.hasDelayCode
-        sensitive = gui.flight is not None and \
-                    gui.flight.stage==const.STAGE_END and \
-                    self._flightType.get_active()>=0 and \
-                    (self._gatesModel.get_iter_first() is None or
-                     self._gate.get_active()>=0) and \
-                     faultsExplained and timesCorrect
-
-        self._updateHelp(faultsExplained, timesCorrect)
-
-        wasSensitive = self._saveButton.get_sensitive()
-
-        if gui.config.pirepAutoSave and sensitive and not wasSensitive:
-            if gui.isWizardActive():
-                self._saveHelper.autoSavePIREP(self)
-            else:
-                self._deferredAutoSave = True
-
-        if not sensitive:
-            self._deferredAutoSave = False
-
-        self._saveButton.set_sensitive(sensitive)
-        self._sendButton.set_sensitive(sensitive and
-                                       self._wizard.bookedFlight.id is not None)
-
-    def grabDefault(self):
-        """If the page has a default button, make it the default one."""
-        super(FinishPage, self).grabDefault()
-        if self._deferredAutoSave:
-            self._saveHelper.autoSavePIREP(self)
-            self._deferredAutoSave = False
-
-    def setPIREPSaved(self):
-        """Mark the PIREP as saved."""
-        self._pirepSaved = True
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _flightTypeChanged(self, comboBox):
-        """Called when the flight type has changed."""
-        self._updateTimes()
-
-    def _gateChanged(self, comboBox):
-        """Called when the arrival gate has changed."""
-        self.updateButtons()
-
-    def _newFlightClicked(self, button):
-        """Called when the new flight button is clicked."""
-        gui = self._wizard.gui
-        if not self._pirepSent and not self._pirepSaved:
-            dialog = gtk.MessageDialog(parent = gui.mainWindow,
-                                       type = MESSAGETYPE_QUESTION,
-                                       message_format = xstr("finish_newFlight_question"))
-
-            dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
-            dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
-
-            dialog.set_title(WINDOW_TITLE_BASE)
-            result = dialog.run()
-            dialog.hide()
-            if result!=RESPONSETYPE_YES:
-                return
-
-        gui.reset()
-
-    def _sendClicked(self, button):
-        """Called when the Send button is clicked."""
-        pirep = PIREP(self._wizard.gui.flight)
-        self._wizard.gui.sendPIREP(pirep,
-                                   callback = self._handlePIREPSent)
-
-    def _handlePIREPSent(self, returned, result):
-        """Callback for the PIREP sending result."""
-        self._pirepSent = returned and result.success
-        if self._wizard.gui.config.onlineGateSystem and \
-           self._wizard.loggedIn and not self._wizard.entranceExam and \
-           returned and result.success:
-            bookedFlight = self._wizard.bookedFlight
-            if bookedFlight.arrivalICAO=="LHBP":
-                iter = self._gate.get_active_iter()
-                gateNumber = None if iter is None \
-                             else self._gatesModel.get_value(iter, 0)
-
-                status = const.PLANE_PARKING if gateNumber is None \
-                         else const.PLANE_HOME
-            else:
-                gateNumber = None
-                status = const.PLANE_AWAY
-
-            self._wizard.updatePlane(self._planeUpdated,
-                                     bookedFlight.tailNumber,
-                                     status, gateNumber = gateNumber)
-
-    def _planeUpdated(self, success):
-        """Callback for the plane updating."""
-        pass
-
-    def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
-        """Format the departure or arrival time based on the given data as a
-        markup for a label."""
-        realTime = time.gmtime(realTimestamp)
-
-        if warning:
-            colour = "red" if error else "orange"
-            markupBegin = '<span foreground="%s">' % (colour,)
-            markupEnd = '</span>'
-        else:
-            markupBegin = markupEnd = ""
-
-        markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
-                 (markupBegin,
-                  realTime.tm_hour, realTime.tm_min,
-                  scheduledTime.hour, scheduledTime.minute,
-                  markupEnd)
-
-        return markup
-
-    def _updateTimes(self):
-        """Format the flight times and the help text according to the flight
-        type.
-
-        The buttons are also updated.
-        """
-        flight = self._wizard.gui._flight
-        bookedFlight = flight.bookedFlight
-
-        (departureWarning, departureError) = flight.blockTimeStartWrong
-        (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
-
-        if self.flightType==const.FLIGHTTYPE_VIP:
-            departureError = arrivalError = False
-
-        self._tooBigTimeDifference = departureError or arrivalError
-
-        self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
-                                                  flight.blockTimeStart,
-                                                  (departureWarning,
-                                                   departureError)))
-
-        self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
-                                                  flight.blockTimeEnd,
-                                                  (arrivalWarning,
-                                                   arrivalError)))
-
-        self.updateButtons()
-
-    def _updateHelp(self, faultsExplained, timesCorrect):
-        """Update the help text according to the actual situation."""
-        if not faultsExplained:
-            self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
-        elif not timesCorrect:
-            self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
-        else:
-            self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
-
-
-#-----------------------------------------------------------------------------
-
-class CheckFlightFinishPage(Page):
-    """Finish page for a check flight."""
-    def __init__(self, wizard, saveHelper):
-        """Construct the check flight finish page."""
-        super(CheckFlightFinishPage, self).__init__(wizard,
-                                                    "chkfinish",
-                                                    xstr("chkfinish_title"),
-                                                    "")
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 1.0, yscale = 1.0)
-        self._label = gtk.Label()
-        alignment.add(self._label)
-
-        self.setMainWidget(alignment)
-
-        self._saveHelper = saveHelper
-        self._saveButton = saveHelper.addButton(self)
-
-        self._button = self.addNextButton(sensitive = False,
-                                          clicked =  self._forwardClicked)
-
-    def activate(self):
-        """Activate the page."""
-        wizard = self._wizard
-        loginResult = wizard.loginResult
-        gui = wizard.gui
-        rating = gui.flight.logger.getRating()
-
-        if rating>=0:
-            loginResult.checkFlightStatus = True
-
-        firstOfficer = \
-          loginResult.entryExamPassed and loginResult.checkFlightStatus
-
-        if firstOfficer:
-            loginResult.rank = "FO"
-
-        if rating<0:
-            mainMessage = xstr("chkfinish_failed")
-        else:
-            mainMessage = xstr("chkfinish_passed_begin")
-            if firstOfficer:
-                mainMessage += xstr("chkfinish_passed_fo")
-            mainMessage += xstr("chkfinish_passed_end")
-
-        if firstOfficer:
-            nextMessage = xstr("chkfinish_next")
-        else:
-            nextMessage = xstr("chkfinish_next_student_begin")
-            if not loginResult.entryExamPassed and \
-               not loginResult.checkFlightStatus:
-                nextMessage += xstr("chkfinish_next_student_nothing")
-            elif loginResult.entryExamPassed and \
-                 not loginResult.checkFlightStatus:
-                nextMessage += xstr("chkfinish_next_student_no_flight")
-            elif not loginResult.entryExamPassed and \
-                 loginResult.checkFlightStatus:
-                nextMessage += xstr("chkfinish_next_student_no_exam")
-
-        self._label.set_text(mainMessage +
-                             xstr("chkfinish_savepirep") +
-                             nextMessage)
-        self._label.set_use_markup(True)
-        self._label.set_alignment(0.5, 0.0)
-
-        self._saveButton.set_sensitive(True)
-        self._button.set_sensitive(True)
-
-    def _forwardClicked(self, button):
-        """Jump to the student page if there are some tasks to do,
-        or to the flight selection page, if the pilot is allowed to perform
-        MAVA flights."""
-        wizard = self._wizard
-        gui = wizard.gui
-
-        loginResult = wizard.loginResult
-        if loginResult.checkFlightStatus:
-            gui.beginBusy(xstr("chkfinish_updateweb_busy"))
-            gui.webHandler.setCheckFlightPassed(self._checkFlightPassedSetCallback,
-                                                wizard.checkFlightAircraftType)
-        else:
-            self._resetGUI()
-
-    def _checkFlightPassedSetCallback(self, returned, result):
-        """Called when the check flight status has been set."""
-        gobject.idle_add(self._checkFlightPassedSet, returned, result)
-
-    def _checkFlightPassedSet(self, returned, result):
-        """Handle the result of an attempt to set the check flight status."""
-        gui = self._wizard.gui
-
-        gui.endBusy()
-
-        if returned:
-            self._resetGUI()
-        else:
-            dialog = gtk.MessageDialog(parent = gui.mainWindow,
-                                       type = MESSAGETYPE_ERROR,
-                                       message_format =
-                                       xstr("chkfinish_passedset_failed"))
-            dialog.set_title(WINDOW_TITLE_BASE + " - " +
-                             xstr("chkfinish_passedset_failed_title"))
-            dialog.format_secondary_markup(xstr("chkfinish_passedset_failed_secondary"))
-
-            dialog.add_button(xstr("button_ok"), 0)
-
-            dialog.run()
-            dialog.hide()
-
-    def _resetGUI(self):
-        """Reset the GUI."""
-        gui = self._wizard.gui
-        gui.reset()
-
-#-----------------------------------------------------------------------------
-
-class Wizard(gtk.VBox):
-    """The flight wizard."""
-    def __init__(self, gui):
-        """Construct the wizard."""
-        super(Wizard, self).__init__()
-
-        self.gui = gui
-
-        self._pages = []
-        self._currentPage = None
-
-        self._loginPage = LoginPage(self)
-        self._pages.append(self._loginPage)
-        self._flightSelectionPage = FlightSelectionPage(self)
-        self._pages.append(self._flightSelectionPage)
-        self._pages.append(GateSelectionPage(self))
-        self._pages.append(RegisterPage(self))
-        self._studentPage = StudentPage(self)
-        self._pages.append(self._studentPage)
-        self._pages.append(ConnectPage(self))
-        self._payloadPage = PayloadPage(self)
-        self._pages.append(self._payloadPage)
-        self._payloadIndex = len(self._pages)
-        self._pages.append(TimePage(self))
-        self._routePage = RoutePage(self)
-        self._pages.append(self._routePage)
-        self._simBriefSetupPage = SimBriefSetupPage(self)
-        self._pages.append(self._simBriefSetupPage)
-        self._simBriefingPage = SimBriefingPage(self)
-        self._pages.append(self._simBriefingPage)
-        self._pages.append(FuelPage(self))
-        self._departureBriefingPage = BriefingPage(self, True)
-        self._pages.append(self._departureBriefingPage)
-        self._arrivalBriefingPage = BriefingPage(self, False)
-        self._pages.append(self._arrivalBriefingPage)
-        self._arrivalBriefingIndex = len(self._pages)
-        self._takeoffPage = TakeoffPage(self)
-        self._pages.append(self._takeoffPage)
-        self._cruisePage = CruisePage(self)
-        self._pages.append(self._cruisePage)
-        self._landingPage = LandingPage(self)
-        self._pages.append(self._landingPage)
-
-        pirepSaveHelper = PIREPSaveHelper(self)
-
-        self._finishPage = FinishPage(self, pirepSaveHelper)
-        self._pages.append(self._finishPage)
-        self._pages.append(CheckFlightFinishPage(self, pirepSaveHelper))
-
-        self._requestedWidth = None
-        self._requestedHeight = None
-
-        self.connect("size-allocate", self._sizeAllocate)
-
-        for page in self._pages:
-            page.show_all()
-            page.setStyle()
-
-        self._initialize()
-
-    def _sizeAllocate(self, widget, allocation):
-        if self._requestedWidth is not None and \
-           self._requestedHeight is not None:
-           return
-
-        if self._currentPage is not None:
-            self.remove(self._pages[self._currentPage])
-
-        maxWidth = 0
-        maxHeight = 0
-        for page in self._pages:
-            self.add(page)
-            self.show_all()
-            pageSizeRequest = page.size_request()
-            width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
-            height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
-            maxWidth = max(maxWidth, width)
-            maxHeight = max(maxHeight, height)
-            self.remove(page)
-
-        if self._currentPage is not None:
-            self.add(self._pages[self._currentPage])
-
-        self._requestedWidth = maxWidth
-        self._requestedHeight = maxHeight
-        self.set_size_request(maxWidth, maxHeight)
-
-    @property
-    def pilotID(self):
-        """Get the pilot ID, if given."""
-        return self._loginPage.pilotID
-
-    @property
-    def entranceExam(self):
-        """Get whether an entrance exam is about to be taken."""
-        return self._loginResult is not None and self._loginResult.rank=="STU"
-
-    @property
-    def loggedIn(self):
-        """Indicate if there was a successful login."""
-        return self._loginResult is not None
-
-    @property
-    def loginResult(self):
-        """Get the login result."""
-        return self._loginResult
-
-    @property
-    def checkFlightAircraftType(self):
-        """Get the type of the aircraft used to perform the check flight."""
-        return self._studentPage.aircraftType
-
-    def setCurrentPage(self, index, finalize = False, fromPageShift = None):
-        """Set the current page to the one with the given index.
-
-        @param fromPageShift if given, the relative index of one of the
-        previous pages that should be used as the from-page of the next
-        page. E.g. if fromPageShift is 1, the previous page will be the
-        from-page."""
-        assert index < len(self._pages)
-
-        fromPage = self._currentPage
-        if fromPage is not None:
-            page = self._pages[fromPage]
-            if finalize and not page._completed:
-                page.complete()
-            self.remove(page)
-            if fromPageShift is not None:
-                fromPage -= fromPageShift
-
-        self._currentPage = index
-        page = self._pages[index]
-        self.add(page)
-        if page._fromPage is None:
-            page._fromPage = fromPage
-            page.initialize()
-        self.show_all()
-        if fromPage is not None:
-            self.grabDefault()
-
-    @property
-    def bookedFlight(self):
-        """Get the booked flight selected."""
-        return self._bookedFlight
-
-    @property
-    def numCrew(self):
-        """Get the number of crew members."""
-        return self._payloadPage.numCrew
-
-    @property
-    def numPassengers(self):
-        """Get the number of passengers."""
-        return self._payloadPage.numPassengers
-
-    @property
-    def bagWeight(self):
-        """Get the baggage weight."""
-        return self._payloadPage.bagWeight
-
-    @property
-    def cargoWeight(self):
-        """Get the cargo weight."""
-        return self._payloadPage.cargoWeight
-
-    @property
-    def mailWeight(self):
-        """Get the mail weight."""
-        return self._payloadPage.mailWeight
-
-    @property
-    def zfw(self):
-        """Get the calculated ZFW value."""
-        return 0 if self._bookedFlight is None \
-               else self._payloadPage.calculateZFW()
-
-    @property
-    def filedCruiseLevel(self):
-        """Get the filed cruise level."""
-        return self._routePage.filedCruiseLevel
-
-    @property
-    def filedCruiseAltitude(self):
-        """Get the filed cruise altitude."""
-        return self._routePage.filedCruiseLevel * 100
-
-    @property
-    def cruiseAltitude(self):
-        """Get the cruise altitude."""
-        level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
-                else self._routePage.filedCruiseLevel
-        return level * 100
-
-    @property
-    def loggableCruiseAltitude(self):
-        """Get the cruise altitude that can be logged."""
-        if self._cruisePage.activated:
-            return self._cruisePage.loggableCruiseLevel * 100
-        else:
-            return 0
-
-    @property
-    def route(self):
-        """Get the route."""
-        return self._routePage.route
-
-    @property
-    def alternate(self):
-        """Get the ICAO code of the alternate airport."""
-        return self._routePage.alternate
-
-    @property
-    def departureMETAR(self):
-        """Get the METAR of the departure airport."""
-        return self._departureBriefingPage.metar
-
-    @property
-    def arrivalMETAR(self):
-        """Get the METAR of the arrival airport."""
-        return self._arrivalBriefingPage.metar
-
-    @property
-    def departureRunway(self):
-        """Get the departure runway."""
-        return self._takeoffPage.runway
-
-    @property
-    def sid(self):
-        """Get the SID."""
-        return self._takeoffPage.sid
-
-    @property
-    def v1(self):
-        """Get the V1 speed."""
-        return self._takeoffPage.v1
-
-    @property
-    def vr(self):
-        """Get the Vr speed."""
-        return self._takeoffPage.vr
-
-    @property
-    def v2(self):
-        """Get the V2 speed."""
-        return self._takeoffPage.v2
-
-    @property
-    def derate(self):
-        """Get the derate value."""
-        return self._takeoffPage.derate
-
-    @property
-    def takeoffAntiIceOn(self):
-        """Get whether the anti-ice system was on during take-off."""
-        return self._takeoffPage.antiIceOn
-
-    @takeoffAntiIceOn.setter
-    def takeoffAntiIceOn(self, value):
-        """Set anti-ice on indicator."""
-        self._takeoffPage.antiIceOn = value
-
-    @property
-    def rtoIndicated(self):
-        """Get whether the pilot has indicated that an RTO has occured."""
-        return self._takeoffPage.rtoIndicated
-
-    @property
-    def arrivalRunway(self):
-        """Get the arrival runway."""
-        return self._landingPage.runway
-
-    @property
-    def star(self):
-        """Get the STAR."""
-        return self._landingPage.star
-
-    @property
-    def transition(self):
-        """Get the transition."""
-        return self._landingPage.transition
-
-    @property
-    def approachType(self):
-        """Get the approach type."""
-        return self._landingPage.approachType
-
-    @property
-    def vref(self):
-        """Get the Vref speed."""
-        return self._landingPage.vref
-
-    @property
-    def landingAntiIceOn(self):
-        """Get whether the anti-ice system was on during landing."""
-        return self._landingPage.antiIceOn
-
-    @landingAntiIceOn.setter
-    def landingAntiIceOn(self, value):
-        """Set anti-ice on indicator."""
-        self._landingPage.antiIceOn = value
-
-    @property
-    def flightType(self):
-        """Get the flight type."""
-        return self._finishPage.flightType
-
-    @property
-    def online(self):
-        """Get whether the flight was online or not."""
-        return self._finishPage.online
-
-    @property
-    def usingSimBrief(self):
-        """Indicate if we are using a SimBrief briefing or not."""
-        return self._usingSimBrief
-
-    @usingSimBrief.setter
-    def usingSimBrief(self, x):
-        """Set whether we are using a SimBrief briefing or not."""
-        self._usingSimBrief = x
-
-    def nextPage(self, finalize = True):
-        """Go to the next page."""
-        nextPageID = self._pages[self._currentPage].nextPageID
-        self.jumpPage(1 if nextPageID is None else nextPageID, finalize)
-
-    def jumpPage(self, countOrID, finalize = True, fromPageShift = None):
-        """Go to the page which is 'count' pages after the current one."""
-        if isinstance(countOrID, str):
-            targetIndex = self._getIndexOf(countOrID)
-        else:
-            targetIndex = self._currentPage + countOrID
-        self.setCurrentPage(targetIndex,
-                            finalize = finalize, fromPageShift = fromPageShift)
-
-    def grabDefault(self):
-        """Make the default button of the current page the default."""
-        self._pages[self._currentPage].grabDefault()
-
-    def connected(self, fsType, descriptor):
-        """Called when the connection could be made to the simulator."""
-        self.nextPage()
-
-    def reset(self, loginResult):
-        """Resets the wizard to go back to the login page."""
-        self._initialize(keepLoginResult = loginResult is None,
-                         loginResult = loginResult)
-
-    def setStage(self, stage):
-        """Set the flight stage to the given one."""
-        if stage!=const.STAGE_END:
-            self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
-
-        if stage==const.STAGE_TAKEOFF:
-            self._takeoffPage.allowForward()
-        elif stage==const.STAGE_LANDING:
-            if not self._arrivalBriefingPage.metarEdited:
-                print "Downloading arrival METAR again"
-                self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
-                                              [self._bookedFlight.arrivalICAO])
-
-        elif stage==const.STAGE_END:
-            for page in self._pages:
-                page.flightEnded()
-
-    def _initialize(self, keepLoginResult = False, loginResult = None):
-        """Initialize the wizard."""
-        if not keepLoginResult:
-            self._loginResult = loginResult
-
-        self._loginCallback = None
-
-        self._fleet = None
-        self._fleetCallback = None
-
-        self._bookedFlight = None
-        self._departureGate = "-"
-        self._fuelData = None
-        self._departureNOTAMs = None
-        self._departureMETAR = None
-        self._arrivalNOTAMs = None
-        self._arrivalMETAR = None
-        self._usingSimBrief = None
-        self.takeoffRunway = None
-        self.landingRunway = None
-
-        firstPage = 0 if self._loginResult is None else 1
-        for page in self._pages[firstPage:]:
-            page.reset()
-
-        self.setCurrentPage(firstPage)
-        #self.setCurrentPage(10)
-
-    def login(self, callback, pilotID, password):
-        """Called when the login button was clicked."""
-        self._loginCallback = callback
-        if pilotID is None:
-            loginResult = self._loginResult
-            assert loginResult is not None and loginResult.loggedIn
-            pilotID = loginResult.pilotID
-            password = loginResult.password
-            busyMessage = xstr("reload_busy")
-        else:
-            self._loginResult = None
-            busyMessage = xstr("login_busy")
-
-        self.gui.beginBusy(busyMessage)
-
-        self.gui.webHandler.login(self._loginResultCallback,
-                                  pilotID, password)
-
-    def reloadFlights(self, callback):
-        """Reload the flights from the MAVA server."""
-        self.login(callback, None, None)
-
-    def addFlight(self, bookedFlight):
-        """Add the given booked flight to the flight selection page."""
-        self._flightSelectionPage.addFlight(bookedFlight)
-
-    def reflyFlight(self, bookedFlight):
-        """Add the given booked flight to the flight selection page."""
-        self._removePendingFlight(bookedFlight)
-        self._flightSelectionPage._reflyFlight(bookedFlight)
-
-    def deleteFlight(self, bookedFlight):
-        """Remove the given flight from the pending flight list."""
-        self._removePendingFlight(bookedFlight)
-        self._flightSelectionPage._updatePending()
-
-    def cancelFlight(self, reloadCallback):
-        """Cancel the flight.
-
-        If it is an entry exam flight, we go back to the student page.
-        Otherwise we reload the flights."""
-        if self.entranceExam:
-            self.reset(None)
-            self.jumpPage("student")
-        else:
-            self.reloadFlights(reloadCallback)
-
-    def cruiseLevelChanged(self):
-        """Called when the cruise level is changed."""
-        return self.gui.cruiseLevelChanged()
-
-    def metarChanged(self, metar, originator):
-        """Called when a METER is changed on on of the pages.
-
-        originator is the page that originated the changed. It will be used to
-        determine which METAR (departure or arrival) has changed."""
-        metar = metar.upper()
-        if originator in [self._departureBriefingPage, self._takeoffPage]:
-            self.departureMETARChanged(metar, originator)
-        else:
-            self.arrivalMETARChanged(metar, originator)
-
-    def departureMETARChanged(self, metar, originator):
-        """Called when the departure METAR has been edited on one of the
-        pages.
-
-        originator is the page that originated the change. It will not be
-        called to set the METAR, while others will be."""
-        for page in [self._departureBriefingPage, self._takeoffPage]:
-            if page is not originator:
-                page.changeMETAR(metar)
-
-    def arrivalMETARChanged(self, metar, originator):
-        """Called when the arrival METAR has been edited on one of the
-        pages.
-
-        originator is the page that originated the change. It will not be
-        called to set the METAR, while others will be."""
-        for page in [self._arrivalBriefingPage, self._landingPage]:
-            if page is not originator:
-                page.changeMETAR(metar)
-
-    def _removePendingFlight(self, flight):
-        """Remove the given pending flight from the login result."""
-        for flights in [self._loginResult.reportedFlights,
-                        self._loginResult.rejectedFlights]:
-            for f in flights:
-                if f.id==flight.id:
-                    flights.remove(f)
-                    return
-
-    def _loginResultCallback(self, returned, result):
-        """The login result callback, called in the web handler's thread."""
-        gobject.idle_add(self._handleLoginResult, returned, result)
-
-    def _handleLoginResult(self, returned, result):
-        """Handle the login result."""
-        self.gui.endBusy()
-        isReload = self._loginResult is not None
-        if returned:
-            if result.loggedIn:
-                self._loginResult = result
-                self.gui.loginSuccessful()
-            else:
-                if isReload:
-                    message = xstr("reload_failed")
-                else:
-                    message = xstr("login_entranceExam_invalid"
-                                   if self.entranceExam else
-                                   xstr("login_invalid"))
-                dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
-                                           type = MESSAGETYPE_ERROR,
-                                           message_format = message)
-                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-                dialog.set_title(WINDOW_TITLE_BASE)
-                if isReload:
-                    secondary = xstr("reload_failed_sec")
-                else:
-                    secondary = xstr("login_entranceExam_invalid_sec"
-                                     if self.entranceExam else
-                                     xstr("login_invalid_sec"))
-                dialog.format_secondary_markup(secondary)
-                dialog.run()
-                dialog.hide()
-        else:
-            message = xstr("reload_failconn") if isReload \
-                      else xstr("login_failconn")
-            dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
-                                       type = MESSAGETYPE_ERROR,
-                                       message_format = message)
-            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-            dialog.set_title(WINDOW_TITLE_BASE)
-            secondary = xstr("reload_failconn_sec") if isReload \
-                        else xstr("login_failconn_sec")
-            dialog.format_secondary_markup(secondary)
-
-            dialog.run()
-            dialog.hide()
-
-        callback = self._loginCallback
-        self._loginCallback = None
-        callback(returned, result)
-
-    def getFleet(self, callback, force = False):
-        """Get the fleet via the GUI and call the given callback."""
-        self._fleetCallback = callback
-        self.gui.getFleet(callback = self._fleetRetrieved, force = force)
-
-    def _fleetRetrieved(self, fleet):
-        """Callback for the fleet retrieval."""
-        self._fleet = fleet
-        if self._fleetCallback is not None:
-            self._fleetCallback(fleet)
-        self._fleetCallback = None
-
-    def updatePlane(self, callback, tailNumber, status, gateNumber = None):
-        """Update the given plane's gate information."""
-        self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
-                             callback = callback)
-
-    def updateRTO(self):
-        """Update the RTO state.
-
-        The RTO checkbox will be enabled if the flight has an RTO state and the
-        comments field contains some text."""
-        flight = self.gui.flight
-        rtoEnabled = flight is not None and flight.hasRTO and \
-                     self.gui.hasComments
-        self._takeoffPage.setRTOEnabled(rtoEnabled)
-
-    def commentsChanged(self):
-        """Called when the comments have changed."""
-        self.updateRTO()
-        self._finishPage.updateButtons()
-
-    def delayCodesChanged(self):
-        """Called when the delay codes have changed."""
-        self._finishPage.updateButtons()
-
-    def faultExplanationsChanged(self):
-        """Called when the faults and their explanations have changed."""
-        self._finishPage.updateButtons()
-
-    def rtoToggled(self, indicated):
-        """Called when the RTO indication has changed."""
-        self.gui.rtoToggled(indicated)
-
-    def _connectSimulator(self, simulatorType):
-        """Connect to the simulator."""
-        self.gui.connectSimulator(self._bookedFlight, simulatorType)
-
-    def _arrivalMETARCallback(self, returned, result):
-        """Called when the METAR of the arrival airport is retrieved."""
-        gobject.idle_add(self._handleArrivalMETAR, returned, result)
-
-    def _handleArrivalMETAR(self, returned, result):
-        """Called when the METAR of the arrival airport is retrieved."""
-        icao = self._bookedFlight.arrivalICAO
-        if returned and icao in result.metars:
-            metar = result.metars[icao]
-            if metar!="":
-                self._arrivalBriefingPage.setMETAR(metar)
-
-    def _getIndexOf(self, pageID):
-        """Get the index for the given page ID.
-
-        It is an assertion failure if the ID is not found."""
-        for index in range(0, len(self._pages)):
-            page = self._pages[index]
-            if page.id==pageID:
-                return index
-        assert False
-
-#-----------------------------------------------------------------------------
Index: src/mlx/gui/flightlist.py
===================================================================
--- src/mlx/gui/flightlist.py	(revision 859)
+++ src/mlx/gui/flightlist.py	(revision 859)
@@ -0,0 +1,698 @@
+# A widget which is a generic list of flights
+
+#-----------------------------------------------------------------------------
+
+from mlx.gui.common import *
+
+import mlx.const as const
+
+#-----------------------------------------------------------------------------
+
+class ColumnDescriptor(object):
+    """A descriptor for a column in the list."""
+    def __init__(self, attribute, heading, type = str,
+                 convertFn = None, renderer = None,
+                 extraColumnAttributes = None, sortable = False,
+                 defaultSortable = False, defaultDescending = False,
+                 cellDataFn = None):
+        """Construct the descriptor."""
+        self._attribute = attribute
+        self._heading = heading
+        self._type = type
+        self._convertFn = convertFn
+        self._renderer = \
+          gtk.CellRendererText() if renderer is None else renderer
+        self._extraColumnAttributes = extraColumnAttributes
+        self._sortable = sortable
+        self._defaultSortable = defaultSortable
+        self._defaultDescending = defaultDescending
+        self._cellDataFn = cellDataFn
+
+    @property
+    def attribute(self):
+        """Get the attribute the column belongs to."""
+        return self._attribute
+
+    @property
+    def defaultSortable(self):
+        """Determine if this column is the default sortable one."""
+        return self._defaultSortable
+
+    def appendType(self, types):
+        """Append the type of this column to the given list of types."""
+        types.append(self._type)
+
+    def getViewColumn(self, index):
+        """Get a new column object for a tree view.
+
+        @param index is the 0-based index of the column."""
+        if isinstance(self._renderer, gtk.CellRendererText):
+            column = gtk.TreeViewColumn(self._heading, self._renderer,
+                                        text = index)
+        elif isinstance(self._renderer, gtk.CellRendererToggle):
+            column = gtk.TreeViewColumn(self._heading, self._renderer,
+                                        active = index)
+        else:
+            column = gtk.TreeViewColumn(self._heading, self._renderer)
+        column.set_expand(True)
+        if self._sortable:
+            column.set_sort_column_id(index)
+            column.set_sort_indicator(True)
+
+        if self._extraColumnAttributes is not None:
+            for (key, value) in self._extraColumnAttributes.iteritems():
+                if key=="alignment":
+                    self._renderer.set_alignment(value, 0.5)
+                else:
+                    raise Exception("unhandled extra column attribute '" +
+                                    key + "'")
+        if self._cellDataFn is not None:
+            column.set_cell_data_func(self._renderer, self._cellDataFn)
+
+        return column
+
+    def getValueFrom(self, flight):
+        """Get the value from the given flight."""
+        attributes = self._attribute.split(".")
+        value = getattr(flight, attributes[0])
+        for attr in attributes[1:]:
+            value = getattr(value, attr)
+        return self._type(value) if self._convertFn is None \
+            else self._convertFn(value, flight)
+
+#-----------------------------------------------------------------------------
+
+class FlightList(gtk.Alignment):
+    """Construct the flight list.
+
+    This is a complete widget with a scroll window. It is alignment centered
+    horizontally and expandable vertically."""
+
+    defaultColumnDescriptors = [
+        ColumnDescriptor("callsign", xstr("flightsel_no")),
+        ColumnDescriptor("departureTime", xstr("flightsel_deptime"),
+                         sortable = True, defaultSortable = True),
+        ColumnDescriptor("departureICAO", xstr("flightsel_from"),
+                         sortable = True),
+        ColumnDescriptor("arrivalICAO", xstr("flightsel_to"), sortable = True)
+    ]
+
+    def __init__(self, columnDescriptors = defaultColumnDescriptors,
+                 popupMenuProducer = None, widthRequest = None,
+                 multiSelection = False):
+        """Construct the flight list with the given column descriptors."""
+
+        self._columnDescriptors = columnDescriptors
+        self._popupMenuProducer = popupMenuProducer
+        self._popupMenu = None
+
+        types = [int]
+        defaultSortableIndex = None
+        for columnDescriptor in self._columnDescriptors:
+            if columnDescriptor.defaultSortable:
+                defaultSortableIndex = len(types)
+            columnDescriptor.appendType(types)
+
+        self._model = gtk.ListStore(*types)
+        if defaultSortableIndex is not None:
+            sortOrder = SORT_DESCENDING \
+              if self._columnDescriptors[defaultSortableIndex-1]._defaultDescending \
+              else SORT_ASCENDING
+            self._model.set_sort_column_id(defaultSortableIndex, sortOrder)
+        self._view = gtk.TreeView(self._model)
+
+        flightIndexColumn = gtk.TreeViewColumn()
+        flightIndexColumn.set_visible(False)
+        self._view.append_column(flightIndexColumn)
+
+        index = 1
+        for columnDescriptor in self._columnDescriptors:
+            column = columnDescriptor.getViewColumn(index)
+            self._view.append_column(column)
+            index += 1
+
+        self._view.connect("row-activated", self._rowActivated)
+        self._view.connect("button-press-event", self._buttonPressEvent)
+
+        selection = self._view.get_selection()
+        selection.connect("changed", self._selectionChanged)
+        if multiSelection:
+            selection.set_mode(SELECTION_MULTIPLE)
+
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.add(self._view)
+        if widthRequest is not None:
+            scrolledWindow.set_size_request(widthRequest, -1)
+        # FIXME: these should be constants in common.py
+        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC,
+                                  gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC)
+        scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
+                                       else gtk.SHADOW_IN)
+
+        super(FlightList, self).__init__(xalign = 0.5, yalign = 0.0,
+                                         xscale = 0.0, yscale = 1.0)
+        self.add(scrolledWindow)
+
+    @property
+    def selectedIndexes(self):
+        """Get the indexes of the selected entries, if any.
+
+        The indexes are sorted."""
+        selection = self._view.get_selection()
+        (model, rows) = selection.get_selected_rows()
+
+        indexes = [self._getIndexForPath(path) for path in rows]
+        indexes.sort()
+        return indexes
+
+    @property
+    def hasFlights(self):
+        """Determine if there are any flights in the list."""
+        return self._model.get_iter_root() is not None
+
+    def clear(self):
+        """Clear the model."""
+        self._model.clear()
+
+    def addFlight(self, flight):
+        """Add the given booked flight."""
+        values = [self._model.iter_n_children(None)]
+        for columnDescriptor in self._columnDescriptors:
+            values.append(columnDescriptor.getValueFrom(flight))
+        self._model.append(values)
+
+    def removeFlights(self, indexes):
+        """Remove the flights with the given indexes."""
+        model = self._model
+        idx = 0
+        iter = model.get_iter_first()
+        while iter is not None:
+            nextIter = model.iter_next(iter)
+            if model.get_value(iter, 0) in indexes:
+                model.remove(iter)
+            else:
+                model.set_value(iter, 0, idx)
+                idx += 1
+            iter = nextIter
+
+    def _getIndexForPath(self, path):
+        """Get the index for the given path."""
+        iter = self._model.get_iter(path)
+        return self._model.get_value(iter, 0)
+
+    def _rowActivated(self, flightList, path, column):
+        """Called when a row is selected."""
+        self.emit("row-activated", self._getIndexForPath(path))
+
+    def _buttonPressEvent(self, widget, event):
+        """Called when a mouse button is pressed or released."""
+        if event.type!=EVENT_BUTTON_PRESS or event.button!=3 or \
+           self._popupMenuProducer is None:
+            return
+
+        (path, _, _, _) = self._view.get_path_at_pos(int(event.x),
+                                                     int(event.y))
+        selection = self._view.get_selection()
+        selection.unselect_all()
+        selection.select_path(path)
+
+        if self._popupMenu is None:
+            self._popupMenu = self._popupMenuProducer()
+        menu = self._popupMenu
+        if pygobject:
+            menu.popup(None, None, None, None, event.button, event.time)
+        else:
+            menu.popup(None, None, None, event.button, event.time)
+
+    def _selectionChanged(self, selection):
+        """Called when the selection has changed."""
+        self.emit("selection-changed", self.selectedIndexes)
+
+#-------------------------------------------------------------------------------
+
+gobject.signal_new("row-activated", FlightList, gobject.SIGNAL_RUN_FIRST,
+                   None, (int,))
+
+gobject.signal_new("selection-changed", FlightList, gobject.SIGNAL_RUN_FIRST,
+                   None, (object,))
+
+#-----------------------------------------------------------------------------
+
+class PendingFlightsFrame(gtk.Frame):
+    """A frame for a list of pending (reported or rejected) flights.
+
+    It contains the list and the buttons available."""
+    @staticmethod
+    def getAircraft(tailNumber, bookedFlight):
+        """Get the aircraft from the given booked flight.
+
+        This is the tail number followed by the ICAO code of the aircraft's
+        type."""
+        return tailNumber + \
+            " (" + const.icaoCodes[bookedFlight.aircraftType] + ")"
+
+    def _getAcft(tailNumber, bookedFlight):
+        return PendingFlightsFrame.getAircraft(tailNumber, bookedFlight)
+
+    columnDescriptors = [
+        ColumnDescriptor("callsign", xstr("flightsel_no")),
+        ColumnDescriptor("departureTime", xstr("flightsel_deptime"),
+                         sortable = True, defaultSortable = True),
+        ColumnDescriptor("departureICAO", xstr("flightsel_from"),
+                         sortable = True),
+        ColumnDescriptor("arrivalICAO", xstr("flightsel_to"),
+                         sortable = True),
+        ColumnDescriptor("tailNumber", xstr("pendflt_acft"),
+                         convertFn = _getAcft)
+    ]
+
+    def __init__(self, which, wizard, window, pirepEditable = False):
+        """Construct the frame with the given title."""
+        super(PendingFlightsFrame, self).__init__(xstr("pendflt_title_" + which))
+
+        self._which = which
+        self._wizard = wizard
+        self._window = window
+        self._pirepEditable = pirepEditable
+
+        alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 2, padding_bottom = 8,
+                              padding_left = 4, padding_right = 4)
+
+        hbox = gtk.HBox()
+
+        self._flights = []
+        self._flightList = FlightList(columnDescriptors =
+                                      PendingFlightsFrame.columnDescriptors,
+                                      widthRequest = 500, multiSelection = True)
+        self._flightList.connect("selection-changed", self._selectionChanged)
+
+        hbox.pack_start(self._flightList, True, True, 4)
+
+        buttonBox = gtk.VBox()
+
+        self._editButton = gtk.Button(xstr("pendflt_" +
+                                           ("edit" if pirepEditable else
+                                            "view") + "_" + which))
+        self._editButton.set_sensitive(False)
+        self._editButton.connect("clicked", self._editClicked)
+        buttonBox.pack_start(self._editButton, False, False, 2)
+
+        self._reflyButton = gtk.Button(xstr("pendflt_refly_" + which))
+        self._reflyButton.set_sensitive(False)
+        self._reflyButton.connect("clicked", self._reflyClicked)
+        buttonBox.pack_start(self._reflyButton, False, False, 2)
+
+        self._deleteButton = gtk.Button(xstr("pendflt_delete_" + which))
+        self._deleteButton.set_sensitive(False)
+        self._deleteButton.connect("clicked", self._deleteClicked)
+        buttonBox.pack_start(self._deleteButton, False, False, 2)
+
+        hbox.pack_start(buttonBox, False, False, 4)
+
+        alignment.add(hbox)
+        self.add(alignment)
+
+    @property
+    def hasFlights(self):
+        """Determine if there are any flights in the list."""
+        return self._flightList.hasFlights
+
+    def clear(self):
+        """Clear the lists."""
+        self._flights = []
+        self._flightList.clear()
+
+    def addFlight(self, flight):
+        """Add a flight to the list."""
+        self._flights.append(flight)
+        self._flightList.addFlight(flight)
+
+    def _selectionChanged(self, flightList, selectedIndexes):
+        """Called when the selection in the list has changed."""
+        self._editButton.set_sensitive(len(selectedIndexes)==1)
+        self._reflyButton.set_sensitive(len(selectedIndexes)>0)
+        self._deleteButton.set_sensitive(len(selectedIndexes)>0)
+
+    def _editClicked(self, button):
+        """Called when the Edit button is clicked."""
+        gui = self._wizard.gui
+        gui.beginBusy(xstr("pendflt_pirep_busy"))
+        self.set_sensitive(False)
+
+        indexes = self._flightList.selectedIndexes
+        assert(len(indexes)==1)
+
+        flightID = self._flights[indexes[0]].id
+        gui.webHandler.getPIREP(self._pirepResultCallback, flightID)
+
+    def _pirepResultCallback(self, returned, result):
+        """Called when the PIREP query result is available."""
+        gobject.idle_add(self._handlePIREPResult, returned, result)
+
+    def _handlePIREPResult(self, returned, result):
+        """Handle the refly result."""
+
+        self.set_sensitive(True)
+        gui = self._wizard.gui
+        gui.endBusy()
+
+        if returned:
+            if self._pirepEditable:
+                gui.editPIREP(result.pirep)
+            else:
+                gui.viewMessagedPIREP(result.pirep)
+
+    def _reflyClicked(self, button):
+        """Called when the Refly button is clicked."""
+        if askYesNo(xstr("pendflt_refly_question"), parent = self._window):
+            gui = self._wizard.gui
+            gui.beginBusy(xstr("pendflt_refly_busy"))
+            self.set_sensitive(False)
+
+            flightIDs = [self._flights[i].id
+                        for i in self._flightList.selectedIndexes]
+            gui.webHandler.reflyFlights(self._reflyResultCallback, flightIDs)
+
+    def _reflyResultCallback(self, returned, result):
+        """Called when the refly result is available."""
+        gobject.idle_add(self._handleReflyResult, returned, result)
+
+    def _handleReflyResult(self, returned, result):
+        """Handle the refly result."""
+
+        self.set_sensitive(True)
+        gui = self._wizard.gui
+        gui.endBusy()
+
+        print "PendingFlightsFrame._handleReflyResult", returned, result
+
+        if returned:
+            indexes = self._flightList.selectedIndexes
+
+            flights = [self._flights[index] for index in indexes]
+
+            self._flightList.removeFlights(indexes)
+            for index in indexes[::-1]:
+                del self._flights[index]
+
+            for flight in flights:
+                self._wizard.reflyFlight(flight)
+            self._window.checkFlights()
+        else:
+            communicationErrorDialog()
+
+    def _deleteClicked(self, button):
+        """Called when the Delete button is clicked."""
+        if askYesNo(xstr("flight_delete_question"), parent = self._window):
+            gui = self._wizard.gui
+            gui.beginBusy(xstr("pendflt_refly_busy"))
+            self.set_sensitive(False)
+
+            flightIDs = [self._flights[i].id
+                        for i in self._flightList.selectedIndexes]
+            gui.webHandler.deleteFlights(self._deleteResultCallback, flightIDs)
+
+    def _deleteResultCallback(self, returned, result):
+        """Called when the deletion result is available."""
+        gobject.idle_add(self._handleDeleteResult, returned, result)
+
+    def _handleDeleteResult(self, returned, result):
+        """Handle the delete result."""
+
+        self.set_sensitive(True)
+        gui = self._wizard.gui
+        gui.endBusy()
+
+        print "PendingFlightsFrame._handleDeleteResult", returned, result
+
+        if returned:
+            indexes = self._flightList.selectedIndexes
+
+            flights = [self._flights[index] for index in indexes]
+
+            self._flightList.removeFlights(indexes)
+            for index in indexes[::-1]:
+                del self._flights[index]
+
+            for flight in flights:
+                self._wizard.deleteFlight(flight)
+            self._window.checkFlights()
+        else:
+            communicationErrorDialog()
+
+#-----------------------------------------------------------------------------
+
+class PendingFlightsWindow(gtk.Window):
+    """The window to display the lists of the pending (reported or rejected)
+    flights."""
+    def __init__(self, wizard):
+        """Construct the window"""
+        super(PendingFlightsWindow, self).__init__()
+
+        gui = wizard.gui
+
+        self.set_title(WINDOW_TITLE_BASE + " - " + xstr("pendflt_title"))
+        self.set_size_request(-1, 450)
+        self.set_transient_for(gui.mainWindow)
+        self.set_modal(True)
+
+        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                      xscale = 1.0, yscale = 1.0)
+        mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
+                                  padding_left = 8, padding_right = 8)
+
+        vbox = gtk.VBox()
+
+        self._reportedFrame = PendingFlightsFrame("reported", wizard, self,
+                                                  True)
+        vbox.pack_start(self._reportedFrame, True, True, 2)
+
+        self._rejectedFrame = PendingFlightsFrame("rejected", wizard, self)
+        vbox.pack_start(self._rejectedFrame, True, True, 2)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        self._closeButton = gtk.Button(xstr("button_ok"))
+        self._closeButton.connect("clicked", self._closeClicked)
+        alignment.add(self._closeButton)
+        vbox.pack_start(alignment, False, False, 2)
+
+        mainAlignment.add(vbox)
+
+        self.add(mainAlignment)
+
+        self.connect("key-press-event", self._keyPressed)
+
+    @property
+    def hasFlights(self):
+        """Determine if the window has any flights."""
+        return self._reportedFrame.hasFlights or self._rejectedFrame.hasFlights
+
+    def clear(self):
+        """Clear the lists."""
+        self._reportedFrame.clear()
+        self._rejectedFrame.clear()
+
+    def addReportedFlight(self, flight):
+        """Add a reported flight."""
+        self._reportedFrame.addFlight(flight)
+
+    def addRejectedFlight(self, flight):
+        """Add a rejected flight."""
+        self._rejectedFrame.addFlight(flight)
+
+    def checkFlights(self):
+        """Check if there are any flights in any of the lists, and close the
+        window if not."""
+        if not self.hasFlights:
+            self.emit("delete-event", None)
+
+    def _closeClicked(self, button):
+        """Called when the Close button is clicked.
+
+        A 'delete-event' is emitted to close the window."""
+        self.emit("delete-event", None)
+
+    def _keyPressed(self, window, event):
+        """Called when a key is pressed in the window.
+
+        If the Escape key is pressed, 'delete-event' is emitted to close the
+        window."""
+        if gdk.keyval_name(event.keyval) == "Escape":
+            self.emit("delete-event", None)
+            return True
+
+#-----------------------------------------------------------------------------
+
+class AcceptedFlightsWindow(gtk.Window):
+    """A window for a list of accepted flights."""
+    def getFlightDuration(flightTimeStart, flight):
+        """Get the flight duration for the given flight."""
+        minutes = int(round((flight.flightTimeEnd - flightTimeStart)/60.0))
+        return "%02d:%02d" % (minutes/60, minutes%60)
+
+    columnDescriptors = [
+        ColumnDescriptor("bookedFlight.callsign", xstr("flightsel_no")),
+        ColumnDescriptor("bookedFlight.departureTime", xstr("flightsel_deptime"),
+                         sortable = True, defaultSortable = True,
+                         defaultDescending = True),
+        ColumnDescriptor("bookedFlight.departureICAO", xstr("flightsel_from"),
+                         sortable = True),
+        ColumnDescriptor("bookedFlight.arrivalICAO", xstr("flightsel_to"),
+                         sortable = True),
+        ColumnDescriptor("bookedFlight.tailNumber", xstr("pendflt_acft"),
+                         convertFn = lambda value, flight:
+                         PendingFlightsFrame.getAircraft(value,
+                                                         flight.bookedFlight)),
+        ColumnDescriptor("flightTimeStart", xstr("acceptedflt_flight_duration"),
+                         convertFn = getFlightDuration, sortable = True,
+                         extraColumnAttributes =
+                             { "alignment": 0.5 } ),
+        ColumnDescriptor("numPassengers", xstr("acceptedflt_num_pax"),
+                         type = int, sortable = True,
+                         extraColumnAttributes =
+                             { "alignment": 1.0 } ),
+        ColumnDescriptor("fuelUsed", xstr("acceptedflt_fuel"),
+                         type = int, sortable = True,
+                         extraColumnAttributes =
+                             { "alignment": 1.0 } ),
+        ColumnDescriptor("rating", xstr("acceptedflt_rating"),
+                         type = float, sortable = True,
+                         extraColumnAttributes =
+                             { "alignment": 1.0 },
+                         cellDataFn = lambda col, cell, model, iter:
+                             cell.set_property("text",
+                                               "%.0f" %
+                                               (model.get(iter, 9)[0],)))
+    ]
+
+    def __init__(self, gui):
+        """Construct the window."""
+        super(AcceptedFlightsWindow, self).__init__()
+
+        self._gui = gui
+
+        self.set_title(WINDOW_TITLE_BASE + " - " + xstr("acceptedflt_title"))
+        self.set_size_request(-1, 700)
+        self.set_transient_for(gui.mainWindow)
+
+        alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 2, padding_bottom = 8,
+                              padding_left = 4, padding_right = 4)
+
+        vbox = gtk.VBox()
+
+        hbox = gtk.HBox()
+        vbox.pack_start(hbox, True, True, 4)
+
+        self._flights = []
+        self._flightList = FlightList(columnDescriptors =
+                                      AcceptedFlightsWindow.columnDescriptors,
+                                      widthRequest = 750,
+                                      multiSelection = False)
+        self._flightList.connect("selection-changed", self._selectionChanged)
+
+        hbox.pack_start(self._flightList, True, True, 4)
+
+        buttonBox = gtk.VBox()
+
+        self._refreshButton = gtk.Button(xstr("acceptedflt_refresh"))
+        self._refreshButton.set_sensitive(True)
+        self._refreshButton.connect("clicked", self._refreshClicked)
+        buttonBox.pack_start(self._refreshButton, False, False, 2)
+
+        filler = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                               xscale = 1.0, yscale = 1.0)
+        filler.set_size_request(-1, 4)
+        buttonBox.pack_start(filler, False, False, 0)
+
+        self._viewButton = gtk.Button(xstr("acceptedflt_view"))
+        self._viewButton.set_sensitive(False)
+        self._viewButton.connect("clicked", self._viewClicked)
+        buttonBox.pack_start(self._viewButton, False, False, 2)
+
+        hbox.pack_start(buttonBox, False, False, 4)
+
+        buttonAlignment = gtk.Alignment(xscale = 0.0, yscale = 0.0,
+                                        xalign = 0.5, yalign = 0.5)
+
+        self._closeButton =  gtk.Button(xstr("button_ok"))
+        self._closeButton.connect("clicked", self._closeClicked)
+
+        buttonAlignment.add(self._closeButton)
+        vbox.pack_start(buttonAlignment, False, False, 2)
+
+        alignment.add(vbox)
+
+        self.add(alignment)
+
+        self.connect("key-press-event", self._keyPressed)
+
+    @property
+    def hasFlights(self):
+        """Determine if there are any flights that we know of."""
+        return len(self._flights)>0
+
+    def clear(self):
+        """Clear the flight list."""
+        self._flights = []
+        self._flightList.clear()
+
+    def addFlight(self, flight):
+        """Add the given flight."""
+        self._flights.append(flight)
+        self._flightList.addFlight(flight)
+
+    def _selectionChanged(self, flightList, selectedIndexes):
+        """Called when the selection has changed."""
+        self._viewButton.set_sensitive(len(selectedIndexes)==1)
+
+    def _refreshClicked(self, button):
+        """Called when the refresh button has been clicked."""
+        self.clear()
+        self._gui.showFlights(None)
+
+    def _viewClicked(self, button):
+        """Called when the view button has been clicked."""
+        gui = self._gui
+        gui.beginBusy(xstr("pendflt_pirep_busy"))
+        self.set_sensitive(False)
+
+        indexes = self._flightList.selectedIndexes
+        assert(len(indexes)==1)
+
+        flightID = self._flights[indexes[0]].bookedFlight.id
+        gui.webHandler.getPIREP(self._pirepResultCallback, flightID)
+
+    def _pirepResultCallback(self, returned, result):
+        """Called when the PIREP query result is available."""
+        gobject.idle_add(self._handlePIREPResult, returned, result)
+
+    def _handlePIREPResult(self, returned, result):
+        """Handle the refly result."""
+        self.set_sensitive(True)
+        gui = self._gui
+        gui.endBusy()
+
+        if returned:
+            gui.viewMessagedPIREP(result.pirep)
+
+    def _closeClicked(self, button):
+        """Called when the Close button is clicked.
+
+        A 'delete-event' is emitted to close the window."""
+        self.emit("delete-event", None)
+
+    def _keyPressed(self, window, event):
+        """Called when a key is pressed in the window.
+
+        If the Escape key is pressed, 'delete-event' is emitted to close the
+        window."""
+        if gdk.keyval_name(event.keyval) == "Escape":
+            self.emit("delete-event", None)
+            return True
+
+#-----------------------------------------------------------------------------
Index: src/mlx/gui/flightlist.py
===================================================================
--- src/mlx/gui/flightlist.py	(revision 859)
+++ 	(revision )
@@ -1,698 +1,0 @@
-# A widget which is a generic list of flights
-
-#-----------------------------------------------------------------------------
-
-from mlx.gui.common import *
-
-import mlx.const as const
-
-#-----------------------------------------------------------------------------
-
-class ColumnDescriptor(object):
-    """A descriptor for a column in the list."""
-    def __init__(self, attribute, heading, type = str,
-                 convertFn = None, renderer = None,
-                 extraColumnAttributes = None, sortable = False,
-                 defaultSortable = False, defaultDescending = False,
-                 cellDataFn = None):
-        """Construct the descriptor."""
-        self._attribute = attribute
-        self._heading = heading
-        self._type = type
-        self._convertFn = convertFn
-        self._renderer = \
-          gtk.CellRendererText() if renderer is None else renderer
-        self._extraColumnAttributes = extraColumnAttributes
-        self._sortable = sortable
-        self._defaultSortable = defaultSortable
-        self._defaultDescending = defaultDescending
-        self._cellDataFn = cellDataFn
-
-    @property
-    def attribute(self):
-        """Get the attribute the column belongs to."""
-        return self._attribute
-
-    @property
-    def defaultSortable(self):
-        """Determine if this column is the default sortable one."""
-        return self._defaultSortable
-
-    def appendType(self, types):
-        """Append the type of this column to the given list of types."""
-        types.append(self._type)
-
-    def getViewColumn(self, index):
-        """Get a new column object for a tree view.
-
-        @param index is the 0-based index of the column."""
-        if isinstance(self._renderer, gtk.CellRendererText):
-            column = gtk.TreeViewColumn(self._heading, self._renderer,
-                                        text = index)
-        elif isinstance(self._renderer, gtk.CellRendererToggle):
-            column = gtk.TreeViewColumn(self._heading, self._renderer,
-                                        active = index)
-        else:
-            column = gtk.TreeViewColumn(self._heading, self._renderer)
-        column.set_expand(True)
-        if self._sortable:
-            column.set_sort_column_id(index)
-            column.set_sort_indicator(True)
-
-        if self._extraColumnAttributes is not None:
-            for (key, value) in self._extraColumnAttributes.iteritems():
-                if key=="alignment":
-                    self._renderer.set_alignment(value, 0.5)
-                else:
-                    raise Exception("unhandled extra column attribute '" +
-                                    key + "'")
-        if self._cellDataFn is not None:
-            column.set_cell_data_func(self._renderer, self._cellDataFn)
-
-        return column
-
-    def getValueFrom(self, flight):
-        """Get the value from the given flight."""
-        attributes = self._attribute.split(".")
-        value = getattr(flight, attributes[0])
-        for attr in attributes[1:]:
-            value = getattr(value, attr)
-        return self._type(value) if self._convertFn is None \
-            else self._convertFn(value, flight)
-
-#-----------------------------------------------------------------------------
-
-class FlightList(gtk.Alignment):
-    """Construct the flight list.
-
-    This is a complete widget with a scroll window. It is alignment centered
-    horizontally and expandable vertically."""
-
-    defaultColumnDescriptors = [
-        ColumnDescriptor("callsign", xstr("flightsel_no")),
-        ColumnDescriptor("departureTime", xstr("flightsel_deptime"),
-                         sortable = True, defaultSortable = True),
-        ColumnDescriptor("departureICAO", xstr("flightsel_from"),
-                         sortable = True),
-        ColumnDescriptor("arrivalICAO", xstr("flightsel_to"), sortable = True)
-    ]
-
-    def __init__(self, columnDescriptors = defaultColumnDescriptors,
-                 popupMenuProducer = None, widthRequest = None,
-                 multiSelection = False):
-        """Construct the flight list with the given column descriptors."""
-
-        self._columnDescriptors = columnDescriptors
-        self._popupMenuProducer = popupMenuProducer
-        self._popupMenu = None
-
-        types = [int]
-        defaultSortableIndex = None
-        for columnDescriptor in self._columnDescriptors:
-            if columnDescriptor.defaultSortable:
-                defaultSortableIndex = len(types)
-            columnDescriptor.appendType(types)
-
-        self._model = gtk.ListStore(*types)
-        if defaultSortableIndex is not None:
-            sortOrder = SORT_DESCENDING \
-              if self._columnDescriptors[defaultSortableIndex-1]._defaultDescending \
-              else SORT_ASCENDING
-            self._model.set_sort_column_id(defaultSortableIndex, sortOrder)
-        self._view = gtk.TreeView(self._model)
-
-        flightIndexColumn = gtk.TreeViewColumn()
-        flightIndexColumn.set_visible(False)
-        self._view.append_column(flightIndexColumn)
-
-        index = 1
-        for columnDescriptor in self._columnDescriptors:
-            column = columnDescriptor.getViewColumn(index)
-            self._view.append_column(column)
-            index += 1
-
-        self._view.connect("row-activated", self._rowActivated)
-        self._view.connect("button-press-event", self._buttonPressEvent)
-
-        selection = self._view.get_selection()
-        selection.connect("changed", self._selectionChanged)
-        if multiSelection:
-            selection.set_mode(SELECTION_MULTIPLE)
-
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.add(self._view)
-        if widthRequest is not None:
-            scrolledWindow.set_size_request(widthRequest, -1)
-        # FIXME: these should be constants in common.py
-        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC,
-                                  gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC)
-        scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
-                                       else gtk.SHADOW_IN)
-
-        super(FlightList, self).__init__(xalign = 0.5, yalign = 0.0,
-                                         xscale = 0.0, yscale = 1.0)
-        self.add(scrolledWindow)
-
-    @property
-    def selectedIndexes(self):
-        """Get the indexes of the selected entries, if any.
-
-        The indexes are sorted."""
-        selection = self._view.get_selection()
-        (model, rows) = selection.get_selected_rows()
-
-        indexes = [self._getIndexForPath(path) for path in rows]
-        indexes.sort()
-        return indexes
-
-    @property
-    def hasFlights(self):
-        """Determine if there are any flights in the list."""
-        return self._model.get_iter_root() is not None
-
-    def clear(self):
-        """Clear the model."""
-        self._model.clear()
-
-    def addFlight(self, flight):
-        """Add the given booked flight."""
-        values = [self._model.iter_n_children(None)]
-        for columnDescriptor in self._columnDescriptors:
-            values.append(columnDescriptor.getValueFrom(flight))
-        self._model.append(values)
-
-    def removeFlights(self, indexes):
-        """Remove the flights with the given indexes."""
-        model = self._model
-        idx = 0
-        iter = model.get_iter_first()
-        while iter is not None:
-            nextIter = model.iter_next(iter)
-            if model.get_value(iter, 0) in indexes:
-                model.remove(iter)
-            else:
-                model.set_value(iter, 0, idx)
-                idx += 1
-            iter = nextIter
-
-    def _getIndexForPath(self, path):
-        """Get the index for the given path."""
-        iter = self._model.get_iter(path)
-        return self._model.get_value(iter, 0)
-
-    def _rowActivated(self, flightList, path, column):
-        """Called when a row is selected."""
-        self.emit("row-activated", self._getIndexForPath(path))
-
-    def _buttonPressEvent(self, widget, event):
-        """Called when a mouse button is pressed or released."""
-        if event.type!=EVENT_BUTTON_PRESS or event.button!=3 or \
-           self._popupMenuProducer is None:
-            return
-
-        (path, _, _, _) = self._view.get_path_at_pos(int(event.x),
-                                                     int(event.y))
-        selection = self._view.get_selection()
-        selection.unselect_all()
-        selection.select_path(path)
-
-        if self._popupMenu is None:
-            self._popupMenu = self._popupMenuProducer()
-        menu = self._popupMenu
-        if pygobject:
-            menu.popup(None, None, None, None, event.button, event.time)
-        else:
-            menu.popup(None, None, None, event.button, event.time)
-
-    def _selectionChanged(self, selection):
-        """Called when the selection has changed."""
-        self.emit("selection-changed", self.selectedIndexes)
-
-#-------------------------------------------------------------------------------
-
-gobject.signal_new("row-activated", FlightList, gobject.SIGNAL_RUN_FIRST,
-                   None, (int,))
-
-gobject.signal_new("selection-changed", FlightList, gobject.SIGNAL_RUN_FIRST,
-                   None, (object,))
-
-#-----------------------------------------------------------------------------
-
-class PendingFlightsFrame(gtk.Frame):
-    """A frame for a list of pending (reported or rejected) flights.
-
-    It contains the list and the buttons available."""
-    @staticmethod
-    def getAircraft(tailNumber, bookedFlight):
-        """Get the aircraft from the given booked flight.
-
-        This is the tail number followed by the ICAO code of the aircraft's
-        type."""
-        return tailNumber + \
-            " (" + const.icaoCodes[bookedFlight.aircraftType] + ")"
-
-    def _getAcft(tailNumber, bookedFlight):
-        return PendingFlightsFrame.getAircraft(tailNumber, bookedFlight)
-
-    columnDescriptors = [
-        ColumnDescriptor("callsign", xstr("flightsel_no")),
-        ColumnDescriptor("departureTime", xstr("flightsel_deptime"),
-                         sortable = True, defaultSortable = True),
-        ColumnDescriptor("departureICAO", xstr("flightsel_from"),
-                         sortable = True),
-        ColumnDescriptor("arrivalICAO", xstr("flightsel_to"),
-                         sortable = True),
-        ColumnDescriptor("tailNumber", xstr("pendflt_acft"),
-                         convertFn = _getAcft)
-    ]
-
-    def __init__(self, which, wizard, window, pirepEditable = False):
-        """Construct the frame with the given title."""
-        super(PendingFlightsFrame, self).__init__(xstr("pendflt_title_" + which))
-
-        self._which = which
-        self._wizard = wizard
-        self._window = window
-        self._pirepEditable = pirepEditable
-
-        alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 2, padding_bottom = 8,
-                              padding_left = 4, padding_right = 4)
-
-        hbox = gtk.HBox()
-
-        self._flights = []
-        self._flightList = FlightList(columnDescriptors =
-                                      PendingFlightsFrame.columnDescriptors,
-                                      widthRequest = 500, multiSelection = True)
-        self._flightList.connect("selection-changed", self._selectionChanged)
-
-        hbox.pack_start(self._flightList, True, True, 4)
-
-        buttonBox = gtk.VBox()
-
-        self._editButton = gtk.Button(xstr("pendflt_" +
-                                           ("edit" if pirepEditable else
-                                            "view") + "_" + which))
-        self._editButton.set_sensitive(False)
-        self._editButton.connect("clicked", self._editClicked)
-        buttonBox.pack_start(self._editButton, False, False, 2)
-
-        self._reflyButton = gtk.Button(xstr("pendflt_refly_" + which))
-        self._reflyButton.set_sensitive(False)
-        self._reflyButton.connect("clicked", self._reflyClicked)
-        buttonBox.pack_start(self._reflyButton, False, False, 2)
-
-        self._deleteButton = gtk.Button(xstr("pendflt_delete_" + which))
-        self._deleteButton.set_sensitive(False)
-        self._deleteButton.connect("clicked", self._deleteClicked)
-        buttonBox.pack_start(self._deleteButton, False, False, 2)
-
-        hbox.pack_start(buttonBox, False, False, 4)
-
-        alignment.add(hbox)
-        self.add(alignment)
-
-    @property
-    def hasFlights(self):
-        """Determine if there are any flights in the list."""
-        return self._flightList.hasFlights
-
-    def clear(self):
-        """Clear the lists."""
-        self._flights = []
-        self._flightList.clear()
-
-    def addFlight(self, flight):
-        """Add a flight to the list."""
-        self._flights.append(flight)
-        self._flightList.addFlight(flight)
-
-    def _selectionChanged(self, flightList, selectedIndexes):
-        """Called when the selection in the list has changed."""
-        self._editButton.set_sensitive(len(selectedIndexes)==1)
-        self._reflyButton.set_sensitive(len(selectedIndexes)>0)
-        self._deleteButton.set_sensitive(len(selectedIndexes)>0)
-
-    def _editClicked(self, button):
-        """Called when the Edit button is clicked."""
-        gui = self._wizard.gui
-        gui.beginBusy(xstr("pendflt_pirep_busy"))
-        self.set_sensitive(False)
-
-        indexes = self._flightList.selectedIndexes
-        assert(len(indexes)==1)
-
-        flightID = self._flights[indexes[0]].id
-        gui.webHandler.getPIREP(self._pirepResultCallback, flightID)
-
-    def _pirepResultCallback(self, returned, result):
-        """Called when the PIREP query result is available."""
-        gobject.idle_add(self._handlePIREPResult, returned, result)
-
-    def _handlePIREPResult(self, returned, result):
-        """Handle the refly result."""
-
-        self.set_sensitive(True)
-        gui = self._wizard.gui
-        gui.endBusy()
-
-        if returned:
-            if self._pirepEditable:
-                gui.editPIREP(result.pirep)
-            else:
-                gui.viewMessagedPIREP(result.pirep)
-
-    def _reflyClicked(self, button):
-        """Called when the Refly button is clicked."""
-        if askYesNo(xstr("pendflt_refly_question"), parent = self._window):
-            gui = self._wizard.gui
-            gui.beginBusy(xstr("pendflt_refly_busy"))
-            self.set_sensitive(False)
-
-            flightIDs = [self._flights[i].id
-                        for i in self._flightList.selectedIndexes]
-            gui.webHandler.reflyFlights(self._reflyResultCallback, flightIDs)
-
-    def _reflyResultCallback(self, returned, result):
-        """Called when the refly result is available."""
-        gobject.idle_add(self._handleReflyResult, returned, result)
-
-    def _handleReflyResult(self, returned, result):
-        """Handle the refly result."""
-
-        self.set_sensitive(True)
-        gui = self._wizard.gui
-        gui.endBusy()
-
-        print "PendingFlightsFrame._handleReflyResult", returned, result
-
-        if returned:
-            indexes = self._flightList.selectedIndexes
-
-            flights = [self._flights[index] for index in indexes]
-
-            self._flightList.removeFlights(indexes)
-            for index in indexes[::-1]:
-                del self._flights[index]
-
-            for flight in flights:
-                self._wizard.reflyFlight(flight)
-            self._window.checkFlights()
-        else:
-            communicationErrorDialog()
-
-    def _deleteClicked(self, button):
-        """Called when the Delete button is clicked."""
-        if askYesNo(xstr("flight_delete_question"), parent = self._window):
-            gui = self._wizard.gui
-            gui.beginBusy(xstr("pendflt_refly_busy"))
-            self.set_sensitive(False)
-
-            flightIDs = [self._flights[i].id
-                        for i in self._flightList.selectedIndexes]
-            gui.webHandler.deleteFlights(self._deleteResultCallback, flightIDs)
-
-    def _deleteResultCallback(self, returned, result):
-        """Called when the deletion result is available."""
-        gobject.idle_add(self._handleDeleteResult, returned, result)
-
-    def _handleDeleteResult(self, returned, result):
-        """Handle the delete result."""
-
-        self.set_sensitive(True)
-        gui = self._wizard.gui
-        gui.endBusy()
-
-        print "PendingFlightsFrame._handleDeleteResult", returned, result
-
-        if returned:
-            indexes = self._flightList.selectedIndexes
-
-            flights = [self._flights[index] for index in indexes]
-
-            self._flightList.removeFlights(indexes)
-            for index in indexes[::-1]:
-                del self._flights[index]
-
-            for flight in flights:
-                self._wizard.deleteFlight(flight)
-            self._window.checkFlights()
-        else:
-            communicationErrorDialog()
-
-#-----------------------------------------------------------------------------
-
-class PendingFlightsWindow(gtk.Window):
-    """The window to display the lists of the pending (reported or rejected)
-    flights."""
-    def __init__(self, wizard):
-        """Construct the window"""
-        super(PendingFlightsWindow, self).__init__()
-
-        gui = wizard.gui
-
-        self.set_title(WINDOW_TITLE_BASE + " - " + xstr("pendflt_title"))
-        self.set_size_request(-1, 450)
-        self.set_transient_for(gui.mainWindow)
-        self.set_modal(True)
-
-        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                      xscale = 1.0, yscale = 1.0)
-        mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
-                                  padding_left = 8, padding_right = 8)
-
-        vbox = gtk.VBox()
-
-        self._reportedFrame = PendingFlightsFrame("reported", wizard, self,
-                                                  True)
-        vbox.pack_start(self._reportedFrame, True, True, 2)
-
-        self._rejectedFrame = PendingFlightsFrame("rejected", wizard, self)
-        vbox.pack_start(self._rejectedFrame, True, True, 2)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        self._closeButton = gtk.Button(xstr("button_ok"))
-        self._closeButton.connect("clicked", self._closeClicked)
-        alignment.add(self._closeButton)
-        vbox.pack_start(alignment, False, False, 2)
-
-        mainAlignment.add(vbox)
-
-        self.add(mainAlignment)
-
-        self.connect("key-press-event", self._keyPressed)
-
-    @property
-    def hasFlights(self):
-        """Determine if the window has any flights."""
-        return self._reportedFrame.hasFlights or self._rejectedFrame.hasFlights
-
-    def clear(self):
-        """Clear the lists."""
-        self._reportedFrame.clear()
-        self._rejectedFrame.clear()
-
-    def addReportedFlight(self, flight):
-        """Add a reported flight."""
-        self._reportedFrame.addFlight(flight)
-
-    def addRejectedFlight(self, flight):
-        """Add a rejected flight."""
-        self._rejectedFrame.addFlight(flight)
-
-    def checkFlights(self):
-        """Check if there are any flights in any of the lists, and close the
-        window if not."""
-        if not self.hasFlights:
-            self.emit("delete-event", None)
-
-    def _closeClicked(self, button):
-        """Called when the Close button is clicked.
-
-        A 'delete-event' is emitted to close the window."""
-        self.emit("delete-event", None)
-
-    def _keyPressed(self, window, event):
-        """Called when a key is pressed in the window.
-
-        If the Escape key is pressed, 'delete-event' is emitted to close the
-        window."""
-        if gdk.keyval_name(event.keyval) == "Escape":
-            self.emit("delete-event", None)
-            return True
-
-#-----------------------------------------------------------------------------
-
-class AcceptedFlightsWindow(gtk.Window):
-    """A window for a list of accepted flights."""
-    def getFlightDuration(flightTimeStart, flight):
-        """Get the flight duration for the given flight."""
-        minutes = int(round((flight.flightTimeEnd - flightTimeStart)/60.0))
-        return "%02d:%02d" % (minutes/60, minutes%60)
-
-    columnDescriptors = [
-        ColumnDescriptor("bookedFlight.callsign", xstr("flightsel_no")),
-        ColumnDescriptor("bookedFlight.departureTime", xstr("flightsel_deptime"),
-                         sortable = True, defaultSortable = True,
-                         defaultDescending = True),
-        ColumnDescriptor("bookedFlight.departureICAO", xstr("flightsel_from"),
-                         sortable = True),
-        ColumnDescriptor("bookedFlight.arrivalICAO", xstr("flightsel_to"),
-                         sortable = True),
-        ColumnDescriptor("bookedFlight.tailNumber", xstr("pendflt_acft"),
-                         convertFn = lambda value, flight:
-                         PendingFlightsFrame.getAircraft(value,
-                                                         flight.bookedFlight)),
-        ColumnDescriptor("flightTimeStart", xstr("acceptedflt_flight_duration"),
-                         convertFn = getFlightDuration, sortable = True,
-                         extraColumnAttributes =
-                             { "alignment": 0.5 } ),
-        ColumnDescriptor("numPassengers", xstr("acceptedflt_num_pax"),
-                         type = int, sortable = True,
-                         extraColumnAttributes =
-                             { "alignment": 1.0 } ),
-        ColumnDescriptor("fuelUsed", xstr("acceptedflt_fuel"),
-                         type = int, sortable = True,
-                         extraColumnAttributes =
-                             { "alignment": 1.0 } ),
-        ColumnDescriptor("rating", xstr("acceptedflt_rating"),
-                         type = float, sortable = True,
-                         extraColumnAttributes =
-                             { "alignment": 1.0 },
-                         cellDataFn = lambda col, cell, model, iter:
-                             cell.set_property("text",
-                                               "%.0f" %
-                                               (model.get(iter, 9)[0],)))
-    ]
-
-    def __init__(self, gui):
-        """Construct the window."""
-        super(AcceptedFlightsWindow, self).__init__()
-
-        self._gui = gui
-
-        self.set_title(WINDOW_TITLE_BASE + " - " + xstr("acceptedflt_title"))
-        self.set_size_request(-1, 700)
-        self.set_transient_for(gui.mainWindow)
-
-        alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 2, padding_bottom = 8,
-                              padding_left = 4, padding_right = 4)
-
-        vbox = gtk.VBox()
-
-        hbox = gtk.HBox()
-        vbox.pack_start(hbox, True, True, 4)
-
-        self._flights = []
-        self._flightList = FlightList(columnDescriptors =
-                                      AcceptedFlightsWindow.columnDescriptors,
-                                      widthRequest = 750,
-                                      multiSelection = False)
-        self._flightList.connect("selection-changed", self._selectionChanged)
-
-        hbox.pack_start(self._flightList, True, True, 4)
-
-        buttonBox = gtk.VBox()
-
-        self._refreshButton = gtk.Button(xstr("acceptedflt_refresh"))
-        self._refreshButton.set_sensitive(True)
-        self._refreshButton.connect("clicked", self._refreshClicked)
-        buttonBox.pack_start(self._refreshButton, False, False, 2)
-
-        filler = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                               xscale = 1.0, yscale = 1.0)
-        filler.set_size_request(-1, 4)
-        buttonBox.pack_start(filler, False, False, 0)
-
-        self._viewButton = gtk.Button(xstr("acceptedflt_view"))
-        self._viewButton.set_sensitive(False)
-        self._viewButton.connect("clicked", self._viewClicked)
-        buttonBox.pack_start(self._viewButton, False, False, 2)
-
-        hbox.pack_start(buttonBox, False, False, 4)
-
-        buttonAlignment = gtk.Alignment(xscale = 0.0, yscale = 0.0,
-                                        xalign = 0.5, yalign = 0.5)
-
-        self._closeButton =  gtk.Button(xstr("button_ok"))
-        self._closeButton.connect("clicked", self._closeClicked)
-
-        buttonAlignment.add(self._closeButton)
-        vbox.pack_start(buttonAlignment, False, False, 2)
-
-        alignment.add(vbox)
-
-        self.add(alignment)
-
-        self.connect("key-press-event", self._keyPressed)
-
-    @property
-    def hasFlights(self):
-        """Determine if there are any flights that we know of."""
-        return len(self._flights)>0
-
-    def clear(self):
-        """Clear the flight list."""
-        self._flights = []
-        self._flightList.clear()
-
-    def addFlight(self, flight):
-        """Add the given flight."""
-        self._flights.append(flight)
-        self._flightList.addFlight(flight)
-
-    def _selectionChanged(self, flightList, selectedIndexes):
-        """Called when the selection has changed."""
-        self._viewButton.set_sensitive(len(selectedIndexes)==1)
-
-    def _refreshClicked(self, button):
-        """Called when the refresh button has been clicked."""
-        self.clear()
-        self._gui.showFlights(None)
-
-    def _viewClicked(self, button):
-        """Called when the view button has been clicked."""
-        gui = self._gui
-        gui.beginBusy(xstr("pendflt_pirep_busy"))
-        self.set_sensitive(False)
-
-        indexes = self._flightList.selectedIndexes
-        assert(len(indexes)==1)
-
-        flightID = self._flights[indexes[0]].bookedFlight.id
-        gui.webHandler.getPIREP(self._pirepResultCallback, flightID)
-
-    def _pirepResultCallback(self, returned, result):
-        """Called when the PIREP query result is available."""
-        gobject.idle_add(self._handlePIREPResult, returned, result)
-
-    def _handlePIREPResult(self, returned, result):
-        """Handle the refly result."""
-        self.set_sensitive(True)
-        gui = self._gui
-        gui.endBusy()
-
-        if returned:
-            gui.viewMessagedPIREP(result.pirep)
-
-    def _closeClicked(self, button):
-        """Called when the Close button is clicked.
-
-        A 'delete-event' is emitted to close the window."""
-        self.emit("delete-event", None)
-
-    def _keyPressed(self, window, event):
-        """Called when a key is pressed in the window.
-
-        If the Escape key is pressed, 'delete-event' is emitted to close the
-        window."""
-        if gdk.keyval_name(event.keyval) == "Escape":
-            self.emit("delete-event", None)
-            return True
-
-#-----------------------------------------------------------------------------
Index: src/mlx/gui/gates.py
===================================================================
--- src/mlx/gui/gates.py	(revision 619)
+++ src/mlx/gui/gates.py	(revision 619)
@@ -0,0 +1,183 @@
+
+from mlx.gui.common import *
+
+from mlx.i18n import xstr
+import mlx.const as const
+from mlx.gates import Gates, lhbpGates
+
+#-------------------------------------------------------------------------------
+
+## @package mlx.gui.gates
+#
+# The gate status display.
+#
+# This module contains the \ref FleetGateStatus widget, which is the tab
+# displaying the status of the MAVA Fleet and the gates at LHBP. The left side
+# of the widget is the table with the list of the planes and their states. The
+# right side displays the numbers of the gates and their occupation status.
+
+#-------------------------------------------------------------------------------
+
+class FleetGateStatus(gtk.VBox):
+    """The tab to display the fleet and gate status."""
+    def __init__(self, gui):
+        """Construct the tab."""
+        super(FleetGateStatus, self).__init__()
+
+        self._gui = gui
+
+        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                      xscale = 1.0, yscale = 1.0)
+        mainAlignment.set_padding(padding_top = 4, padding_bottom = 4,
+                                  padding_left = 12, padding_right = 12)
+        self.add(mainAlignment)
+
+        self._mainBox = mainBox = gtk.VBox()
+        mainAlignment.add(mainBox)
+    
+        self._statusBox = statusBox = gtk.HBox()
+        mainBox.pack_start(statusBox, True, True, 4)
+
+        fleetFrame = gtk.Frame(label = xstr("gates_fleet_title"))
+        fleetLabel = fleetFrame.get_label_widget()
+        fleetLabel.set_use_underline(True)
+        statusBox.pack_start(fleetFrame, False, False, 4)
+
+        self._fleetStore = gtk.ListStore(str, str)
+        self._fleetList = gtk.TreeView(self._fleetStore)
+        self._fleetList.set_tooltip_markup(xstr("gates_planes_tooltip"))
+        fleetLabel.set_mnemonic_widget(self._fleetList)
+        column = gtk.TreeViewColumn(xstr("gates_tailno"), gtk.CellRendererText(),
+                                    text = 0)
+        column.set_expand(True)
+        column.set_sort_column_id(0)
+        self._fleetList.append_column(column)
+
+        column = gtk.TreeViewColumn(xstr("gates_planestatus"), gtk.CellRendererText(),
+                                    markup = 1)
+        column.set_expand(True)
+        column.set_sort_column_id(1)
+        self._fleetList.append_column(column)
+
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.add(self._fleetList)
+        scrolledWindow.set_size_request(200, -1)
+        # FIXME: these should be constants in common.py
+        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC,
+                                  gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC)
+        scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
+                                       else gtk.SHADOW_IN)
+
+
+        self._fleetAlignment =  alignment = \
+                               gtk.Alignment(xalign = 0.5, yalign = 0.0,
+                                             xscale = 0.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 4,
+                              padding_left = 4, padding_right = 4)
+        alignment.add(scrolledWindow)
+        fleetFrame.add(alignment)        
+        
+        self._gatesFrame = gatesFrame = gtk.Frame(label = xstr("gates_gates_title"))
+        statusBox.pack_start(gatesFrame, True, True, 4)        
+
+        self._gatesTable = table = gtk.Table(lhbpGates.numRows,
+                                             lhbpGates.numColumns)
+        table.set_tooltip_markup(xstr("gates_gates_tooltip"))
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 4,
+                              padding_left = 4, padding_right = 4)
+        alignment.add(table)
+        gatesFrame.add(alignment)
+        
+        self._gateLabels = {}
+        column = 0
+        row = 0
+        for (type, data) in lhbpGates.displayInfos:
+            if type==Gates.DISPLAY_GATE:
+                gate = data
+
+                label = gtk.Label()
+                label.set_markup("<b>" + gate.number + "</b>")
+                table.attach(label, column, column + 1, row, row + 1)
+
+                self._gateLabels[gate.number] = label
+                row += 1
+            elif type==Gates.DISPLAY_SPACE:
+                row += 1
+            elif type==Gates.DISPLAY_NEW_COLUMN:
+                row = 0
+                column += 1
+
+        button = gtk.Button(xstr("gates_refresh"))
+        button.set_use_underline(True)
+        button.set_tooltip_text(xstr("gates_refresh_tooltip"))
+        button.connect("clicked", self._refreshClicked)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(button)
+        mainBox.pack_start(alignment, False, False, 4)
+
+        self._fleetList.set_sensitive(False)
+        self._gatesTable.set_sensitive(False)
+        
+    def disable(self):
+        """Disable the widget."""
+        self._mainBox.set_sensitive(False)
+    
+    def enable(self):
+        """Enable the widget."""
+        self._mainBox.set_sensitive(True)
+
+    def _refreshClicked(self, button):
+        """Called when the Refresh data button is clicked."""
+        self._gui.getFleet(force = True)
+
+    def handleFleet(self, fleet):
+        """Handle new fleet information.
+
+        If fleet is None, the data will be cleared."""
+        self._fleetStore.clear()
+        if fleet is None:
+            for (gateNumber, label) in self._gateLabels.iteritems():
+                label.set_markup("<b>" + gateNumber + "</b>")
+        else:        
+            for plane in fleet:
+                conflicting = False
+                tailNumber = plane.tailNumber
+                if plane.status==const.PLANE_HOME:
+                    status = "LHBP - %s" % (plane.gateNumber,)
+                    conflicting = fleet.isGateConflicting(plane)
+                elif plane.status==const.PLANE_AWAY:
+                    status = xstr("gates_plane_away")
+                elif plane.status==const.PLANE_PARKING:
+                    status = xstr("gates_plane_parking")
+                else:
+                    status = xstr("gates_plane_unknown")
+
+                if conflicting:
+                    tailNumber = '<span foreground="red">' + tailNumber + '</span>'
+                    status = '<span foreground="red">' + status + '</span>'
+
+                self._fleetStore.append([tailNumber, status])
+            # FIXME: this should be a constant in common.py
+            self._fleetStore.set_sort_column_id(0,
+                                                gtk.SortType.ASCENDING if pygobject
+                                                else gtk.SORT_ASCENDING)
+
+            occupiedGateNumbers = fleet.getOccupiedGateNumbers()
+            for gate in lhbpGates.gates:
+                gateNumber = gate.number
+                markup = gateNumber
+                if gateNumber in occupiedGateNumbers:
+                    markup = '<span foreground="orange">' + markup + '</span>'
+                markup = '<b>' + markup + '</b>'
+                self._gateLabels[gateNumber].set_markup(markup)
+            
+        self._fleetList.set_sensitive(fleet is not None)
+        self._gatesTable.set_sensitive(fleet is not None)
+
+#----------------------------------------------------------------------------
Index: src/mlx/gui/gates.py
===================================================================
--- src/mlx/gui/gates.py	(revision 619)
+++ 	(revision )
@@ -1,183 +1,0 @@
-
-from mlx.gui.common import *
-
-from mlx.i18n import xstr
-import mlx.const as const
-from mlx.gates import Gates, lhbpGates
-
-#-------------------------------------------------------------------------------
-
-## @package mlx.gui.gates
-#
-# The gate status display.
-#
-# This module contains the \ref FleetGateStatus widget, which is the tab
-# displaying the status of the MAVA Fleet and the gates at LHBP. The left side
-# of the widget is the table with the list of the planes and their states. The
-# right side displays the numbers of the gates and their occupation status.
-
-#-------------------------------------------------------------------------------
-
-class FleetGateStatus(gtk.VBox):
-    """The tab to display the fleet and gate status."""
-    def __init__(self, gui):
-        """Construct the tab."""
-        super(FleetGateStatus, self).__init__()
-
-        self._gui = gui
-
-        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                      xscale = 1.0, yscale = 1.0)
-        mainAlignment.set_padding(padding_top = 4, padding_bottom = 4,
-                                  padding_left = 12, padding_right = 12)
-        self.add(mainAlignment)
-
-        self._mainBox = mainBox = gtk.VBox()
-        mainAlignment.add(mainBox)
-    
-        self._statusBox = statusBox = gtk.HBox()
-        mainBox.pack_start(statusBox, True, True, 4)
-
-        fleetFrame = gtk.Frame(label = xstr("gates_fleet_title"))
-        fleetLabel = fleetFrame.get_label_widget()
-        fleetLabel.set_use_underline(True)
-        statusBox.pack_start(fleetFrame, False, False, 4)
-
-        self._fleetStore = gtk.ListStore(str, str)
-        self._fleetList = gtk.TreeView(self._fleetStore)
-        self._fleetList.set_tooltip_markup(xstr("gates_planes_tooltip"))
-        fleetLabel.set_mnemonic_widget(self._fleetList)
-        column = gtk.TreeViewColumn(xstr("gates_tailno"), gtk.CellRendererText(),
-                                    text = 0)
-        column.set_expand(True)
-        column.set_sort_column_id(0)
-        self._fleetList.append_column(column)
-
-        column = gtk.TreeViewColumn(xstr("gates_planestatus"), gtk.CellRendererText(),
-                                    markup = 1)
-        column.set_expand(True)
-        column.set_sort_column_id(1)
-        self._fleetList.append_column(column)
-
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.add(self._fleetList)
-        scrolledWindow.set_size_request(200, -1)
-        # FIXME: these should be constants in common.py
-        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC,
-                                  gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC)
-        scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
-                                       else gtk.SHADOW_IN)
-
-
-        self._fleetAlignment =  alignment = \
-                               gtk.Alignment(xalign = 0.5, yalign = 0.0,
-                                             xscale = 0.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 4,
-                              padding_left = 4, padding_right = 4)
-        alignment.add(scrolledWindow)
-        fleetFrame.add(alignment)        
-        
-        self._gatesFrame = gatesFrame = gtk.Frame(label = xstr("gates_gates_title"))
-        statusBox.pack_start(gatesFrame, True, True, 4)        
-
-        self._gatesTable = table = gtk.Table(lhbpGates.numRows,
-                                             lhbpGates.numColumns)
-        table.set_tooltip_markup(xstr("gates_gates_tooltip"))
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 4,
-                              padding_left = 4, padding_right = 4)
-        alignment.add(table)
-        gatesFrame.add(alignment)
-        
-        self._gateLabels = {}
-        column = 0
-        row = 0
-        for (type, data) in lhbpGates.displayInfos:
-            if type==Gates.DISPLAY_GATE:
-                gate = data
-
-                label = gtk.Label()
-                label.set_markup("<b>" + gate.number + "</b>")
-                table.attach(label, column, column + 1, row, row + 1)
-
-                self._gateLabels[gate.number] = label
-                row += 1
-            elif type==Gates.DISPLAY_SPACE:
-                row += 1
-            elif type==Gates.DISPLAY_NEW_COLUMN:
-                row = 0
-                column += 1
-
-        button = gtk.Button(xstr("gates_refresh"))
-        button.set_use_underline(True)
-        button.set_tooltip_text(xstr("gates_refresh_tooltip"))
-        button.connect("clicked", self._refreshClicked)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(button)
-        mainBox.pack_start(alignment, False, False, 4)
-
-        self._fleetList.set_sensitive(False)
-        self._gatesTable.set_sensitive(False)
-        
-    def disable(self):
-        """Disable the widget."""
-        self._mainBox.set_sensitive(False)
-    
-    def enable(self):
-        """Enable the widget."""
-        self._mainBox.set_sensitive(True)
-
-    def _refreshClicked(self, button):
-        """Called when the Refresh data button is clicked."""
-        self._gui.getFleet(force = True)
-
-    def handleFleet(self, fleet):
-        """Handle new fleet information.
-
-        If fleet is None, the data will be cleared."""
-        self._fleetStore.clear()
-        if fleet is None:
-            for (gateNumber, label) in self._gateLabels.iteritems():
-                label.set_markup("<b>" + gateNumber + "</b>")
-        else:        
-            for plane in fleet:
-                conflicting = False
-                tailNumber = plane.tailNumber
-                if plane.status==const.PLANE_HOME:
-                    status = "LHBP - %s" % (plane.gateNumber,)
-                    conflicting = fleet.isGateConflicting(plane)
-                elif plane.status==const.PLANE_AWAY:
-                    status = xstr("gates_plane_away")
-                elif plane.status==const.PLANE_PARKING:
-                    status = xstr("gates_plane_parking")
-                else:
-                    status = xstr("gates_plane_unknown")
-
-                if conflicting:
-                    tailNumber = '<span foreground="red">' + tailNumber + '</span>'
-                    status = '<span foreground="red">' + status + '</span>'
-
-                self._fleetStore.append([tailNumber, status])
-            # FIXME: this should be a constant in common.py
-            self._fleetStore.set_sort_column_id(0,
-                                                gtk.SortType.ASCENDING if pygobject
-                                                else gtk.SORT_ASCENDING)
-
-            occupiedGateNumbers = fleet.getOccupiedGateNumbers()
-            for gate in lhbpGates.gates:
-                gateNumber = gate.number
-                markup = gateNumber
-                if gateNumber in occupiedGateNumbers:
-                    markup = '<span foreground="orange">' + markup + '</span>'
-                markup = '<b>' + markup + '</b>'
-                self._gateLabels[gateNumber].set_markup(markup)
-            
-        self._fleetList.set_sensitive(fleet is not None)
-        self._gatesTable.set_sensitive(fleet is not None)
-
-#----------------------------------------------------------------------------
Index: src/mlx/gui/gui.py
===================================================================
--- src/mlx/gui/gui.py	(revision 859)
+++ src/mlx/gui/gui.py	(revision 859)
@@ -0,0 +1,1946 @@
+# -*- coding: utf-8 -*-
+
+from statusicon import StatusIcon
+from statusbar import Statusbar
+from info import FlightInfo
+from update import Updater
+from mlx.gui.common import *
+from mlx.gui.flight import Wizard
+from mlx.gui.monitor import MonitorWindow
+from mlx.gui.weighthelp import WeightHelp
+from mlx.gui.gates import FleetGateStatus
+from mlx.gui.prefs import Preferences
+from mlx.gui.checklist import ChecklistEditor
+from mlx.gui.callouts import ApproachCalloutsEditor
+from mlx.gui.flightlist import AcceptedFlightsWindow
+from mlx.gui.pirep import PIREPViewer, PIREPEditor
+from mlx.gui.bugreport import BugReportDialog
+from mlx.gui.acars import ACARS
+from mlx.gui.timetable import TimetableWindow
+import cef
+
+import mlx.const as const
+import mlx.fs as fs
+import mlx.flight as flight
+import mlx.logger as logger
+import mlx.acft as acft
+import mlx.web as web
+import mlx.singleton as singleton
+import mlx.airports as airports
+from  mlx.i18n import xstr, getLanguage
+from mlx.pirep import PIREP
+
+import time
+import threading
+import sys
+import datetime
+import webbrowser
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.gui
+#
+# The main GUI class.
+#
+# The \ref GUI class is the main class of the GUI. It is a connection listener,
+# and aggregates all the windows, the menu, etc. It maintains the connection to
+# the simulator as well as the flight object.
+
+#------------------------------------------------------------------------------
+
+class GUI(fs.ConnectionListener):
+    """The main GUI class."""
+    _authors = [ (u"Váradi", u"István", "prog_test"),
+                 (u"Galyassy", u"Tamás", "negotiation"),
+                 (u"Kurják", u"Ákos", "test"),
+                 (u"Nagy", u"Dániel", "test"),
+                 (u"Radó", u"Iván", "test"),
+                 (u"Petrovszki", u"Gábor", "test"),
+                 (u"Serfőző", u"Tamás", "test"),
+                 (u"Szebenyi", u"Bálint", "test"),
+                 (u"Zsebényi-Loksa", u"Gergely", "test") ]
+
+    def __init__(self, programDirectory, config):
+        """Construct the GUI."""
+        gobject.threads_init()
+
+        self._programDirectory = programDirectory
+        self.config = config
+        self._connecting = False
+        self._reconnecting = False
+        self._connected = False
+        self._logger = logger.Logger(self)
+        self._flight = None
+        self._simulator = None
+        self._fsType = None
+        self._monitoring = False
+
+        self._fleet = None
+
+        self._fleetCallback = None
+
+        self._updatePlaneCallback = None
+        self._updatePlaneTailNumber = None
+        self._updatePlaneStatus = None
+        self._updatePlaneGateNumber = None
+
+        self._stdioLock = threading.Lock()
+        self._stdioText = ""
+        self._stdioStartingLine = True
+
+        self._sendPIREPCallback = None
+        self._sendBugReportCallback = None
+
+        self._credentialsCondition = threading.Condition()
+        self._credentialsAvailable = False
+        self._credentialsUserName = None
+        self._credentialsPassword = None
+
+        self._bookFlightsUserCallback = None
+        self._bookFlightsBusyCallback = None
+
+        self.webHandler = web.Handler(config, self._getCredentialsCallback)
+        self.webHandler.start()
+
+        self.toRestart = False
+
+    def build(self, iconDirectory):
+        """Build the GUI."""
+
+        self._mainWindow = window = gtk.Window()
+        window.set_title(WINDOW_TITLE_BASE)
+        window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
+        window.set_resizable(False)
+        window.connect("delete-event", self.deleteMainWindow)
+        window.connect("window-state-event", self._handleMainWindowState)
+        if os.name=="nt":
+            window.connect("leave-notify-event", self._handleLeaveNotify)
+        accelGroup = gtk.AccelGroup()
+        window.add_accel_group(accelGroup)
+        window.realize()
+
+        mainVBox = gtk.VBox()
+        window.add(mainVBox)
+
+        self._preferences = Preferences(self)
+        self._timetableWindow = TimetableWindow(self)
+        self._timetableWindow.connect("delete-event", self._hideTimetableWindow)
+        self._flightsWindow = AcceptedFlightsWindow(self)
+        self._flightsWindow.connect("delete-event", self._hideFlightsWindow)
+        self._checklistEditor = ChecklistEditor(self)
+        self._approachCalloutsEditor = ApproachCalloutsEditor(self)
+        self._bugReportDialog = BugReportDialog(self)
+
+        menuBar = self._buildMenuBar(accelGroup)
+        mainVBox.pack_start(menuBar, False, False, 0)
+
+        self._notebook = gtk.Notebook()
+        mainVBox.pack_start(self._notebook, True, True, 4)
+
+        self._wizard = Wizard(self)
+        label = gtk.Label(xstr("tab_flight"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("tab_flight_tooltip"))
+        self._notebook.append_page(self._wizard, label)
+
+        self._flightInfo = FlightInfo(self)
+        label = gtk.Label(xstr("tab_flight_info"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
+        self._notebook.append_page(self._flightInfo, label)
+        self._flightInfo.disable()
+
+        self._weightHelp = WeightHelp(self)
+        label = gtk.Label(xstr("tab_weight_help"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
+        self._notebook.append_page(self._weightHelp, label)
+
+        (logWidget, self._logView)  = self._buildLogWidget()
+        addFaultTag(self._logView.get_buffer())
+        label = gtk.Label(xstr("tab_log"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("tab_log_tooltip"))
+        self._notebook.append_page(logWidget, label)
+
+        self._fleetGateStatus = FleetGateStatus(self)
+        label = gtk.Label(xstr("tab_gates"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("tab_gates_tooltip"))
+        self._notebook.append_page(self._fleetGateStatus, label)
+
+        self._acars = ACARS(self)
+        label = gtk.Label("ACARS")
+        label.set_use_underline(True)
+        self._notebook.append_page(self._acars, label)
+
+        (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
+        self._debugLogWidget.show_all()
+
+        mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
+
+        self._statusbar = Statusbar(iconDirectory)
+        mainVBox.pack_start(self._statusbar, False, False, 0)
+
+        self._notebook.connect("switch-page", self._notebookPageSwitch)
+
+        self._monitorWindow = MonitorWindow(self, iconDirectory)
+        self._monitorWindow.add_accel_group(accelGroup)
+        self._monitorWindowX = None
+        self._monitorWindowY = None
+        self._selfToggling = False
+
+        self._pirepViewer = PIREPViewer(self)
+        self._messagedPIREPViewer = PIREPViewer(self, showMessages = True)
+
+        self._pirepEditor = PIREPEditor(self)
+
+        window.show_all()
+        if os.name=="nt":
+            window.get_window().focus()
+
+        self._wizard.grabDefault()
+        self._weightHelp.reset()
+        self._weightHelp.disable()
+
+        self._statusIcon = StatusIcon(iconDirectory, self)
+
+        self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
+                                      else gdk.WATCH)
+
+        self._loadPIREPDialog = None
+        self._lastLoadedPIREP = None
+
+        self._hotkeySetID = None
+        self._pilotHotkeyIndex = None
+        self._checklistHotkeyIndex = None
+
+        self._aboutDialog = None
+
+    @property
+    def mainWindow(self):
+        """Get the main window of the GUI."""
+        return self._mainWindow
+
+    @property
+    def logger(self):
+        """Get the logger used by us."""
+        return self._logger
+
+    @property
+    def simulator(self):
+        """Get the simulator used by us."""
+        return self._simulator
+
+    @property
+    def flight(self):
+        """Get the flight being performed."""
+        return self._flight
+
+    @property
+    def fsType(self):
+        """Get the flight simulator type."""
+        return self._fsType
+
+    @property
+    def entranceExam(self):
+        """Get whether an entrance exam is about to be taken."""
+        return self._wizard.entranceExam
+
+    @property
+    def loggedIn(self):
+        """Indicate if the user has logged in properly."""
+        return self._wizard.loggedIn
+
+    @property
+    def loginResult(self):
+        """Get the result of the login."""
+        return self._wizard.loginResult
+
+    @property
+    def bookedFlight(self):
+        """Get the booked flight selected, if any."""
+        return self._wizard.bookedFlight
+
+    @property
+    def numCrew(self):
+        """Get the number of crew members."""
+        return self._wizard.numCrew
+
+    @property
+    def numPassengers(self):
+        """Get the number of passengers."""
+        return self._wizard.numPassengers
+
+    @property
+    def bagWeight(self):
+        """Get the bag weight."""
+        return self._wizard.bagWeight
+
+    @property
+    def cargoWeight(self):
+        """Get the cargo weight."""
+        return self._wizard.cargoWeight
+
+    @property
+    def mailWeight(self):
+        """Get the mail weight."""
+        return self._wizard.mailWeight
+
+    @property
+    def zfw(self):
+        """Get Zero-Fuel Weight calculated for the current flight."""
+        return self._wizard.zfw
+
+    @property
+    def filedCruiseAltitude(self):
+        """Get cruise altitude filed for the current flight."""
+        return self._wizard.filedCruiseAltitude
+
+    @property
+    def cruiseAltitude(self):
+        """Get cruise altitude set for the current flight."""
+        return self._wizard.cruiseAltitude
+
+    @property
+    def loggableCruiseAltitude(self):
+        """Get the cruise altitude that can be logged."""
+        return self._wizard.loggableCruiseAltitude
+
+    @property
+    def route(self):
+        """Get the flight route."""
+        return self._wizard.route
+
+    @property
+    def departureMETAR(self):
+        """Get the METAR of the deprature airport."""
+        return self._wizard.departureMETAR
+
+    @property
+    def arrivalMETAR(self):
+        """Get the METAR of the deprature airport."""
+        return self._wizard.arrivalMETAR
+
+    @property
+    def departureRunway(self):
+        """Get the name of the departure runway."""
+        return self._wizard.departureRunway
+
+    @property
+    def sid(self):
+        """Get the SID."""
+        return self._wizard.sid
+
+    @property
+    def v1(self):
+        """Get the V1 speed calculated for the flight."""
+        return self._wizard.v1
+
+    @property
+    def vr(self):
+        """Get the Vr speed calculated for the flight."""
+        return self._wizard.vr
+
+    @property
+    def v2(self):
+        """Get the V2 speed calculated for the flight."""
+        return self._wizard.v2
+
+    @property
+    def derate(self):
+        """Get the derate value calculated for the flight."""
+        return self._wizard.derate
+
+    @property
+    def takeoffAntiIceOn(self):
+        """Get whether the anti-ice system was on during take-off."""
+        return self._wizard.takeoffAntiIceOn
+
+    @takeoffAntiIceOn.setter
+    def takeoffAntiIceOn(self, value):
+        """Set the anti-ice on indicator."""
+        gobject.idle_add(self._setTakeoffAntiIceOn, value)
+
+    @property
+    def rtoIndicated(self):
+        """Get whether the pilot has indicated than an RTO has occured."""
+        return self._wizard.rtoIndicated
+
+    @property
+    def arrivalRunway(self):
+        """Get the arrival runway."""
+        return self._wizard.arrivalRunway
+
+    @property
+    def star(self):
+        """Get the STAR."""
+        return self._wizard.star
+
+    @property
+    def transition(self):
+        """Get the transition."""
+        return self._wizard.transition
+
+    @property
+    def approachType(self):
+        """Get the approach type."""
+        return self._wizard.approachType
+
+    @property
+    def vref(self):
+        """Get the Vref speed calculated for the flight."""
+        return self._wizard.vref
+
+    @property
+    def landingAntiIceOn(self):
+        """Get whether the anti-ice system was on during landing."""
+        return self._wizard.landingAntiIceOn
+
+    @landingAntiIceOn.setter
+    def landingAntiIceOn(self, value):
+        """Set the anti-ice on indicator."""
+        gobject.idle_add(self._setLandingAntiIceOn, value)
+
+    @property
+    def flightType(self):
+        """Get the flight type."""
+        return self._wizard.flightType
+
+    @property
+    def online(self):
+        """Get whether the flight was online or not."""
+        return self._wizard.online
+
+    @property
+    def comments(self):
+        """Get the comments."""
+        return self._flightInfo.comments
+
+    @property
+    def hasComments(self):
+        """Indicate whether there is a comment."""
+        return self._flightInfo.hasComments
+
+    @property
+    def flightDefects(self):
+        """Get the flight defects."""
+        return self._flightInfo.faultsAndExplanations
+
+    @property
+    def delayCodes(self):
+        """Get the delay codes."""
+        return self._flightInfo.delayCodes
+
+    @property
+    def hasDelayCode(self):
+        """Determine if there is at least one delay code selected."""
+        return self._flightInfo.hasDelayCode
+
+    @property
+    def faultsFullyExplained(self):
+        """Determine if all the faults have been fully explained by the
+        user."""
+        return self._flightInfo.faultsFullyExplained
+
+    if pygobject:
+        @property
+        def backgroundColour(self):
+            """Get the background colour of the main window."""
+            return self._mainWindow.get_style_context().\
+              get_background_color(gtk.StateFlags.NORMAL)
+
+    def run(self):
+        """Run the GUI."""
+        if self.config.autoUpdate:
+            self._updater = Updater(self,
+                                    self._programDirectory,
+                                    self.config.updateURL,
+                                    self._mainWindow)
+            self._updater.start()
+        else:
+            self.updateDone()
+
+        cef.initialize(self._cefInitialized)
+
+        singleton.raiseCallback = self.raiseCallback
+        gtk.main()
+        singleton.raiseCallback = None
+
+        cef.finalize()
+
+        self._disconnect()
+
+    def updateDone(self):
+        """Called when the update is done (and there is no need to restart)."""
+        gobject.idle_add(self._updateDone)
+
+    def connected(self, fsType, descriptor):
+        """Called when we have connected to the simulator."""
+        self._connected = True
+        self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
+                                    (const.VERSION, descriptor))
+        fs.sendMessage(const.MESSAGETYPE_INFORMATION,
+                       "Welcome to MAVA Logger X " + const.VERSION)
+        gobject.idle_add(self._handleConnected, fsType, descriptor)
+
+    def _handleConnected(self, fsType, descriptor):
+        """Called when the connection to the simulator has succeeded."""
+        self._statusbar.updateConnection(self._connecting, self._connected)
+        self.endBusy()
+        if not self._reconnecting:
+            self._wizard.connected(fsType, descriptor)
+        self._reconnecting = False
+        self._fsType = fsType
+        self._listenHotkeys()
+
+    def connectionFailed(self):
+        """Called when the connection failed."""
+        self._logger.untimedMessage("Connection to the simulator failed")
+        gobject.idle_add(self._connectionFailed)
+
+    def _connectionFailed(self):
+        """Called when the connection failed."""
+        self.endBusy()
+        self._statusbar.updateConnection(self._connecting, self._connected)
+
+        dialog = gtk.MessageDialog(parent = self._mainWindow,
+                                   type = MESSAGETYPE_ERROR,
+                                   message_format = xstr("conn_failed"))
+
+        dialog.set_title(WINDOW_TITLE_BASE)
+        dialog.format_secondary_markup(xstr("conn_failed_sec"))
+
+        dialog.add_button(xstr("button_cancel"), 0)
+        dialog.add_button(xstr("button_tryagain"), 1)
+        dialog.set_default_response(1)
+
+        result = dialog.run()
+        dialog.hide()
+        if result == 1:
+            self.beginBusy(xstr("connect_busy"))
+            self._simulator.reconnect()
+        else:
+            self.reset()
+
+    def disconnected(self):
+        """Called when we have disconnected from the simulator."""
+        self._connected = False
+        self._logger.untimedMessage("Disconnected from the simulator")
+        if self._flight is not None:
+            self._flight.disconnected()
+
+        gobject.idle_add(self._disconnected)
+
+    def _disconnected(self):
+        """Called when we have disconnected from the simulator unexpectedly."""
+        self._statusbar.updateConnection(self._connecting, self._connected)
+
+        dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
+                                   message_format = xstr("conn_broken"),
+                                   parent = self._mainWindow)
+        dialog.set_title(WINDOW_TITLE_BASE)
+        dialog.format_secondary_markup(xstr("conn_broken_sec"))
+
+        dialog.add_button(xstr("button_cancel"), 0)
+        dialog.add_button(xstr("button_reconnect"), 1)
+        dialog.set_default_response(1)
+
+        result = dialog.run()
+        dialog.hide()
+        if result == 1:
+            self.beginBusy(xstr("connect_busy"))
+            self._reconnecting = True
+            self._simulator.reconnect()
+        else:
+            self.reset()
+
+    def enableFlightInfo(self, aircraftType):
+        """Enable the flight info tab."""
+        self._flightInfo.enable(aircraftType)
+
+    def bookFlights(self, callback, flightIDs, date, tailNumber,
+                    busyCallback = None):
+        """Initiate the booking of flights with the given timetable IDs and
+        other data"""
+        self._bookFlightsUserCallback = callback
+        self._bookFlightsBusyCallback = busyCallback
+
+        self.beginBusy(xstr("bookflights_busy"))
+        if busyCallback is not None:
+            busyCallback(True)
+
+        self.webHandler.bookFlights(self._bookFlightsCallback,
+                                    flightIDs, date, tailNumber)
+
+    def _bookFlightsCallback(self, returned, result):
+        """Called when the booking of flights has finished."""
+        gobject.idle_add(self._handleBookFlightsResult, returned, result)
+
+    def _handleBookFlightsResult(self, returned, result):
+        """Called when the booking of flights is done.
+
+        If it was successful, the booked flights are added to the list of the
+        flight selector."""
+        if self._bookFlightsBusyCallback is not None:
+            self._bookFlightsBusyCallback(False)
+        self.endBusy()
+
+        if returned:
+            for bookedFlight in result.bookedFlights:
+                self._wizard.addFlight(bookedFlight)
+
+        self._bookFlightsUserCallback(returned, result)
+
+    def cancelFlight(self):
+        """Cancel the current file, if the user confirms it."""
+        dialog = gtk.MessageDialog(parent = self._mainWindow,
+                                   type = MESSAGETYPE_QUESTION,
+                                   message_format = xstr("cancelFlight_question"))
+
+        dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
+        dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
+
+        dialog.set_title(WINDOW_TITLE_BASE)
+        result = dialog.run()
+        dialog.hide()
+
+        if result==RESPONSETYPE_YES:
+            self.reset()
+
+    def reset(self):
+        """Reset the GUI."""
+        self._disconnect()
+
+        self._simulator = None
+
+        self._flightInfo.reset()
+        self._flightInfo.disable()
+        self.resetFlightStatus()
+
+        self._weightHelp.reset()
+        self._weightHelp.disable()
+        self._notebook.set_current_page(0)
+
+        self._logView.get_buffer().set_text("")
+
+        if self.loggedIn:
+            self._wizard.cancelFlight(self._handleReloadResult)
+        else:
+            self._wizard.reset(None)
+
+    def _handleReloadResult(self, returned, result):
+        """Handle the result of the reloading of the flights."""
+        self._wizard.reset(result if returned and result.loggedIn else None)
+
+    def _disconnect(self, closingMessage = None, duration = 3):
+        """Disconnect from the simulator if connected."""
+        self.stopMonitoring()
+        self._clearHotkeys()
+
+        if self._connected:
+            if closingMessage is None:
+                self._flight.simulator.disconnect()
+            else:
+                fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
+                               closingMessage, duration,
+                               disconnect = True)
+            self._connected = False
+
+        self._connecting = False
+        self._reconnecting = False
+        self._statusbar.updateConnection(False, False)
+        self._weightHelp.disable()
+
+        return True
+
+    def insertFlightLogLine(self, index, timestampString, text, isFault):
+        """Insert the flight log line with the given data."""
+        gobject.idle_add(self._insertFlightLogLine, index,
+                         formatFlightLogLine(timestampString, text),
+                         isFault)
+
+    def _insertFlightLogLine(self, index, line, isFault):
+        """Perform the real insertion.
+
+        To be called from the event loop."""
+        buffer = self._logView.get_buffer()
+        lineIter = buffer.get_iter_at_line(index)
+        insertTextBuffer(buffer, lineIter, line, isFault = isFault)
+        self._logView.scroll_mark_onscreen(buffer.get_insert())
+
+    def removeFlightLogLine(self, index):
+        """Remove the flight log line with the given index."""
+        gobject.idle_add(self._removeFlightLogLine, index)
+
+    def addFault(self, id, timestampString, text):
+        """Add a fault to the list of faults."""
+        faultText = formatFlightLogLine(timestampString, text).strip()
+        gobject.idle_add(self._flightInfo.addFault, id, faultText)
+
+    def updateFault(self, id, timestampString, text):
+        """Update a fault in the list of faults."""
+        faultText = formatFlightLogLine(timestampString, text).strip()
+        gobject.idle_add(self._flightInfo.updateFault, id, faultText)
+
+    def clearFault(self, id):
+        """Clear a fault in the list of faults."""
+        gobject.idle_add(self._flightInfo.clearFault, id)
+
+    def _removeFlightLogLine(self, index):
+        """Perform the real removal."""
+        buffer = self._logView.get_buffer()
+        startIter = buffer.get_iter_at_line(index)
+        endIter = buffer.get_iter_at_line(index+1)
+        buffer.delete(startIter, endIter)
+        self._logView.scroll_mark_onscreen(buffer.get_insert())
+
+    def check(self, flight, aircraft, logger, oldState, state):
+        """Update the data."""
+        gobject.idle_add(self._monitorWindow.setData, state)
+        gobject.idle_add(self._statusbar.updateTime, state.timestamp)
+
+    def resetFlightStatus(self):
+        """Reset the status of the flight."""
+        self._statusbar.resetFlightStatus()
+        self._statusbar.updateTime()
+        self._statusIcon.resetFlightStatus()
+
+    def setStage(self, stage):
+        """Set the stage of the flight."""
+        gobject.idle_add(self._setStage, stage)
+
+    def _setStage(self, stage):
+        """Set the stage of the flight."""
+        self._statusbar.setStage(stage)
+        self._statusIcon.setStage(stage)
+        self._wizard.setStage(stage)
+        if stage==const.STAGE_END:
+            welcomeMessage = \
+                airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
+            self._disconnect(closingMessage =
+                             "Flight plan closed. " + welcomeMessage,
+                             duration = 5)
+
+    def setRating(self, rating):
+        """Set the rating of the flight."""
+        gobject.idle_add(self._setRating, rating)
+
+    def _setRating(self, rating):
+        """Set the rating of the flight."""
+        self._statusbar.setRating(rating)
+        self._statusIcon.setRating(rating)
+
+    def setNoGo(self, reason):
+        """Set the rating of the flight to No-Go with the given reason."""
+        gobject.idle_add(self._setNoGo, reason)
+
+    def _setNoGo(self, reason):
+        """Set the rating of the flight."""
+        self._statusbar.setNoGo(reason)
+        self._statusIcon.setNoGo(reason)
+
+    def _handleMainWindowState(self, window, event):
+        """Hande a change in the state of the window"""
+        iconified = gdk.WindowState.ICONIFIED if pygobject \
+                    else gdk.WINDOW_STATE_ICONIFIED
+
+        if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
+            if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
+                self._statusIcon.mainWindowHidden()
+            else:
+                self._statusIcon.mainWindowShown()
+
+        if self.config.hideMinimizedWindow and not pygobject and \
+           (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
+           (event.new_window_state&WINDOW_STATE_ICONIFIED)!=0:
+            self.hideMainWindow(savePosition = False)
+        elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
+             (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
+            self._mainWindow.present()
+
+    def _handleLeaveNotify(self, widget, event):
+        """Handle the leave-notify event.
+
+        Here we reset the focus to the main window as CEF might have acquired
+        it earlier."""
+        self._mainWindow.get_window().focus()
+
+    def raiseCallback(self):
+        """Callback for the singleton handling code."""
+        gobject.idle_add(self.raiseMainWindow)
+
+    def raiseMainWindow(self):
+        """Show the main window if invisible, and raise it."""
+        if not self._mainWindow.get_visible():
+            self.showMainWindow()
+        self._mainWindow.present()
+
+    def deleteMainWindow(self, window, event):
+        """Handle the delete event for the main window."""
+        if self.config.quitOnClose:
+            self._quit()
+        else:
+            self.hideMainWindow()
+        return True
+
+    def hideMainWindow(self, savePosition = True):
+        """Hide the main window and save its position."""
+        if savePosition:
+            (self._mainWindowX, self._mainWindowY) = \
+                 self._mainWindow.get_window().get_root_origin()
+        else:
+            self._mainWindowX = self._mainWindowY = None
+        self._mainWindow.hide()
+        return True
+
+    def showMainWindow(self):
+        """Show the main window at its former position."""
+        if self._mainWindowX is not None and self._mainWindowY is not None:
+            self._mainWindow.move(self._mainWindowX, self._mainWindowY)
+
+        if pygobject:
+            self._mainWindow.show()
+        else:
+            self._mainWindow.present()
+        self._mainWindow.deiconify()
+
+    def toggleMainWindow(self):
+        """Toggle the main window."""
+        if self._mainWindow.get_visible():
+            self.hideMainWindow()
+        else:
+            self.showMainWindow()
+
+    def hideMonitorWindow(self, savePosition = True):
+        """Hide the monitor window."""
+        if savePosition:
+            (self._monitorWindowX, self._monitorWindowY) = \
+                 self._monitorWindow.get_window().get_root_origin()
+        else:
+            self._monitorWindowX = self._monitorWindowY = None
+        self._monitorWindow.hide()
+        self._statusIcon.monitorWindowHidden()
+        if self._showMonitorMenuItem.get_active():
+            self._selfToggling = True
+            self._showMonitorMenuItem.set_active(False)
+        return True
+
+    def showMonitorWindow(self):
+        """Show the monitor window."""
+        if self._monitorWindowX is not None and self._monitorWindowY is not None:
+            self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
+        self._monitorWindow.show_all()
+        self._statusIcon.monitorWindowShown()
+        if not self._showMonitorMenuItem.get_active():
+            self._selfToggling = True
+            self._showMonitorMenuItem.set_active(True)
+
+    def _toggleMonitorWindow(self, menuItem):
+        if self._selfToggling:
+            self._selfToggling = False
+        elif self._monitorWindow.get_visible():
+            self.hideMonitorWindow()
+        else:
+            self.showMonitorWindow()
+
+    def restart(self):
+        """Quit and restart the application."""
+        self.toRestart = True
+        self._quit(force = True)
+
+    def flushStdIO(self):
+        """Flush any text to the standard error that could not be logged."""
+        if self._stdioText:
+            sys.__stderr__.write(self._stdioText)
+
+    def writeStdIO(self, text):
+        """Write the given text into standard I/O log."""
+        with self._stdioLock:
+            self._stdioText += text
+
+        gobject.idle_add(self._writeStdIO)
+
+    def beginBusy(self, message):
+        """Begin a period of background processing."""
+        self._wizard.set_sensitive(False)
+        self._weightHelp.set_sensitive(False)
+        self._mainWindow.get_window().set_cursor(self._busyCursor)
+        self._statusbar.updateBusyState(message)
+
+    def updateBusyState(self, message):
+        """Update the busy state."""
+        self._statusbar.updateBusyState(message)
+
+    def endBusy(self):
+        """End a period of background processing."""
+        self._mainWindow.get_window().set_cursor(None)
+        self._weightHelp.set_sensitive(True)
+        self._wizard.set_sensitive(True)
+        self._statusbar.updateBusyState(None)
+
+    def initializeWeightHelp(self):
+        """Initialize the weight help tab."""
+        self._weightHelp.reset()
+        self._weightHelp.enable()
+
+    def getFleetAsync(self, callback = None, force = None):
+        """Get the fleet asynchronously."""
+        gobject.idle_add(self.getFleet, callback, force)
+
+    def getFleet(self, callback = None, force = False, busyCallback = None):
+        """Get the fleet.
+
+        If force is False, and we already have a fleet retrieved,
+        that one will be used."""
+        if self._fleet is None or force:
+            self._fleetCallback = callback
+            self._fleetBusyCallback = busyCallback
+            if busyCallback is not None:
+                busyCallback(True)
+            self.beginBusy(xstr("fleet_busy"))
+            self.webHandler.getFleet(self._fleetResultCallback)
+        else:
+            callback(self._fleet)
+
+    def commentsChanged(self):
+        """Indicate that the comments have changed."""
+        self._wizard.commentsChanged()
+
+    def delayCodesChanged(self):
+        """Called when the delay codes have changed."""
+        self._wizard.delayCodesChanged()
+
+    def faultExplanationsChanged(self):
+        """Called when the status of the explanations of the faults have
+        changed."""
+        self._wizard.faultExplanationsChanged()
+
+    def updateRTO(self, inLoop = False):
+        """Indicate that the RTO state should be updated."""
+        if inLoop:
+            self._wizard.updateRTO()
+        else:
+            gobject.idle_add(self.updateRTO, True)
+
+    def rtoToggled(self, indicated):
+        """Called when the user has toggled the RTO checkbox."""
+        self._flight.rtoToggled(indicated)
+
+    def _fleetResultCallback(self, returned, result):
+        """Called when the fleet has been queried."""
+        gobject.idle_add(self._handleFleetResult, returned, result)
+
+    def _handleFleetResult(self, returned, result):
+        """Handle the fleet result."""
+        self.endBusy()
+        if self._fleetBusyCallback is not None:
+            self._fleetBusyCallback(False)
+        if returned:
+            self._fleet = result.fleet
+        else:
+            self._fleet = None
+
+            dialog = gtk.MessageDialog(parent = self.mainWindow,
+                                       type = MESSAGETYPE_ERROR,
+                                       message_format = xstr("fleet_failed"))
+            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+            dialog.set_title(WINDOW_TITLE_BASE)
+            dialog.run()
+            dialog.hide()
+
+        callback = self._fleetCallback
+        self._fleetCallback = None
+        self._fleetBusyCallback = None
+        if  callback is not None:
+            callback(self._fleet)
+        self._fleetGateStatus.handleFleet(self._fleet)
+
+    def updatePlane(self, tailNumber, status,
+                    gateNumber = None, callback = None):
+        """Update the status of the given plane."""
+        self.beginBusy(xstr("fleet_update_busy"))
+
+        self._updatePlaneCallback = callback
+
+        self._updatePlaneTailNumber = tailNumber
+        self._updatePlaneStatus = status
+        self._updatePlaneGateNumber = gateNumber
+
+        self.webHandler.updatePlane(self._updatePlaneResultCallback,
+                                    tailNumber, status, gateNumber)
+
+    def _updatePlaneResultCallback(self, returned, result):
+        """Called when the status of a plane has been updated."""
+        gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
+
+    def _handleUpdatePlaneResult(self, returned, result):
+        """Handle the plane update result."""
+        self.endBusy()
+        if returned:
+            success = result.success
+            if success:
+                if self._fleet is not None:
+                    self._fleet.updatePlane(self._updatePlaneTailNumber,
+                                            self._updatePlaneStatus,
+                                            self._updatePlaneGateNumber)
+                    self._fleetGateStatus.handleFleet(self._fleet)
+        else:
+            dialog = gtk.MessageDialog(parent = self.mainWindow,
+                                       type = MESSAGETYPE_ERROR,
+                                       message_format = xstr("fleet_update_failed"))
+            dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
+            dialog.set_title(WINDOW_TITLE_BASE)
+            dialog.run()
+            dialog.hide()
+
+            success = None
+
+        callback = self._updatePlaneCallback
+        self._updatePlaneCallback = None
+        if callback is not None:
+            callback(success)
+
+    def _writeStdIO(self):
+        """Perform the real writing."""
+        with self._stdioLock:
+            text = self._stdioText
+            self._stdioText = ""
+        if not text: return
+
+        lines = text.splitlines()
+        if text[-1]=="\n":
+            text = ""
+        else:
+            text = lines[-1]
+            lines = lines[:-1]
+
+        now = datetime.datetime.now()
+        timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
+
+        for line in lines:
+            #print >> sys.__stdout__, line
+            if self._stdioStartingLine:
+                self._writeLog(timeStr, self._debugLogView)
+            self._writeLog(line + "\n", self._debugLogView)
+            self._stdioStartingLine = True
+
+        if text:
+            #print >> sys.__stdout__, text,
+            if self._stdioStartingLine:
+                self._writeLog(timeStr, self._debugLogView)
+            self._writeLog(text, self._debugLogView)
+            self._stdioStartingLine = False
+
+    def connectSimulator(self, bookedFlight, simulatorType):
+        """Connect to the simulator for the first time."""
+        self._logger.reset()
+
+        self._flight = flight.Flight(self._logger, self)
+        self._flight.flareTimeFromFS = self.config.flareTimeFromFS
+        self._flight.aircraftType = bookedFlight.aircraftType
+        self._flight.aircraft = acft.Aircraft.create(self._flight, bookedFlight)
+        self._flight.aircraft._checkers.append(self)
+
+        if self._simulator is None:
+            self._simulator = fs.createSimulator(simulatorType, self)
+            fs.setupMessageSending(self.config, self._simulator)
+            self._setupTimeSync()
+
+        self._flight.simulator = self._simulator
+
+        self.beginBusy(xstr("connect_busy"))
+        self._statusbar.updateConnection(self._connecting, self._connected)
+
+        self._connecting = True
+        self._simulator.connect(self._flight.aircraft)
+
+    def startMonitoring(self):
+        """Start monitoring."""
+        if not self._monitoring:
+            self.simulator.startMonitoring()
+            self._monitoring = True
+
+    def stopMonitoring(self):
+        """Stop monitoring."""
+        if self._monitoring:
+            self.simulator.stopMonitoring()
+            self._monitoring = False
+
+    def cruiseLevelChanged(self):
+        """Called when the cruise level is changed in the flight wizard."""
+        if self._flight is not None:
+            return self._flight.cruiseLevelChanged()
+        else:
+            return False
+
+    def _buildMenuBar(self, accelGroup):
+        """Build the main menu bar."""
+        menuBar = gtk.MenuBar()
+
+        fileMenuItem = gtk.MenuItem(xstr("menu_file"))
+        fileMenu = gtk.Menu()
+        fileMenuItem.set_submenu(fileMenu)
+        menuBar.append(fileMenuItem)
+
+        loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
+        loadPIREPMenuItem.set_use_stock(True)
+        loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
+        loadPIREPMenuItem.add_accelerator("activate", accelGroup,
+                                          ord(xstr("menu_file_loadPIREP_key")),
+                                          CONTROL_MASK, ACCEL_VISIBLE)
+        loadPIREPMenuItem.connect("activate", self._loadPIREP)
+        fileMenu.append(loadPIREPMenuItem)
+
+        fileMenu.append(gtk.SeparatorMenuItem())
+
+        quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
+        quitMenuItem.set_use_stock(True)
+        quitMenuItem.set_label(xstr("menu_file_quit"))
+        quitMenuItem.add_accelerator("activate", accelGroup,
+                                     ord(xstr("menu_file_quit_key")),
+                                     CONTROL_MASK, ACCEL_VISIBLE)
+        quitMenuItem.connect("activate", self._quit)
+        fileMenu.append(quitMenuItem)
+
+        toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
+        toolsMenu = gtk.Menu()
+        toolsMenuItem.set_submenu(toolsMenu)
+        menuBar.append(toolsMenuItem)
+
+        self._timetableMenuItem = timetableMenuItem = \
+          gtk.ImageMenuItem(gtk.STOCK_INDENT)
+        timetableMenuItem.set_use_stock(True)
+        timetableMenuItem.set_label(xstr("menu_tools_timetable"))
+        timetableMenuItem.add_accelerator("activate", accelGroup,
+                                          ord(xstr("menu_tools_timetable_key")),
+                                          CONTROL_MASK, ACCEL_VISIBLE)
+        timetableMenuItem.connect("activate", self.showTimetable)
+        self._timetableMenuItem.set_sensitive(False)
+        toolsMenu.append(timetableMenuItem)
+
+        self._flightsMenuItem = flightsMenuItem = \
+          gtk.ImageMenuItem(gtk.STOCK_SPELL_CHECK)
+        flightsMenuItem.set_use_stock(True)
+        flightsMenuItem.set_label(xstr("menu_tools_flights"))
+        flightsMenuItem.add_accelerator("activate", accelGroup,
+                                        ord(xstr("menu_tools_flights_key")),
+                                        CONTROL_MASK, ACCEL_VISIBLE)
+        flightsMenuItem.connect("activate", self.showFlights)
+        self._flightsMenuItem.set_sensitive(False)
+        toolsMenu.append(flightsMenuItem)
+
+        checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
+        checklistMenuItem.set_use_stock(True)
+        checklistMenuItem.set_label(xstr("menu_tools_chklst"))
+        checklistMenuItem.add_accelerator("activate", accelGroup,
+                                          ord(xstr("menu_tools_chklst_key")),
+                                          CONTROL_MASK, ACCEL_VISIBLE)
+        checklistMenuItem.connect("activate", self._editChecklist)
+        toolsMenu.append(checklistMenuItem)
+
+        approachCalloutsMenuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT)
+        approachCalloutsMenuItem.set_use_stock(True)
+        approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
+        approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
+                                                 ord(xstr("menu_tools_callouts_key")),
+                                                 CONTROL_MASK, ACCEL_VISIBLE)
+        approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
+        toolsMenu.append(approachCalloutsMenuItem)
+
+        prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
+        prefsMenuItem.set_use_stock(True)
+        prefsMenuItem.set_label(xstr("menu_tools_prefs"))
+        prefsMenuItem.add_accelerator("activate", accelGroup,
+                                      ord(xstr("menu_tools_prefs_key")),
+                                      CONTROL_MASK, ACCEL_VISIBLE)
+        prefsMenuItem.connect("activate", self._editPreferences)
+        toolsMenu.append(prefsMenuItem)
+
+        toolsMenu.append(gtk.SeparatorMenuItem())
+
+        bugReportMenuItem = gtk.ImageMenuItem(gtk.STOCK_PASTE)
+        bugReportMenuItem.set_use_stock(True)
+        bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
+        bugReportMenuItem.add_accelerator("activate", accelGroup,
+                                          ord(xstr("menu_tools_bugreport_key")),
+                                          CONTROL_MASK, ACCEL_VISIBLE)
+        bugReportMenuItem.connect("activate", self._reportBug)
+        toolsMenu.append(bugReportMenuItem)
+
+        viewMenuItem = gtk.MenuItem(xstr("menu_view"))
+        viewMenu = gtk.Menu()
+        viewMenuItem.set_submenu(viewMenu)
+        menuBar.append(viewMenuItem)
+
+        self._showMonitorMenuItem = gtk.CheckMenuItem()
+        self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
+        self._showMonitorMenuItem.set_use_underline(True)
+        self._showMonitorMenuItem.set_active(False)
+        self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
+                                                  ord(xstr("menu_view_monitor_key")),
+                                                  CONTROL_MASK, ACCEL_VISIBLE)
+        self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
+        viewMenu.append(self._showMonitorMenuItem)
+
+        showDebugMenuItem = gtk.CheckMenuItem()
+        showDebugMenuItem.set_label(xstr("menu_view_debug"))
+        showDebugMenuItem.set_use_underline(True)
+        showDebugMenuItem.set_active(False)
+        showDebugMenuItem.add_accelerator("activate", accelGroup,
+                                          ord(xstr("menu_view_debug_key")),
+                                          CONTROL_MASK, ACCEL_VISIBLE)
+        showDebugMenuItem.connect("toggled", self._toggleDebugLog)
+        viewMenu.append(showDebugMenuItem)
+
+        helpMenuItem = gtk.MenuItem(xstr("menu_help"))
+        helpMenu = gtk.Menu()
+        helpMenuItem.set_submenu(helpMenu)
+        menuBar.append(helpMenuItem)
+
+        manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
+        manualMenuItem.set_use_stock(True)
+        manualMenuItem.set_label(xstr("menu_help_manual"))
+        manualMenuItem.add_accelerator("activate", accelGroup,
+                                       ord(xstr("menu_help_manual_key")),
+                                       CONTROL_MASK, ACCEL_VISIBLE)
+        manualMenuItem.connect("activate", self._showManual)
+        helpMenu.append(manualMenuItem)
+
+        helpMenu.append(gtk.SeparatorMenuItem())
+
+        aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
+        aboutMenuItem.set_use_stock(True)
+        aboutMenuItem.set_label(xstr("menu_help_about"))
+        aboutMenuItem.add_accelerator("activate", accelGroup,
+                                      ord(xstr("menu_help_about_key")),
+                                      CONTROL_MASK, ACCEL_VISIBLE)
+        aboutMenuItem.connect("activate", self._showAbout)
+        helpMenu.append(aboutMenuItem)
+
+        return menuBar
+
+    def _toggleDebugLog(self, menuItem):
+        """Toggle the debug log."""
+        if menuItem.get_active():
+            label = gtk.Label(xstr("tab_debug_log"))
+            label.set_use_underline(True)
+            label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
+            self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
+            self._notebook.set_current_page(self._debugLogPage)
+        else:
+            self._notebook.remove_page(self._debugLogPage)
+
+    def _buildLogWidget(self):
+        """Build the widget for the log."""
+        alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
+
+        alignment.set_padding(padding_top = 8, padding_bottom = 8,
+                              padding_left = 16, padding_right = 16)
+
+        logScroller = gtk.ScrolledWindow()
+        # FIXME: these should be constants in common
+        logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
+                               else gtk.POLICY_AUTOMATIC,
+                               gtk.PolicyType.AUTOMATIC if pygobject
+                               else gtk.POLICY_AUTOMATIC)
+        logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
+                                    else gtk.SHADOW_IN)
+        logView = gtk.TextView()
+        logView.set_editable(False)
+        logView.set_cursor_visible(False)
+        logScroller.add(logView)
+
+        logBox = gtk.VBox()
+        logBox.pack_start(logScroller, True, True, 0)
+        logBox.set_size_request(-1, 200)
+
+        alignment.add(logBox)
+
+        return (alignment, logView)
+
+    def _writeLog(self, msg, logView, isFault = False):
+        """Write the given message to the log."""
+        buffer = logView.get_buffer()
+        appendTextBuffer(buffer, msg, isFault = isFault)
+        logView.scroll_mark_onscreen(buffer.get_insert())
+
+    def _quit(self, what = None, force = False):
+        """Quit from the application."""
+        if force:
+            result=RESPONSETYPE_YES
+        else:
+            dialog = gtk.MessageDialog(parent = self._mainWindow,
+                                       type = MESSAGETYPE_QUESTION,
+                                       message_format = xstr("quit_question"))
+
+            dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
+            dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
+
+            dialog.set_title(WINDOW_TITLE_BASE)
+            result = dialog.run()
+            dialog.hide()
+
+        if result==RESPONSETYPE_YES:
+            self._statusIcon.destroy()
+            return gtk.main_quit()
+
+    def _notebookPageSwitch(self, notebook, page, page_num):
+        """Called when the current page of the notebook has changed."""
+        if page_num==0:
+            gobject.idle_add(self._wizard.grabDefault)
+        else:
+            self._mainWindow.set_default(None)
+
+    def loginSuccessful(self):
+        """Called when the login is successful."""
+        self._flightsMenuItem.set_sensitive(True)
+        self._timetableMenuItem.set_sensitive(True)
+
+    def isWizardActive(self):
+        """Determine if the flight wizard is active."""
+        return self._notebook.get_current_page()==0
+
+    def showTimetable(self, menuItem):
+        """Callback for showing the timetable."""
+        if self._timetableWindow.hasFlightPairs:
+            self._timetableWindow.show_all()
+        else:
+            date = datetime.date.today()
+            self._timetableWindow.setTypes(self.loginResult.types)
+            self._timetableWindow.setDate(date)
+            self.updateTimeTable(date)
+            self.beginBusy(xstr("timetable_query_busy"))
+
+    def updateTimeTable(self, date):
+        """Update the time table for the given date."""
+        self.beginBusy(xstr("timetable_query_busy"))
+        self._timetableWindow.set_sensitive(False)
+        window = self._timetableWindow.get_window()
+        if window is not None:
+            window.set_cursor(self._busyCursor)
+        self.webHandler.getTimetable(self._timetableCallback, date,
+                                     self.loginResult.types)
+
+    def _timetableCallback(self, returned, result):
+        """Called when the timetable has been received."""
+        gobject.idle_add(self._handleTimetable, returned, result)
+
+    def _handleTimetable(self, returned, result):
+        """Handle the result of the query for the timetable."""
+        self.endBusy()
+        window = self._timetableWindow.get_window()
+        if window is not None:
+            window.set_cursor(None)
+        self._timetableWindow.set_sensitive(True)
+        if returned:
+            self._timetableWindow.setFlightPairs(result.flightPairs)
+            self._timetableWindow.show_all()
+        else:
+            dialog = gtk.MessageDialog(parent = self.mainWindow,
+                                       type = MESSAGETYPE_ERROR,
+                                       message_format = xstr("timetable_failed"))
+            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+            dialog.set_title(WINDOW_TITLE_BASE)
+            dialog.run()
+            dialog.hide()
+            self._timetableWindow.clear()
+
+    def showFlights(self, menuItem):
+        """Callback for showing the flight list."""
+        if self._flightsWindow.hasFlights:
+            self._flightsWindow.show_all()
+        else:
+            self.beginBusy(xstr("acceptedflt_query_busy"))
+            self.webHandler.getAcceptedFlights(self._acceptedFlightsCallback)
+
+    def _acceptedFlightsCallback(self, returned, result):
+        """Called when the accepted flights have been received."""
+        gobject.idle_add(self._handleAcceptedFlights, returned, result)
+
+    def _handleAcceptedFlights(self, returned, result):
+        """Handle the result of the query for accepted flights."""
+        self.endBusy()
+        if returned:
+            self._flightsWindow.clear()
+            for flight in result.flights:
+                self._flightsWindow.addFlight(flight)
+            self._flightsWindow.show_all()
+        else:
+            dialog = gtk.MessageDialog(parent = self.mainWindow,
+                                       type = MESSAGETYPE_ERROR,
+                                       message_format = xstr("acceptedflt_failed"))
+            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+            dialog.set_title(WINDOW_TITLE_BASE)
+            dialog.run()
+            dialog.hide()
+
+    def _hideTimetableWindow(self, window, event):
+        """Hide the window of the timetable."""
+        self._timetableWindow.hide()
+        return True
+
+    def _hideFlightsWindow(self, window, event):
+        """Hide the window of the accepted flights."""
+        self._flightsWindow.hide()
+        return True
+
+    def _editChecklist(self, menuItem):
+        """Callback for editing the checklists."""
+        self._checklistEditor.run()
+
+    def _editApproachCallouts(self, menuItem):
+        """Callback for editing the approach callouts."""
+        self._approachCalloutsEditor.run()
+
+    def _editPreferences(self, menuItem):
+        """Callback for editing the preferences."""
+        self._clearHotkeys()
+        self._preferences.run(self.config)
+        self._setupTimeSync()
+        self._listenHotkeys()
+
+    def _reportBug(self, menuItem):
+        """Callback for reporting a bug."""
+        self._bugReportDialog.run()
+
+    def _setupTimeSync(self):
+        """Enable or disable the simulator time synchronization based on the
+        configuration."""
+        simulator = self._simulator
+        if simulator is not None:
+            if self.config.syncFSTime:
+                simulator.enableTimeSync()
+            else:
+                simulator.disableTimeSync()
+
+    def viewPIREP(self, pirep):
+        """Display the PIREP viewer window with the given PIREP."""
+        self._pirepViewer.setPIREP(pirep)
+        self._pirepViewer.show_all()
+        self._pirepViewer.run()
+        self._pirepViewer.hide()
+
+    def viewMessagedPIREP(self, pirep):
+        """Display the PIREP viewer window with the given PIREP containing
+        messages as well."""
+        self._messagedPIREPViewer.setPIREP(pirep)
+        self._messagedPIREPViewer.show_all()
+        self._messagedPIREPViewer.run()
+        self._messagedPIREPViewer.hide()
+
+    def editPIREP(self, pirep):
+        """Display the PIREP editor window and allow editing the PIREP."""
+        self._pirepEditor.setPIREP(pirep)
+        self._pirepEditor.show_all()
+        if self._pirepEditor.run()==RESPONSETYPE_OK:
+            self.beginBusy(xstr("pirepEdit_save_busy"))
+            self.webHandler.sendPIREP(self._pirepUpdatedCallback, pirep,
+                                      update = True)
+        else:
+            self._pirepEditor.hide()
+
+    def _pirepUpdatedCallback(self, returned, result):
+        """Callback for the PIREP updating result."""
+        gobject.idle_add(self._handlePIREPUpdated, returned, result)
+
+    def _handlePIREPUpdated(self, returned, result):
+        """Callback for the PIREP updating result."""
+        self.endBusy()
+        secondaryMarkup = None
+        type = MESSAGETYPE_ERROR
+        if returned:
+            if result.success:
+                type = None
+            elif result.alreadyFlown:
+                messageFormat = xstr("sendPIREP_already")
+                secondaryMarkup = xstr("sendPIREP_already_sec")
+            elif result.notAvailable:
+                messageFormat = xstr("sendPIREP_notavail")
+            else:
+                messageFormat = xstr("sendPIREP_unknown")
+                secondaryMarkup = xstr("sendPIREP_unknown_sec")
+        else:
+            print "PIREP sending failed", result
+            messageFormat = xstr("sendPIREP_failed")
+            secondaryMarkup = xstr("sendPIREP_failed_sec")
+
+        if type is not None:
+            dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
+                                       type = type, message_format = messageFormat)
+            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+            dialog.set_title(WINDOW_TITLE_BASE)
+            if secondaryMarkup is not None:
+                dialog.format_secondary_markup(secondaryMarkup)
+
+            dialog.run()
+            dialog.hide()
+
+        self._pirepEditor.hide()
+
+    def _loadPIREP(self, menuItem):
+        """Load a PIREP for sending."""
+        dialog = self._getLoadPirepDialog()
+
+        if self._lastLoadedPIREP:
+            dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
+        else:
+            pirepDirectory = self.config.pirepDirectory
+            if pirepDirectory is not None:
+                dialog.set_current_folder(pirepDirectory)
+
+        result = dialog.run()
+        dialog.hide()
+
+        if result==RESPONSETYPE_OK:
+            self._lastLoadedPIREP = text2unicode(dialog.get_filename())
+
+            pirep = PIREP.load(self._lastLoadedPIREP)
+            if pirep is None:
+                dialog = gtk.MessageDialog(parent = self._mainWindow,
+                                           type = MESSAGETYPE_ERROR,
+                                           message_format = xstr("loadPIREP_failed"))
+                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+                dialog.set_title(WINDOW_TITLE_BASE)
+                dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
+                dialog.run()
+                dialog.hide()
+            else:
+                dialog = self._getSendLoadedDialog(pirep)
+                dialog.show_all()
+                while True:
+                    result = dialog.run()
+
+                    if result==RESPONSETYPE_OK:
+                        self.sendPIREP(pirep)
+                    elif result==1:
+                        self.viewPIREP(pirep)
+                    else:
+                        break
+
+                dialog.hide()
+
+    def _getLoadPirepDialog(self):
+        """Get the PIREP loading file chooser dialog.
+
+        If it is not created yet, it will be created."""
+        if self._loadPIREPDialog is None:
+            dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
+                                           xstr("loadPIREP_browser_title"),
+                                           action = FILE_CHOOSER_ACTION_OPEN,
+                                           buttons = (gtk.STOCK_CANCEL,
+                                                      RESPONSETYPE_CANCEL,
+                                                      gtk.STOCK_OK, RESPONSETYPE_OK),
+                                           parent = self._mainWindow)
+            dialog.set_modal(True)
+
+
+            filter = gtk.FileFilter()
+            filter.set_name(xstr("file_filter_pireps"))
+            filter.add_pattern("*.pirep")
+            dialog.add_filter(filter)
+
+            filter = gtk.FileFilter()
+            filter.set_name(xstr("file_filter_all"))
+            filter.add_pattern("*.*")
+            dialog.add_filter(filter)
+
+            self._loadPIREPDialog = dialog
+
+        return self._loadPIREPDialog
+
+    def _getSendLoadedDialog(self, pirep):
+        """Get a dialog displaying the main information of the flight from the
+        PIREP and providing Cancel and Send buttons."""
+        dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
+                            xstr("loadPIREP_send_title"),
+                            parent = self._mainWindow,
+                            flags = DIALOG_MODAL)
+
+        contentArea = dialog.get_content_area()
+
+        label = gtk.Label(xstr("loadPIREP_send_help"))
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.set_padding(padding_top = 16, padding_bottom = 0,
+                              padding_left = 48, padding_right = 48)
+        alignment.add(label)
+        contentArea.pack_start(alignment, False, False, 8)
+
+        table = gtk.Table(5, 2)
+        tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
+                                   padding_left = 48, padding_right = 48)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        tableAlignment.add(table)
+        contentArea.pack_start(tableAlignment, True, True, 8)
+
+        bookedFlight = pirep.bookedFlight
+
+        label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
+        label.set_use_markup(True)
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 0, 1)
+
+        label = gtk.Label(bookedFlight.callsign)
+        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 1, 2, 0, 1)
+
+        label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
+        label.set_use_markup(True)
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 1, 2)
+
+        label = gtk.Label(str(bookedFlight.departureTime.date()))
+        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 1, 2, 1, 2)
+
+        label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
+        label.set_use_markup(True)
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 2, 3)
+
+        label = gtk.Label(bookedFlight.departureICAO)
+        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 1, 2, 2, 3)
+
+        label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
+        label.set_use_markup(True)
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 3, 4)
+
+        label = gtk.Label(bookedFlight.arrivalICAO)
+        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 1, 2, 3, 4)
+
+        label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
+        label.set_use_markup(True)
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 4, 5)
+
+        rating = pirep.rating
+        label = gtk.Label()
+        if rating<0:
+            label.set_markup('<b><span foreground="red">NO GO</span></b>')
+        else:
+            label.set_text("%.1f %%" % (rating,))
+
+        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 1, 2, 4, 5)
+
+        dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
+        dialog.add_button(xstr("viewPIREP"), 1)
+        dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
+
+        return dialog
+
+    def sendPIREP(self, pirep, callback = None):
+        """Send the given PIREP."""
+        self.beginBusy(xstr("sendPIREP_busy"))
+        self._sendPIREPCallback = callback
+        self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
+
+    def _pirepSentCallback(self, returned, result):
+        """Callback for the PIREP sending result."""
+        gobject.idle_add(self._handlePIREPSent, returned, result)
+
+    def _handlePIREPSent(self, returned, result):
+        """Callback for the PIREP sending result."""
+        self.endBusy()
+        secondaryMarkup = None
+        type = MESSAGETYPE_ERROR
+        if returned:
+            if result.success:
+                type = MESSAGETYPE_INFO
+                messageFormat = xstr("sendPIREP_success")
+                secondaryMarkup = xstr("sendPIREP_success_sec")
+            elif result.alreadyFlown:
+                messageFormat = xstr("sendPIREP_already")
+                secondaryMarkup = xstr("sendPIREP_already_sec")
+            elif result.notAvailable:
+                messageFormat = xstr("sendPIREP_notavail")
+            else:
+                messageFormat = xstr("sendPIREP_unknown")
+                secondaryMarkup = xstr("sendPIREP_unknown_sec")
+        else:
+            print "PIREP sending failed", result
+            messageFormat = xstr("sendPIREP_failed")
+            secondaryMarkup = xstr("sendPIREP_failed_sec")
+
+        dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
+                                   type = type, message_format = messageFormat)
+        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+        dialog.set_title(WINDOW_TITLE_BASE)
+        if secondaryMarkup is not None:
+            dialog.format_secondary_markup(secondaryMarkup)
+
+        dialog.run()
+        dialog.hide()
+
+        callback = self._sendPIREPCallback
+        self._sendPIREPCallback = None
+        if callback is not None:
+            callback(returned, result)
+
+    def sendBugReport(self, summary, description, email, callback = None):
+        """Send the bug report with the given data."""
+        description += "\n\n" + ("=" * 40)
+        description += "\n\nThe contents of the log:\n\n"
+
+        for (timestampString, text) in self._logger.lines:
+            description += unicode(formatFlightLogLine(timestampString, text))
+
+        description += "\n\n" + ("=" * 40)
+        description += "\n\nThe contents of the debug log:\n\n"
+
+        buffer = self._debugLogView.get_buffer()
+        description += buffer.get_text(buffer.get_start_iter(),
+                                       buffer.get_end_iter(), True)
+
+        self.beginBusy(xstr("sendBugReport_busy"))
+        self._sendBugReportCallback = callback
+        self.webHandler.sendBugReport(self._bugReportSentCallback,
+                                      summary, description, email)
+
+    def _cefInitialized(self):
+        """Called when CEF has been initialized."""
+        self._acars.start()
+        cef.initializeSimBrief()
+
+    def _bugReportSentCallback(self, returned, result):
+        """Callback function for the bug report sending result."""
+        gobject.idle_add(self._handleBugReportSent, returned, result)
+
+    def _handleBugReportSent(self, returned, result):
+        """Callback for the bug report sending result."""
+        self.endBusy()
+        secondaryMarkup = None
+        type = MESSAGETYPE_ERROR
+        if returned:
+            if result.success:
+                type = MESSAGETYPE_INFO
+                messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
+                secondaryMarkup = xstr("sendBugReport_success_sec")
+            else:
+                messageFormat = xstr("sendBugReport_error")
+                secondaryMarkup = xstr("sendBugReport_siteerror_sec")
+        else:
+            messageFormat = xstr("sendBugReport_error")
+            secondaryMarkup = xstr("sendBugReport_error_sec")
+
+        dialog = gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
+                                   type = type, message_format = messageFormat)
+        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+        dialog.set_title(WINDOW_TITLE_BASE)
+        if secondaryMarkup is not None:
+            dialog.format_secondary_markup(secondaryMarkup)
+
+        dialog.run()
+        dialog.hide()
+
+        callback = self._sendBugReportCallback
+        self._sendBugReportCallback = None
+        if callback is not None:
+            callback(returned, result)
+
+    def _listenHotkeys(self):
+        """Setup the hotkeys based on the configuration."""
+        if self._hotkeySetID is None and self._simulator is not None:
+            self._pilotHotkeyIndex = None
+            self._checklistHotkeyIndex = None
+
+            hotkeys = []
+
+            config = self.config
+            if config.enableSounds and config.pilotControlsSounds:
+                self._pilotHotkeyIndex = len(hotkeys)
+                hotkeys.append(config.pilotHotkey)
+
+            if config.enableChecklists:
+                self._checklistHotkeyIndex = len(hotkeys)
+                hotkeys.append(config.checklistHotkey)
+
+            if hotkeys:
+                self._hotkeySetID = \
+                    self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
+
+    def _clearHotkeys(self):
+        """Clear the hotkeys."""
+        if self._hotkeySetID is not None:
+            self._hotkeySetID=None
+            self._simulator.clearHotkeys()
+
+    def _handleHotkeys(self, id, hotkeys):
+        """Handle the hotkeys."""
+        if id==self._hotkeySetID:
+            for index in hotkeys:
+                if index==self._pilotHotkeyIndex:
+                    print "gui.GUI._handleHotkeys: pilot hotkey pressed"
+                    self._flight.pilotHotkeyPressed()
+                elif index==self._checklistHotkeyIndex:
+                    print "gui.GUI._handleHotkeys: checklist hotkey pressed"
+                    self._flight.checklistHotkeyPressed()
+                else:
+                    print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
+
+    def _showManual(self, menuitem):
+        """Show the user's manual."""
+        webbrowser.open(url ="file://" +
+                        os.path.join(self._programDirectory, "doc", "manual",
+                                     getLanguage(), "index.html"),
+                        new = 1)
+
+    def _showAbout(self, menuitem):
+        """Show the about dialog."""
+        dialog = self._getAboutDialog()
+        dialog.show_all()
+        dialog.run()
+        dialog.hide()
+
+    def _getAboutDialog(self):
+        """Get the about dialog.
+
+        If it does not exist yet, it will be created."""
+        if self._aboutDialog is None:
+            dialog = gtk.AboutDialog()
+            dialog.set_transient_for(self._mainWindow)
+            dialog.set_modal(True)
+
+            logoPath = os.path.join(self._programDirectory, "logo.png")
+            logo = pixbuf_new_from_file(logoPath)
+            dialog.set_logo(logo)
+
+            dialog.set_program_name(PROGRAM_NAME)
+            dialog.set_version(const.VERSION)
+            dialog.set_copyright("(c) 2012 by István Váradi")
+            dialog.set_website("http://mlx.varadiistvan.hu")
+            dialog.set_website_label(xstr("about_website"))
+
+            isHungarian = getLanguage()=="hu"
+            authors = []
+            for (familyName, firstName, role) in GUI._authors:
+                author = "%s %s" % \
+                         (familyName if isHungarian else firstName,
+                          firstName if isHungarian else familyName)
+                role = xstr("about_role_" + role)
+                authors.append(author + " (" + role + ")")
+            dialog.set_authors(authors)
+
+            dialog.set_license(xstr("about_license"))
+
+            if not pygobject:
+                gtk.about_dialog_set_url_hook(self._showAboutURL, None)
+
+            self._aboutDialog = dialog
+
+        return self._aboutDialog
+
+    def _showAboutURL(self, dialog, link, user_data):
+        """Show the about URL."""
+        webbrowser.open(url = link, new = 1)
+
+    def _setTakeoffAntiIceOn(self, value):
+        """Set the anti-ice on indicator."""
+        self._wizard.takeoffAntiIceOn = value
+
+    def _setLandingAntiIceOn(self, value):
+        """Set the anti-ice on indicator."""
+        self._wizard.landingAntiIceOn = value
+
+    def _getCredentialsCallback(self):
+        """Called when the web handler asks for the credentials."""
+        # FIXME: this is almost the same as
+        # SimBriefSetupPage._getCredentialsCallback
+        with self._credentialsCondition:
+            self._credentialsAvailable = False
+
+            gobject.idle_add(self._getCredentials)
+
+            while not self._credentialsAvailable:
+                self._credentialsCondition.wait()
+
+            return (self._credentialsUserName, self._credentialsPassword)
+
+    def _getCredentials(self):
+        """Get the credentials."""
+        # FIXME: this is almost the same as
+        # SimBriefSetupPage._getCredentials
+        with self._credentialsCondition:
+            config = self.config
+
+            dialog = CredentialsDialog(self, config.pilotID, config.password,
+                                       xstr("login_title"),
+                                       xstr("button_cancel"),
+                                       xstr("button_ok"),
+                                       xstr("label_pilotID"),
+                                       xstr("login_pilotID_tooltip"),
+                                       xstr("label_password"),
+                                       xstr("login_password_tooltip"),
+                                       xstr("login_info"),
+                                       config.rememberPassword,
+                                       xstr("remember_password"),
+                                       xstr("login_remember_tooltip"))
+            response = dialog.run()
+
+            if response==RESPONSETYPE_OK:
+                self._credentialsUserName = dialog.userName
+                self._credentialsPassword = dialog.password
+                rememberPassword = dialog.rememberPassword
+
+                config.pilotID = self._credentialsUserName
+
+                config.password = \
+                  self._credentialsPassword if rememberPassword else ""
+                config.rememberPassword = rememberPassword
+
+                config.save()
+            else:
+                self._credentialsUserName = None
+                self._credentialsPassword = None
+
+            self._credentialsAvailable = True
+            self._credentialsCondition.notify()
+
+    def _updateDone(self):
+        """Called when the update is done.
+
+        It checks if we already know the PID, and if not, asks the user whether
+        to register."""
+        if not self.config.pilotID and not self.config.password:
+            dialog = gtk.MessageDialog(parent = self._mainWindow,
+                                       type = MESSAGETYPE_QUESTION,
+                                       message_format = xstr("register_ask"))
+
+            dialog.set_title(WINDOW_TITLE_BASE)
+            dialog.format_secondary_markup(xstr("register_ask_sec"))
+
+            dialog.add_button(xstr("button_cancel"), 0)
+            dialog.add_button(xstr("button_register"), 1)
+            dialog.set_default_response(1)
+
+            result = dialog.run()
+            dialog.hide()
+            if result == 1:
+                self._wizard.jumpPage("register")
Index: src/mlx/gui/gui.py
===================================================================
--- src/mlx/gui/gui.py	(revision 859)
+++ 	(revision )
@@ -1,1946 +1,0 @@
-# -*- coding: utf-8 -*-
-
-from statusicon import StatusIcon
-from statusbar import Statusbar
-from info import FlightInfo
-from update import Updater
-from mlx.gui.common import *
-from mlx.gui.flight import Wizard
-from mlx.gui.monitor import MonitorWindow
-from mlx.gui.weighthelp import WeightHelp
-from mlx.gui.gates import FleetGateStatus
-from mlx.gui.prefs import Preferences
-from mlx.gui.checklist import ChecklistEditor
-from mlx.gui.callouts import ApproachCalloutsEditor
-from mlx.gui.flightlist import AcceptedFlightsWindow
-from mlx.gui.pirep import PIREPViewer, PIREPEditor
-from mlx.gui.bugreport import BugReportDialog
-from mlx.gui.acars import ACARS
-from mlx.gui.timetable import TimetableWindow
-import cef
-
-import mlx.const as const
-import mlx.fs as fs
-import mlx.flight as flight
-import mlx.logger as logger
-import mlx.acft as acft
-import mlx.web as web
-import mlx.singleton as singleton
-import mlx.airports as airports
-from  mlx.i18n import xstr, getLanguage
-from mlx.pirep import PIREP
-
-import time
-import threading
-import sys
-import datetime
-import webbrowser
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.gui
-#
-# The main GUI class.
-#
-# The \ref GUI class is the main class of the GUI. It is a connection listener,
-# and aggregates all the windows, the menu, etc. It maintains the connection to
-# the simulator as well as the flight object.
-
-#------------------------------------------------------------------------------
-
-class GUI(fs.ConnectionListener):
-    """The main GUI class."""
-    _authors = [ (u"Váradi", u"István", "prog_test"),
-                 (u"Galyassy", u"Tamás", "negotiation"),
-                 (u"Kurják", u"Ákos", "test"),
-                 (u"Nagy", u"Dániel", "test"),
-                 (u"Radó", u"Iván", "test"),
-                 (u"Petrovszki", u"Gábor", "test"),
-                 (u"Serfőző", u"Tamás", "test"),
-                 (u"Szebenyi", u"Bálint", "test"),
-                 (u"Zsebényi-Loksa", u"Gergely", "test") ]
-
-    def __init__(self, programDirectory, config):
-        """Construct the GUI."""
-        gobject.threads_init()
-
-        self._programDirectory = programDirectory
-        self.config = config
-        self._connecting = False
-        self._reconnecting = False
-        self._connected = False
-        self._logger = logger.Logger(self)
-        self._flight = None
-        self._simulator = None
-        self._fsType = None
-        self._monitoring = False
-
-        self._fleet = None
-
-        self._fleetCallback = None
-
-        self._updatePlaneCallback = None
-        self._updatePlaneTailNumber = None
-        self._updatePlaneStatus = None
-        self._updatePlaneGateNumber = None
-
-        self._stdioLock = threading.Lock()
-        self._stdioText = ""
-        self._stdioStartingLine = True
-
-        self._sendPIREPCallback = None
-        self._sendBugReportCallback = None
-
-        self._credentialsCondition = threading.Condition()
-        self._credentialsAvailable = False
-        self._credentialsUserName = None
-        self._credentialsPassword = None
-
-        self._bookFlightsUserCallback = None
-        self._bookFlightsBusyCallback = None
-
-        self.webHandler = web.Handler(config, self._getCredentialsCallback)
-        self.webHandler.start()
-
-        self.toRestart = False
-
-    def build(self, iconDirectory):
-        """Build the GUI."""
-
-        self._mainWindow = window = gtk.Window()
-        window.set_title(WINDOW_TITLE_BASE)
-        window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
-        window.set_resizable(False)
-        window.connect("delete-event", self.deleteMainWindow)
-        window.connect("window-state-event", self._handleMainWindowState)
-        if os.name=="nt":
-            window.connect("leave-notify-event", self._handleLeaveNotify)
-        accelGroup = gtk.AccelGroup()
-        window.add_accel_group(accelGroup)
-        window.realize()
-
-        mainVBox = gtk.VBox()
-        window.add(mainVBox)
-
-        self._preferences = Preferences(self)
-        self._timetableWindow = TimetableWindow(self)
-        self._timetableWindow.connect("delete-event", self._hideTimetableWindow)
-        self._flightsWindow = AcceptedFlightsWindow(self)
-        self._flightsWindow.connect("delete-event", self._hideFlightsWindow)
-        self._checklistEditor = ChecklistEditor(self)
-        self._approachCalloutsEditor = ApproachCalloutsEditor(self)
-        self._bugReportDialog = BugReportDialog(self)
-
-        menuBar = self._buildMenuBar(accelGroup)
-        mainVBox.pack_start(menuBar, False, False, 0)
-
-        self._notebook = gtk.Notebook()
-        mainVBox.pack_start(self._notebook, True, True, 4)
-
-        self._wizard = Wizard(self)
-        label = gtk.Label(xstr("tab_flight"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("tab_flight_tooltip"))
-        self._notebook.append_page(self._wizard, label)
-
-        self._flightInfo = FlightInfo(self)
-        label = gtk.Label(xstr("tab_flight_info"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
-        self._notebook.append_page(self._flightInfo, label)
-        self._flightInfo.disable()
-
-        self._weightHelp = WeightHelp(self)
-        label = gtk.Label(xstr("tab_weight_help"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
-        self._notebook.append_page(self._weightHelp, label)
-
-        (logWidget, self._logView)  = self._buildLogWidget()
-        addFaultTag(self._logView.get_buffer())
-        label = gtk.Label(xstr("tab_log"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("tab_log_tooltip"))
-        self._notebook.append_page(logWidget, label)
-
-        self._fleetGateStatus = FleetGateStatus(self)
-        label = gtk.Label(xstr("tab_gates"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("tab_gates_tooltip"))
-        self._notebook.append_page(self._fleetGateStatus, label)
-
-        self._acars = ACARS(self)
-        label = gtk.Label("ACARS")
-        label.set_use_underline(True)
-        self._notebook.append_page(self._acars, label)
-
-        (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
-        self._debugLogWidget.show_all()
-
-        mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
-
-        self._statusbar = Statusbar(iconDirectory)
-        mainVBox.pack_start(self._statusbar, False, False, 0)
-
-        self._notebook.connect("switch-page", self._notebookPageSwitch)
-
-        self._monitorWindow = MonitorWindow(self, iconDirectory)
-        self._monitorWindow.add_accel_group(accelGroup)
-        self._monitorWindowX = None
-        self._monitorWindowY = None
-        self._selfToggling = False
-
-        self._pirepViewer = PIREPViewer(self)
-        self._messagedPIREPViewer = PIREPViewer(self, showMessages = True)
-
-        self._pirepEditor = PIREPEditor(self)
-
-        window.show_all()
-        if os.name=="nt":
-            window.get_window().focus()
-
-        self._wizard.grabDefault()
-        self._weightHelp.reset()
-        self._weightHelp.disable()
-
-        self._statusIcon = StatusIcon(iconDirectory, self)
-
-        self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
-                                      else gdk.WATCH)
-
-        self._loadPIREPDialog = None
-        self._lastLoadedPIREP = None
-
-        self._hotkeySetID = None
-        self._pilotHotkeyIndex = None
-        self._checklistHotkeyIndex = None
-
-        self._aboutDialog = None
-
-    @property
-    def mainWindow(self):
-        """Get the main window of the GUI."""
-        return self._mainWindow
-
-    @property
-    def logger(self):
-        """Get the logger used by us."""
-        return self._logger
-
-    @property
-    def simulator(self):
-        """Get the simulator used by us."""
-        return self._simulator
-
-    @property
-    def flight(self):
-        """Get the flight being performed."""
-        return self._flight
-
-    @property
-    def fsType(self):
-        """Get the flight simulator type."""
-        return self._fsType
-
-    @property
-    def entranceExam(self):
-        """Get whether an entrance exam is about to be taken."""
-        return self._wizard.entranceExam
-
-    @property
-    def loggedIn(self):
-        """Indicate if the user has logged in properly."""
-        return self._wizard.loggedIn
-
-    @property
-    def loginResult(self):
-        """Get the result of the login."""
-        return self._wizard.loginResult
-
-    @property
-    def bookedFlight(self):
-        """Get the booked flight selected, if any."""
-        return self._wizard.bookedFlight
-
-    @property
-    def numCrew(self):
-        """Get the number of crew members."""
-        return self._wizard.numCrew
-
-    @property
-    def numPassengers(self):
-        """Get the number of passengers."""
-        return self._wizard.numPassengers
-
-    @property
-    def bagWeight(self):
-        """Get the bag weight."""
-        return self._wizard.bagWeight
-
-    @property
-    def cargoWeight(self):
-        """Get the cargo weight."""
-        return self._wizard.cargoWeight
-
-    @property
-    def mailWeight(self):
-        """Get the mail weight."""
-        return self._wizard.mailWeight
-
-    @property
-    def zfw(self):
-        """Get Zero-Fuel Weight calculated for the current flight."""
-        return self._wizard.zfw
-
-    @property
-    def filedCruiseAltitude(self):
-        """Get cruise altitude filed for the current flight."""
-        return self._wizard.filedCruiseAltitude
-
-    @property
-    def cruiseAltitude(self):
-        """Get cruise altitude set for the current flight."""
-        return self._wizard.cruiseAltitude
-
-    @property
-    def loggableCruiseAltitude(self):
-        """Get the cruise altitude that can be logged."""
-        return self._wizard.loggableCruiseAltitude
-
-    @property
-    def route(self):
-        """Get the flight route."""
-        return self._wizard.route
-
-    @property
-    def departureMETAR(self):
-        """Get the METAR of the deprature airport."""
-        return self._wizard.departureMETAR
-
-    @property
-    def arrivalMETAR(self):
-        """Get the METAR of the deprature airport."""
-        return self._wizard.arrivalMETAR
-
-    @property
-    def departureRunway(self):
-        """Get the name of the departure runway."""
-        return self._wizard.departureRunway
-
-    @property
-    def sid(self):
-        """Get the SID."""
-        return self._wizard.sid
-
-    @property
-    def v1(self):
-        """Get the V1 speed calculated for the flight."""
-        return self._wizard.v1
-
-    @property
-    def vr(self):
-        """Get the Vr speed calculated for the flight."""
-        return self._wizard.vr
-
-    @property
-    def v2(self):
-        """Get the V2 speed calculated for the flight."""
-        return self._wizard.v2
-
-    @property
-    def derate(self):
-        """Get the derate value calculated for the flight."""
-        return self._wizard.derate
-
-    @property
-    def takeoffAntiIceOn(self):
-        """Get whether the anti-ice system was on during take-off."""
-        return self._wizard.takeoffAntiIceOn
-
-    @takeoffAntiIceOn.setter
-    def takeoffAntiIceOn(self, value):
-        """Set the anti-ice on indicator."""
-        gobject.idle_add(self._setTakeoffAntiIceOn, value)
-
-    @property
-    def rtoIndicated(self):
-        """Get whether the pilot has indicated than an RTO has occured."""
-        return self._wizard.rtoIndicated
-
-    @property
-    def arrivalRunway(self):
-        """Get the arrival runway."""
-        return self._wizard.arrivalRunway
-
-    @property
-    def star(self):
-        """Get the STAR."""
-        return self._wizard.star
-
-    @property
-    def transition(self):
-        """Get the transition."""
-        return self._wizard.transition
-
-    @property
-    def approachType(self):
-        """Get the approach type."""
-        return self._wizard.approachType
-
-    @property
-    def vref(self):
-        """Get the Vref speed calculated for the flight."""
-        return self._wizard.vref
-
-    @property
-    def landingAntiIceOn(self):
-        """Get whether the anti-ice system was on during landing."""
-        return self._wizard.landingAntiIceOn
-
-    @landingAntiIceOn.setter
-    def landingAntiIceOn(self, value):
-        """Set the anti-ice on indicator."""
-        gobject.idle_add(self._setLandingAntiIceOn, value)
-
-    @property
-    def flightType(self):
-        """Get the flight type."""
-        return self._wizard.flightType
-
-    @property
-    def online(self):
-        """Get whether the flight was online or not."""
-        return self._wizard.online
-
-    @property
-    def comments(self):
-        """Get the comments."""
-        return self._flightInfo.comments
-
-    @property
-    def hasComments(self):
-        """Indicate whether there is a comment."""
-        return self._flightInfo.hasComments
-
-    @property
-    def flightDefects(self):
-        """Get the flight defects."""
-        return self._flightInfo.faultsAndExplanations
-
-    @property
-    def delayCodes(self):
-        """Get the delay codes."""
-        return self._flightInfo.delayCodes
-
-    @property
-    def hasDelayCode(self):
-        """Determine if there is at least one delay code selected."""
-        return self._flightInfo.hasDelayCode
-
-    @property
-    def faultsFullyExplained(self):
-        """Determine if all the faults have been fully explained by the
-        user."""
-        return self._flightInfo.faultsFullyExplained
-
-    if pygobject:
-        @property
-        def backgroundColour(self):
-            """Get the background colour of the main window."""
-            return self._mainWindow.get_style_context().\
-              get_background_color(gtk.StateFlags.NORMAL)
-
-    def run(self):
-        """Run the GUI."""
-        if self.config.autoUpdate:
-            self._updater = Updater(self,
-                                    self._programDirectory,
-                                    self.config.updateURL,
-                                    self._mainWindow)
-            self._updater.start()
-        else:
-            self.updateDone()
-
-        cef.initialize(self._cefInitialized)
-
-        singleton.raiseCallback = self.raiseCallback
-        gtk.main()
-        singleton.raiseCallback = None
-
-        cef.finalize()
-
-        self._disconnect()
-
-    def updateDone(self):
-        """Called when the update is done (and there is no need to restart)."""
-        gobject.idle_add(self._updateDone)
-
-    def connected(self, fsType, descriptor):
-        """Called when we have connected to the simulator."""
-        self._connected = True
-        self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
-                                    (const.VERSION, descriptor))
-        fs.sendMessage(const.MESSAGETYPE_INFORMATION,
-                       "Welcome to MAVA Logger X " + const.VERSION)
-        gobject.idle_add(self._handleConnected, fsType, descriptor)
-
-    def _handleConnected(self, fsType, descriptor):
-        """Called when the connection to the simulator has succeeded."""
-        self._statusbar.updateConnection(self._connecting, self._connected)
-        self.endBusy()
-        if not self._reconnecting:
-            self._wizard.connected(fsType, descriptor)
-        self._reconnecting = False
-        self._fsType = fsType
-        self._listenHotkeys()
-
-    def connectionFailed(self):
-        """Called when the connection failed."""
-        self._logger.untimedMessage("Connection to the simulator failed")
-        gobject.idle_add(self._connectionFailed)
-
-    def _connectionFailed(self):
-        """Called when the connection failed."""
-        self.endBusy()
-        self._statusbar.updateConnection(self._connecting, self._connected)
-
-        dialog = gtk.MessageDialog(parent = self._mainWindow,
-                                   type = MESSAGETYPE_ERROR,
-                                   message_format = xstr("conn_failed"))
-
-        dialog.set_title(WINDOW_TITLE_BASE)
-        dialog.format_secondary_markup(xstr("conn_failed_sec"))
-
-        dialog.add_button(xstr("button_cancel"), 0)
-        dialog.add_button(xstr("button_tryagain"), 1)
-        dialog.set_default_response(1)
-
-        result = dialog.run()
-        dialog.hide()
-        if result == 1:
-            self.beginBusy(xstr("connect_busy"))
-            self._simulator.reconnect()
-        else:
-            self.reset()
-
-    def disconnected(self):
-        """Called when we have disconnected from the simulator."""
-        self._connected = False
-        self._logger.untimedMessage("Disconnected from the simulator")
-        if self._flight is not None:
-            self._flight.disconnected()
-
-        gobject.idle_add(self._disconnected)
-
-    def _disconnected(self):
-        """Called when we have disconnected from the simulator unexpectedly."""
-        self._statusbar.updateConnection(self._connecting, self._connected)
-
-        dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
-                                   message_format = xstr("conn_broken"),
-                                   parent = self._mainWindow)
-        dialog.set_title(WINDOW_TITLE_BASE)
-        dialog.format_secondary_markup(xstr("conn_broken_sec"))
-
-        dialog.add_button(xstr("button_cancel"), 0)
-        dialog.add_button(xstr("button_reconnect"), 1)
-        dialog.set_default_response(1)
-
-        result = dialog.run()
-        dialog.hide()
-        if result == 1:
-            self.beginBusy(xstr("connect_busy"))
-            self._reconnecting = True
-            self._simulator.reconnect()
-        else:
-            self.reset()
-
-    def enableFlightInfo(self, aircraftType):
-        """Enable the flight info tab."""
-        self._flightInfo.enable(aircraftType)
-
-    def bookFlights(self, callback, flightIDs, date, tailNumber,
-                    busyCallback = None):
-        """Initiate the booking of flights with the given timetable IDs and
-        other data"""
-        self._bookFlightsUserCallback = callback
-        self._bookFlightsBusyCallback = busyCallback
-
-        self.beginBusy(xstr("bookflights_busy"))
-        if busyCallback is not None:
-            busyCallback(True)
-
-        self.webHandler.bookFlights(self._bookFlightsCallback,
-                                    flightIDs, date, tailNumber)
-
-    def _bookFlightsCallback(self, returned, result):
-        """Called when the booking of flights has finished."""
-        gobject.idle_add(self._handleBookFlightsResult, returned, result)
-
-    def _handleBookFlightsResult(self, returned, result):
-        """Called when the booking of flights is done.
-
-        If it was successful, the booked flights are added to the list of the
-        flight selector."""
-        if self._bookFlightsBusyCallback is not None:
-            self._bookFlightsBusyCallback(False)
-        self.endBusy()
-
-        if returned:
-            for bookedFlight in result.bookedFlights:
-                self._wizard.addFlight(bookedFlight)
-
-        self._bookFlightsUserCallback(returned, result)
-
-    def cancelFlight(self):
-        """Cancel the current file, if the user confirms it."""
-        dialog = gtk.MessageDialog(parent = self._mainWindow,
-                                   type = MESSAGETYPE_QUESTION,
-                                   message_format = xstr("cancelFlight_question"))
-
-        dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
-        dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
-
-        dialog.set_title(WINDOW_TITLE_BASE)
-        result = dialog.run()
-        dialog.hide()
-
-        if result==RESPONSETYPE_YES:
-            self.reset()
-
-    def reset(self):
-        """Reset the GUI."""
-        self._disconnect()
-
-        self._simulator = None
-
-        self._flightInfo.reset()
-        self._flightInfo.disable()
-        self.resetFlightStatus()
-
-        self._weightHelp.reset()
-        self._weightHelp.disable()
-        self._notebook.set_current_page(0)
-
-        self._logView.get_buffer().set_text("")
-
-        if self.loggedIn:
-            self._wizard.cancelFlight(self._handleReloadResult)
-        else:
-            self._wizard.reset(None)
-
-    def _handleReloadResult(self, returned, result):
-        """Handle the result of the reloading of the flights."""
-        self._wizard.reset(result if returned and result.loggedIn else None)
-
-    def _disconnect(self, closingMessage = None, duration = 3):
-        """Disconnect from the simulator if connected."""
-        self.stopMonitoring()
-        self._clearHotkeys()
-
-        if self._connected:
-            if closingMessage is None:
-                self._flight.simulator.disconnect()
-            else:
-                fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
-                               closingMessage, duration,
-                               disconnect = True)
-            self._connected = False
-
-        self._connecting = False
-        self._reconnecting = False
-        self._statusbar.updateConnection(False, False)
-        self._weightHelp.disable()
-
-        return True
-
-    def insertFlightLogLine(self, index, timestampString, text, isFault):
-        """Insert the flight log line with the given data."""
-        gobject.idle_add(self._insertFlightLogLine, index,
-                         formatFlightLogLine(timestampString, text),
-                         isFault)
-
-    def _insertFlightLogLine(self, index, line, isFault):
-        """Perform the real insertion.
-
-        To be called from the event loop."""
-        buffer = self._logView.get_buffer()
-        lineIter = buffer.get_iter_at_line(index)
-        insertTextBuffer(buffer, lineIter, line, isFault = isFault)
-        self._logView.scroll_mark_onscreen(buffer.get_insert())
-
-    def removeFlightLogLine(self, index):
-        """Remove the flight log line with the given index."""
-        gobject.idle_add(self._removeFlightLogLine, index)
-
-    def addFault(self, id, timestampString, text):
-        """Add a fault to the list of faults."""
-        faultText = formatFlightLogLine(timestampString, text).strip()
-        gobject.idle_add(self._flightInfo.addFault, id, faultText)
-
-    def updateFault(self, id, timestampString, text):
-        """Update a fault in the list of faults."""
-        faultText = formatFlightLogLine(timestampString, text).strip()
-        gobject.idle_add(self._flightInfo.updateFault, id, faultText)
-
-    def clearFault(self, id):
-        """Clear a fault in the list of faults."""
-        gobject.idle_add(self._flightInfo.clearFault, id)
-
-    def _removeFlightLogLine(self, index):
-        """Perform the real removal."""
-        buffer = self._logView.get_buffer()
-        startIter = buffer.get_iter_at_line(index)
-        endIter = buffer.get_iter_at_line(index+1)
-        buffer.delete(startIter, endIter)
-        self._logView.scroll_mark_onscreen(buffer.get_insert())
-
-    def check(self, flight, aircraft, logger, oldState, state):
-        """Update the data."""
-        gobject.idle_add(self._monitorWindow.setData, state)
-        gobject.idle_add(self._statusbar.updateTime, state.timestamp)
-
-    def resetFlightStatus(self):
-        """Reset the status of the flight."""
-        self._statusbar.resetFlightStatus()
-        self._statusbar.updateTime()
-        self._statusIcon.resetFlightStatus()
-
-    def setStage(self, stage):
-        """Set the stage of the flight."""
-        gobject.idle_add(self._setStage, stage)
-
-    def _setStage(self, stage):
-        """Set the stage of the flight."""
-        self._statusbar.setStage(stage)
-        self._statusIcon.setStage(stage)
-        self._wizard.setStage(stage)
-        if stage==const.STAGE_END:
-            welcomeMessage = \
-                airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
-            self._disconnect(closingMessage =
-                             "Flight plan closed. " + welcomeMessage,
-                             duration = 5)
-
-    def setRating(self, rating):
-        """Set the rating of the flight."""
-        gobject.idle_add(self._setRating, rating)
-
-    def _setRating(self, rating):
-        """Set the rating of the flight."""
-        self._statusbar.setRating(rating)
-        self._statusIcon.setRating(rating)
-
-    def setNoGo(self, reason):
-        """Set the rating of the flight to No-Go with the given reason."""
-        gobject.idle_add(self._setNoGo, reason)
-
-    def _setNoGo(self, reason):
-        """Set the rating of the flight."""
-        self._statusbar.setNoGo(reason)
-        self._statusIcon.setNoGo(reason)
-
-    def _handleMainWindowState(self, window, event):
-        """Hande a change in the state of the window"""
-        iconified = gdk.WindowState.ICONIFIED if pygobject \
-                    else gdk.WINDOW_STATE_ICONIFIED
-
-        if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
-            if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
-                self._statusIcon.mainWindowHidden()
-            else:
-                self._statusIcon.mainWindowShown()
-
-        if self.config.hideMinimizedWindow and not pygobject and \
-           (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
-           (event.new_window_state&WINDOW_STATE_ICONIFIED)!=0:
-            self.hideMainWindow(savePosition = False)
-        elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
-             (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
-            self._mainWindow.present()
-
-    def _handleLeaveNotify(self, widget, event):
-        """Handle the leave-notify event.
-
-        Here we reset the focus to the main window as CEF might have acquired
-        it earlier."""
-        self._mainWindow.get_window().focus()
-
-    def raiseCallback(self):
-        """Callback for the singleton handling code."""
-        gobject.idle_add(self.raiseMainWindow)
-
-    def raiseMainWindow(self):
-        """Show the main window if invisible, and raise it."""
-        if not self._mainWindow.get_visible():
-            self.showMainWindow()
-        self._mainWindow.present()
-
-    def deleteMainWindow(self, window, event):
-        """Handle the delete event for the main window."""
-        if self.config.quitOnClose:
-            self._quit()
-        else:
-            self.hideMainWindow()
-        return True
-
-    def hideMainWindow(self, savePosition = True):
-        """Hide the main window and save its position."""
-        if savePosition:
-            (self._mainWindowX, self._mainWindowY) = \
-                 self._mainWindow.get_window().get_root_origin()
-        else:
-            self._mainWindowX = self._mainWindowY = None
-        self._mainWindow.hide()
-        return True
-
-    def showMainWindow(self):
-        """Show the main window at its former position."""
-        if self._mainWindowX is not None and self._mainWindowY is not None:
-            self._mainWindow.move(self._mainWindowX, self._mainWindowY)
-
-        if pygobject:
-            self._mainWindow.show()
-        else:
-            self._mainWindow.present()
-        self._mainWindow.deiconify()
-
-    def toggleMainWindow(self):
-        """Toggle the main window."""
-        if self._mainWindow.get_visible():
-            self.hideMainWindow()
-        else:
-            self.showMainWindow()
-
-    def hideMonitorWindow(self, savePosition = True):
-        """Hide the monitor window."""
-        if savePosition:
-            (self._monitorWindowX, self._monitorWindowY) = \
-                 self._monitorWindow.get_window().get_root_origin()
-        else:
-            self._monitorWindowX = self._monitorWindowY = None
-        self._monitorWindow.hide()
-        self._statusIcon.monitorWindowHidden()
-        if self._showMonitorMenuItem.get_active():
-            self._selfToggling = True
-            self._showMonitorMenuItem.set_active(False)
-        return True
-
-    def showMonitorWindow(self):
-        """Show the monitor window."""
-        if self._monitorWindowX is not None and self._monitorWindowY is not None:
-            self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
-        self._monitorWindow.show_all()
-        self._statusIcon.monitorWindowShown()
-        if not self._showMonitorMenuItem.get_active():
-            self._selfToggling = True
-            self._showMonitorMenuItem.set_active(True)
-
-    def _toggleMonitorWindow(self, menuItem):
-        if self._selfToggling:
-            self._selfToggling = False
-        elif self._monitorWindow.get_visible():
-            self.hideMonitorWindow()
-        else:
-            self.showMonitorWindow()
-
-    def restart(self):
-        """Quit and restart the application."""
-        self.toRestart = True
-        self._quit(force = True)
-
-    def flushStdIO(self):
-        """Flush any text to the standard error that could not be logged."""
-        if self._stdioText:
-            sys.__stderr__.write(self._stdioText)
-
-    def writeStdIO(self, text):
-        """Write the given text into standard I/O log."""
-        with self._stdioLock:
-            self._stdioText += text
-
-        gobject.idle_add(self._writeStdIO)
-
-    def beginBusy(self, message):
-        """Begin a period of background processing."""
-        self._wizard.set_sensitive(False)
-        self._weightHelp.set_sensitive(False)
-        self._mainWindow.get_window().set_cursor(self._busyCursor)
-        self._statusbar.updateBusyState(message)
-
-    def updateBusyState(self, message):
-        """Update the busy state."""
-        self._statusbar.updateBusyState(message)
-
-    def endBusy(self):
-        """End a period of background processing."""
-        self._mainWindow.get_window().set_cursor(None)
-        self._weightHelp.set_sensitive(True)
-        self._wizard.set_sensitive(True)
-        self._statusbar.updateBusyState(None)
-
-    def initializeWeightHelp(self):
-        """Initialize the weight help tab."""
-        self._weightHelp.reset()
-        self._weightHelp.enable()
-
-    def getFleetAsync(self, callback = None, force = None):
-        """Get the fleet asynchronously."""
-        gobject.idle_add(self.getFleet, callback, force)
-
-    def getFleet(self, callback = None, force = False, busyCallback = None):
-        """Get the fleet.
-
-        If force is False, and we already have a fleet retrieved,
-        that one will be used."""
-        if self._fleet is None or force:
-            self._fleetCallback = callback
-            self._fleetBusyCallback = busyCallback
-            if busyCallback is not None:
-                busyCallback(True)
-            self.beginBusy(xstr("fleet_busy"))
-            self.webHandler.getFleet(self._fleetResultCallback)
-        else:
-            callback(self._fleet)
-
-    def commentsChanged(self):
-        """Indicate that the comments have changed."""
-        self._wizard.commentsChanged()
-
-    def delayCodesChanged(self):
-        """Called when the delay codes have changed."""
-        self._wizard.delayCodesChanged()
-
-    def faultExplanationsChanged(self):
-        """Called when the status of the explanations of the faults have
-        changed."""
-        self._wizard.faultExplanationsChanged()
-
-    def updateRTO(self, inLoop = False):
-        """Indicate that the RTO state should be updated."""
-        if inLoop:
-            self._wizard.updateRTO()
-        else:
-            gobject.idle_add(self.updateRTO, True)
-
-    def rtoToggled(self, indicated):
-        """Called when the user has toggled the RTO checkbox."""
-        self._flight.rtoToggled(indicated)
-
-    def _fleetResultCallback(self, returned, result):
-        """Called when the fleet has been queried."""
-        gobject.idle_add(self._handleFleetResult, returned, result)
-
-    def _handleFleetResult(self, returned, result):
-        """Handle the fleet result."""
-        self.endBusy()
-        if self._fleetBusyCallback is not None:
-            self._fleetBusyCallback(False)
-        if returned:
-            self._fleet = result.fleet
-        else:
-            self._fleet = None
-
-            dialog = gtk.MessageDialog(parent = self.mainWindow,
-                                       type = MESSAGETYPE_ERROR,
-                                       message_format = xstr("fleet_failed"))
-            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-            dialog.set_title(WINDOW_TITLE_BASE)
-            dialog.run()
-            dialog.hide()
-
-        callback = self._fleetCallback
-        self._fleetCallback = None
-        self._fleetBusyCallback = None
-        if  callback is not None:
-            callback(self._fleet)
-        self._fleetGateStatus.handleFleet(self._fleet)
-
-    def updatePlane(self, tailNumber, status,
-                    gateNumber = None, callback = None):
-        """Update the status of the given plane."""
-        self.beginBusy(xstr("fleet_update_busy"))
-
-        self._updatePlaneCallback = callback
-
-        self._updatePlaneTailNumber = tailNumber
-        self._updatePlaneStatus = status
-        self._updatePlaneGateNumber = gateNumber
-
-        self.webHandler.updatePlane(self._updatePlaneResultCallback,
-                                    tailNumber, status, gateNumber)
-
-    def _updatePlaneResultCallback(self, returned, result):
-        """Called when the status of a plane has been updated."""
-        gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
-
-    def _handleUpdatePlaneResult(self, returned, result):
-        """Handle the plane update result."""
-        self.endBusy()
-        if returned:
-            success = result.success
-            if success:
-                if self._fleet is not None:
-                    self._fleet.updatePlane(self._updatePlaneTailNumber,
-                                            self._updatePlaneStatus,
-                                            self._updatePlaneGateNumber)
-                    self._fleetGateStatus.handleFleet(self._fleet)
-        else:
-            dialog = gtk.MessageDialog(parent = self.mainWindow,
-                                       type = MESSAGETYPE_ERROR,
-                                       message_format = xstr("fleet_update_failed"))
-            dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
-            dialog.set_title(WINDOW_TITLE_BASE)
-            dialog.run()
-            dialog.hide()
-
-            success = None
-
-        callback = self._updatePlaneCallback
-        self._updatePlaneCallback = None
-        if callback is not None:
-            callback(success)
-
-    def _writeStdIO(self):
-        """Perform the real writing."""
-        with self._stdioLock:
-            text = self._stdioText
-            self._stdioText = ""
-        if not text: return
-
-        lines = text.splitlines()
-        if text[-1]=="\n":
-            text = ""
-        else:
-            text = lines[-1]
-            lines = lines[:-1]
-
-        now = datetime.datetime.now()
-        timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
-
-        for line in lines:
-            #print >> sys.__stdout__, line
-            if self._stdioStartingLine:
-                self._writeLog(timeStr, self._debugLogView)
-            self._writeLog(line + "\n", self._debugLogView)
-            self._stdioStartingLine = True
-
-        if text:
-            #print >> sys.__stdout__, text,
-            if self._stdioStartingLine:
-                self._writeLog(timeStr, self._debugLogView)
-            self._writeLog(text, self._debugLogView)
-            self._stdioStartingLine = False
-
-    def connectSimulator(self, bookedFlight, simulatorType):
-        """Connect to the simulator for the first time."""
-        self._logger.reset()
-
-        self._flight = flight.Flight(self._logger, self)
-        self._flight.flareTimeFromFS = self.config.flareTimeFromFS
-        self._flight.aircraftType = bookedFlight.aircraftType
-        self._flight.aircraft = acft.Aircraft.create(self._flight, bookedFlight)
-        self._flight.aircraft._checkers.append(self)
-
-        if self._simulator is None:
-            self._simulator = fs.createSimulator(simulatorType, self)
-            fs.setupMessageSending(self.config, self._simulator)
-            self._setupTimeSync()
-
-        self._flight.simulator = self._simulator
-
-        self.beginBusy(xstr("connect_busy"))
-        self._statusbar.updateConnection(self._connecting, self._connected)
-
-        self._connecting = True
-        self._simulator.connect(self._flight.aircraft)
-
-    def startMonitoring(self):
-        """Start monitoring."""
-        if not self._monitoring:
-            self.simulator.startMonitoring()
-            self._monitoring = True
-
-    def stopMonitoring(self):
-        """Stop monitoring."""
-        if self._monitoring:
-            self.simulator.stopMonitoring()
-            self._monitoring = False
-
-    def cruiseLevelChanged(self):
-        """Called when the cruise level is changed in the flight wizard."""
-        if self._flight is not None:
-            return self._flight.cruiseLevelChanged()
-        else:
-            return False
-
-    def _buildMenuBar(self, accelGroup):
-        """Build the main menu bar."""
-        menuBar = gtk.MenuBar()
-
-        fileMenuItem = gtk.MenuItem(xstr("menu_file"))
-        fileMenu = gtk.Menu()
-        fileMenuItem.set_submenu(fileMenu)
-        menuBar.append(fileMenuItem)
-
-        loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
-        loadPIREPMenuItem.set_use_stock(True)
-        loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
-        loadPIREPMenuItem.add_accelerator("activate", accelGroup,
-                                          ord(xstr("menu_file_loadPIREP_key")),
-                                          CONTROL_MASK, ACCEL_VISIBLE)
-        loadPIREPMenuItem.connect("activate", self._loadPIREP)
-        fileMenu.append(loadPIREPMenuItem)
-
-        fileMenu.append(gtk.SeparatorMenuItem())
-
-        quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
-        quitMenuItem.set_use_stock(True)
-        quitMenuItem.set_label(xstr("menu_file_quit"))
-        quitMenuItem.add_accelerator("activate", accelGroup,
-                                     ord(xstr("menu_file_quit_key")),
-                                     CONTROL_MASK, ACCEL_VISIBLE)
-        quitMenuItem.connect("activate", self._quit)
-        fileMenu.append(quitMenuItem)
-
-        toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
-        toolsMenu = gtk.Menu()
-        toolsMenuItem.set_submenu(toolsMenu)
-        menuBar.append(toolsMenuItem)
-
-        self._timetableMenuItem = timetableMenuItem = \
-          gtk.ImageMenuItem(gtk.STOCK_INDENT)
-        timetableMenuItem.set_use_stock(True)
-        timetableMenuItem.set_label(xstr("menu_tools_timetable"))
-        timetableMenuItem.add_accelerator("activate", accelGroup,
-                                          ord(xstr("menu_tools_timetable_key")),
-                                          CONTROL_MASK, ACCEL_VISIBLE)
-        timetableMenuItem.connect("activate", self.showTimetable)
-        self._timetableMenuItem.set_sensitive(False)
-        toolsMenu.append(timetableMenuItem)
-
-        self._flightsMenuItem = flightsMenuItem = \
-          gtk.ImageMenuItem(gtk.STOCK_SPELL_CHECK)
-        flightsMenuItem.set_use_stock(True)
-        flightsMenuItem.set_label(xstr("menu_tools_flights"))
-        flightsMenuItem.add_accelerator("activate", accelGroup,
-                                        ord(xstr("menu_tools_flights_key")),
-                                        CONTROL_MASK, ACCEL_VISIBLE)
-        flightsMenuItem.connect("activate", self.showFlights)
-        self._flightsMenuItem.set_sensitive(False)
-        toolsMenu.append(flightsMenuItem)
-
-        checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
-        checklistMenuItem.set_use_stock(True)
-        checklistMenuItem.set_label(xstr("menu_tools_chklst"))
-        checklistMenuItem.add_accelerator("activate", accelGroup,
-                                          ord(xstr("menu_tools_chklst_key")),
-                                          CONTROL_MASK, ACCEL_VISIBLE)
-        checklistMenuItem.connect("activate", self._editChecklist)
-        toolsMenu.append(checklistMenuItem)
-
-        approachCalloutsMenuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT)
-        approachCalloutsMenuItem.set_use_stock(True)
-        approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
-        approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
-                                                 ord(xstr("menu_tools_callouts_key")),
-                                                 CONTROL_MASK, ACCEL_VISIBLE)
-        approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
-        toolsMenu.append(approachCalloutsMenuItem)
-
-        prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
-        prefsMenuItem.set_use_stock(True)
-        prefsMenuItem.set_label(xstr("menu_tools_prefs"))
-        prefsMenuItem.add_accelerator("activate", accelGroup,
-                                      ord(xstr("menu_tools_prefs_key")),
-                                      CONTROL_MASK, ACCEL_VISIBLE)
-        prefsMenuItem.connect("activate", self._editPreferences)
-        toolsMenu.append(prefsMenuItem)
-
-        toolsMenu.append(gtk.SeparatorMenuItem())
-
-        bugReportMenuItem = gtk.ImageMenuItem(gtk.STOCK_PASTE)
-        bugReportMenuItem.set_use_stock(True)
-        bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
-        bugReportMenuItem.add_accelerator("activate", accelGroup,
-                                          ord(xstr("menu_tools_bugreport_key")),
-                                          CONTROL_MASK, ACCEL_VISIBLE)
-        bugReportMenuItem.connect("activate", self._reportBug)
-        toolsMenu.append(bugReportMenuItem)
-
-        viewMenuItem = gtk.MenuItem(xstr("menu_view"))
-        viewMenu = gtk.Menu()
-        viewMenuItem.set_submenu(viewMenu)
-        menuBar.append(viewMenuItem)
-
-        self._showMonitorMenuItem = gtk.CheckMenuItem()
-        self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
-        self._showMonitorMenuItem.set_use_underline(True)
-        self._showMonitorMenuItem.set_active(False)
-        self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
-                                                  ord(xstr("menu_view_monitor_key")),
-                                                  CONTROL_MASK, ACCEL_VISIBLE)
-        self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
-        viewMenu.append(self._showMonitorMenuItem)
-
-        showDebugMenuItem = gtk.CheckMenuItem()
-        showDebugMenuItem.set_label(xstr("menu_view_debug"))
-        showDebugMenuItem.set_use_underline(True)
-        showDebugMenuItem.set_active(False)
-        showDebugMenuItem.add_accelerator("activate", accelGroup,
-                                          ord(xstr("menu_view_debug_key")),
-                                          CONTROL_MASK, ACCEL_VISIBLE)
-        showDebugMenuItem.connect("toggled", self._toggleDebugLog)
-        viewMenu.append(showDebugMenuItem)
-
-        helpMenuItem = gtk.MenuItem(xstr("menu_help"))
-        helpMenu = gtk.Menu()
-        helpMenuItem.set_submenu(helpMenu)
-        menuBar.append(helpMenuItem)
-
-        manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
-        manualMenuItem.set_use_stock(True)
-        manualMenuItem.set_label(xstr("menu_help_manual"))
-        manualMenuItem.add_accelerator("activate", accelGroup,
-                                       ord(xstr("menu_help_manual_key")),
-                                       CONTROL_MASK, ACCEL_VISIBLE)
-        manualMenuItem.connect("activate", self._showManual)
-        helpMenu.append(manualMenuItem)
-
-        helpMenu.append(gtk.SeparatorMenuItem())
-
-        aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
-        aboutMenuItem.set_use_stock(True)
-        aboutMenuItem.set_label(xstr("menu_help_about"))
-        aboutMenuItem.add_accelerator("activate", accelGroup,
-                                      ord(xstr("menu_help_about_key")),
-                                      CONTROL_MASK, ACCEL_VISIBLE)
-        aboutMenuItem.connect("activate", self._showAbout)
-        helpMenu.append(aboutMenuItem)
-
-        return menuBar
-
-    def _toggleDebugLog(self, menuItem):
-        """Toggle the debug log."""
-        if menuItem.get_active():
-            label = gtk.Label(xstr("tab_debug_log"))
-            label.set_use_underline(True)
-            label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
-            self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
-            self._notebook.set_current_page(self._debugLogPage)
-        else:
-            self._notebook.remove_page(self._debugLogPage)
-
-    def _buildLogWidget(self):
-        """Build the widget for the log."""
-        alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
-
-        alignment.set_padding(padding_top = 8, padding_bottom = 8,
-                              padding_left = 16, padding_right = 16)
-
-        logScroller = gtk.ScrolledWindow()
-        # FIXME: these should be constants in common
-        logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
-                               else gtk.POLICY_AUTOMATIC,
-                               gtk.PolicyType.AUTOMATIC if pygobject
-                               else gtk.POLICY_AUTOMATIC)
-        logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
-                                    else gtk.SHADOW_IN)
-        logView = gtk.TextView()
-        logView.set_editable(False)
-        logView.set_cursor_visible(False)
-        logScroller.add(logView)
-
-        logBox = gtk.VBox()
-        logBox.pack_start(logScroller, True, True, 0)
-        logBox.set_size_request(-1, 200)
-
-        alignment.add(logBox)
-
-        return (alignment, logView)
-
-    def _writeLog(self, msg, logView, isFault = False):
-        """Write the given message to the log."""
-        buffer = logView.get_buffer()
-        appendTextBuffer(buffer, msg, isFault = isFault)
-        logView.scroll_mark_onscreen(buffer.get_insert())
-
-    def _quit(self, what = None, force = False):
-        """Quit from the application."""
-        if force:
-            result=RESPONSETYPE_YES
-        else:
-            dialog = gtk.MessageDialog(parent = self._mainWindow,
-                                       type = MESSAGETYPE_QUESTION,
-                                       message_format = xstr("quit_question"))
-
-            dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
-            dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
-
-            dialog.set_title(WINDOW_TITLE_BASE)
-            result = dialog.run()
-            dialog.hide()
-
-        if result==RESPONSETYPE_YES:
-            self._statusIcon.destroy()
-            return gtk.main_quit()
-
-    def _notebookPageSwitch(self, notebook, page, page_num):
-        """Called when the current page of the notebook has changed."""
-        if page_num==0:
-            gobject.idle_add(self._wizard.grabDefault)
-        else:
-            self._mainWindow.set_default(None)
-
-    def loginSuccessful(self):
-        """Called when the login is successful."""
-        self._flightsMenuItem.set_sensitive(True)
-        self._timetableMenuItem.set_sensitive(True)
-
-    def isWizardActive(self):
-        """Determine if the flight wizard is active."""
-        return self._notebook.get_current_page()==0
-
-    def showTimetable(self, menuItem):
-        """Callback for showing the timetable."""
-        if self._timetableWindow.hasFlightPairs:
-            self._timetableWindow.show_all()
-        else:
-            date = datetime.date.today()
-            self._timetableWindow.setTypes(self.loginResult.types)
-            self._timetableWindow.setDate(date)
-            self.updateTimeTable(date)
-            self.beginBusy(xstr("timetable_query_busy"))
-
-    def updateTimeTable(self, date):
-        """Update the time table for the given date."""
-        self.beginBusy(xstr("timetable_query_busy"))
-        self._timetableWindow.set_sensitive(False)
-        window = self._timetableWindow.get_window()
-        if window is not None:
-            window.set_cursor(self._busyCursor)
-        self.webHandler.getTimetable(self._timetableCallback, date,
-                                     self.loginResult.types)
-
-    def _timetableCallback(self, returned, result):
-        """Called when the timetable has been received."""
-        gobject.idle_add(self._handleTimetable, returned, result)
-
-    def _handleTimetable(self, returned, result):
-        """Handle the result of the query for the timetable."""
-        self.endBusy()
-        window = self._timetableWindow.get_window()
-        if window is not None:
-            window.set_cursor(None)
-        self._timetableWindow.set_sensitive(True)
-        if returned:
-            self._timetableWindow.setFlightPairs(result.flightPairs)
-            self._timetableWindow.show_all()
-        else:
-            dialog = gtk.MessageDialog(parent = self.mainWindow,
-                                       type = MESSAGETYPE_ERROR,
-                                       message_format = xstr("timetable_failed"))
-            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-            dialog.set_title(WINDOW_TITLE_BASE)
-            dialog.run()
-            dialog.hide()
-            self._timetableWindow.clear()
-
-    def showFlights(self, menuItem):
-        """Callback for showing the flight list."""
-        if self._flightsWindow.hasFlights:
-            self._flightsWindow.show_all()
-        else:
-            self.beginBusy(xstr("acceptedflt_query_busy"))
-            self.webHandler.getAcceptedFlights(self._acceptedFlightsCallback)
-
-    def _acceptedFlightsCallback(self, returned, result):
-        """Called when the accepted flights have been received."""
-        gobject.idle_add(self._handleAcceptedFlights, returned, result)
-
-    def _handleAcceptedFlights(self, returned, result):
-        """Handle the result of the query for accepted flights."""
-        self.endBusy()
-        if returned:
-            self._flightsWindow.clear()
-            for flight in result.flights:
-                self._flightsWindow.addFlight(flight)
-            self._flightsWindow.show_all()
-        else:
-            dialog = gtk.MessageDialog(parent = self.mainWindow,
-                                       type = MESSAGETYPE_ERROR,
-                                       message_format = xstr("acceptedflt_failed"))
-            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-            dialog.set_title(WINDOW_TITLE_BASE)
-            dialog.run()
-            dialog.hide()
-
-    def _hideTimetableWindow(self, window, event):
-        """Hide the window of the timetable."""
-        self._timetableWindow.hide()
-        return True
-
-    def _hideFlightsWindow(self, window, event):
-        """Hide the window of the accepted flights."""
-        self._flightsWindow.hide()
-        return True
-
-    def _editChecklist(self, menuItem):
-        """Callback for editing the checklists."""
-        self._checklistEditor.run()
-
-    def _editApproachCallouts(self, menuItem):
-        """Callback for editing the approach callouts."""
-        self._approachCalloutsEditor.run()
-
-    def _editPreferences(self, menuItem):
-        """Callback for editing the preferences."""
-        self._clearHotkeys()
-        self._preferences.run(self.config)
-        self._setupTimeSync()
-        self._listenHotkeys()
-
-    def _reportBug(self, menuItem):
-        """Callback for reporting a bug."""
-        self._bugReportDialog.run()
-
-    def _setupTimeSync(self):
-        """Enable or disable the simulator time synchronization based on the
-        configuration."""
-        simulator = self._simulator
-        if simulator is not None:
-            if self.config.syncFSTime:
-                simulator.enableTimeSync()
-            else:
-                simulator.disableTimeSync()
-
-    def viewPIREP(self, pirep):
-        """Display the PIREP viewer window with the given PIREP."""
-        self._pirepViewer.setPIREP(pirep)
-        self._pirepViewer.show_all()
-        self._pirepViewer.run()
-        self._pirepViewer.hide()
-
-    def viewMessagedPIREP(self, pirep):
-        """Display the PIREP viewer window with the given PIREP containing
-        messages as well."""
-        self._messagedPIREPViewer.setPIREP(pirep)
-        self._messagedPIREPViewer.show_all()
-        self._messagedPIREPViewer.run()
-        self._messagedPIREPViewer.hide()
-
-    def editPIREP(self, pirep):
-        """Display the PIREP editor window and allow editing the PIREP."""
-        self._pirepEditor.setPIREP(pirep)
-        self._pirepEditor.show_all()
-        if self._pirepEditor.run()==RESPONSETYPE_OK:
-            self.beginBusy(xstr("pirepEdit_save_busy"))
-            self.webHandler.sendPIREP(self._pirepUpdatedCallback, pirep,
-                                      update = True)
-        else:
-            self._pirepEditor.hide()
-
-    def _pirepUpdatedCallback(self, returned, result):
-        """Callback for the PIREP updating result."""
-        gobject.idle_add(self._handlePIREPUpdated, returned, result)
-
-    def _handlePIREPUpdated(self, returned, result):
-        """Callback for the PIREP updating result."""
-        self.endBusy()
-        secondaryMarkup = None
-        type = MESSAGETYPE_ERROR
-        if returned:
-            if result.success:
-                type = None
-            elif result.alreadyFlown:
-                messageFormat = xstr("sendPIREP_already")
-                secondaryMarkup = xstr("sendPIREP_already_sec")
-            elif result.notAvailable:
-                messageFormat = xstr("sendPIREP_notavail")
-            else:
-                messageFormat = xstr("sendPIREP_unknown")
-                secondaryMarkup = xstr("sendPIREP_unknown_sec")
-        else:
-            print "PIREP sending failed", result
-            messageFormat = xstr("sendPIREP_failed")
-            secondaryMarkup = xstr("sendPIREP_failed_sec")
-
-        if type is not None:
-            dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
-                                       type = type, message_format = messageFormat)
-            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-            dialog.set_title(WINDOW_TITLE_BASE)
-            if secondaryMarkup is not None:
-                dialog.format_secondary_markup(secondaryMarkup)
-
-            dialog.run()
-            dialog.hide()
-
-        self._pirepEditor.hide()
-
-    def _loadPIREP(self, menuItem):
-        """Load a PIREP for sending."""
-        dialog = self._getLoadPirepDialog()
-
-        if self._lastLoadedPIREP:
-            dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
-        else:
-            pirepDirectory = self.config.pirepDirectory
-            if pirepDirectory is not None:
-                dialog.set_current_folder(pirepDirectory)
-
-        result = dialog.run()
-        dialog.hide()
-
-        if result==RESPONSETYPE_OK:
-            self._lastLoadedPIREP = text2unicode(dialog.get_filename())
-
-            pirep = PIREP.load(self._lastLoadedPIREP)
-            if pirep is None:
-                dialog = gtk.MessageDialog(parent = self._mainWindow,
-                                           type = MESSAGETYPE_ERROR,
-                                           message_format = xstr("loadPIREP_failed"))
-                dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-                dialog.set_title(WINDOW_TITLE_BASE)
-                dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
-                dialog.run()
-                dialog.hide()
-            else:
-                dialog = self._getSendLoadedDialog(pirep)
-                dialog.show_all()
-                while True:
-                    result = dialog.run()
-
-                    if result==RESPONSETYPE_OK:
-                        self.sendPIREP(pirep)
-                    elif result==1:
-                        self.viewPIREP(pirep)
-                    else:
-                        break
-
-                dialog.hide()
-
-    def _getLoadPirepDialog(self):
-        """Get the PIREP loading file chooser dialog.
-
-        If it is not created yet, it will be created."""
-        if self._loadPIREPDialog is None:
-            dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
-                                           xstr("loadPIREP_browser_title"),
-                                           action = FILE_CHOOSER_ACTION_OPEN,
-                                           buttons = (gtk.STOCK_CANCEL,
-                                                      RESPONSETYPE_CANCEL,
-                                                      gtk.STOCK_OK, RESPONSETYPE_OK),
-                                           parent = self._mainWindow)
-            dialog.set_modal(True)
-
-
-            filter = gtk.FileFilter()
-            filter.set_name(xstr("file_filter_pireps"))
-            filter.add_pattern("*.pirep")
-            dialog.add_filter(filter)
-
-            filter = gtk.FileFilter()
-            filter.set_name(xstr("file_filter_all"))
-            filter.add_pattern("*.*")
-            dialog.add_filter(filter)
-
-            self._loadPIREPDialog = dialog
-
-        return self._loadPIREPDialog
-
-    def _getSendLoadedDialog(self, pirep):
-        """Get a dialog displaying the main information of the flight from the
-        PIREP and providing Cancel and Send buttons."""
-        dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
-                            xstr("loadPIREP_send_title"),
-                            parent = self._mainWindow,
-                            flags = DIALOG_MODAL)
-
-        contentArea = dialog.get_content_area()
-
-        label = gtk.Label(xstr("loadPIREP_send_help"))
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.set_padding(padding_top = 16, padding_bottom = 0,
-                              padding_left = 48, padding_right = 48)
-        alignment.add(label)
-        contentArea.pack_start(alignment, False, False, 8)
-
-        table = gtk.Table(5, 2)
-        tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
-                                   padding_left = 48, padding_right = 48)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        tableAlignment.add(table)
-        contentArea.pack_start(tableAlignment, True, True, 8)
-
-        bookedFlight = pirep.bookedFlight
-
-        label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
-        label.set_use_markup(True)
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 0, 1)
-
-        label = gtk.Label(bookedFlight.callsign)
-        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 1, 2, 0, 1)
-
-        label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
-        label.set_use_markup(True)
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 1, 2)
-
-        label = gtk.Label(str(bookedFlight.departureTime.date()))
-        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 1, 2, 1, 2)
-
-        label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
-        label.set_use_markup(True)
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 2, 3)
-
-        label = gtk.Label(bookedFlight.departureICAO)
-        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 1, 2, 2, 3)
-
-        label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
-        label.set_use_markup(True)
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 3, 4)
-
-        label = gtk.Label(bookedFlight.arrivalICAO)
-        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 1, 2, 3, 4)
-
-        label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
-        label.set_use_markup(True)
-        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 4, 5)
-
-        rating = pirep.rating
-        label = gtk.Label()
-        if rating<0:
-            label.set_markup('<b><span foreground="red">NO GO</span></b>')
-        else:
-            label.set_text("%.1f %%" % (rating,))
-
-        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 1, 2, 4, 5)
-
-        dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
-        dialog.add_button(xstr("viewPIREP"), 1)
-        dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
-
-        return dialog
-
-    def sendPIREP(self, pirep, callback = None):
-        """Send the given PIREP."""
-        self.beginBusy(xstr("sendPIREP_busy"))
-        self._sendPIREPCallback = callback
-        self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
-
-    def _pirepSentCallback(self, returned, result):
-        """Callback for the PIREP sending result."""
-        gobject.idle_add(self._handlePIREPSent, returned, result)
-
-    def _handlePIREPSent(self, returned, result):
-        """Callback for the PIREP sending result."""
-        self.endBusy()
-        secondaryMarkup = None
-        type = MESSAGETYPE_ERROR
-        if returned:
-            if result.success:
-                type = MESSAGETYPE_INFO
-                messageFormat = xstr("sendPIREP_success")
-                secondaryMarkup = xstr("sendPIREP_success_sec")
-            elif result.alreadyFlown:
-                messageFormat = xstr("sendPIREP_already")
-                secondaryMarkup = xstr("sendPIREP_already_sec")
-            elif result.notAvailable:
-                messageFormat = xstr("sendPIREP_notavail")
-            else:
-                messageFormat = xstr("sendPIREP_unknown")
-                secondaryMarkup = xstr("sendPIREP_unknown_sec")
-        else:
-            print "PIREP sending failed", result
-            messageFormat = xstr("sendPIREP_failed")
-            secondaryMarkup = xstr("sendPIREP_failed_sec")
-
-        dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
-                                   type = type, message_format = messageFormat)
-        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-        dialog.set_title(WINDOW_TITLE_BASE)
-        if secondaryMarkup is not None:
-            dialog.format_secondary_markup(secondaryMarkup)
-
-        dialog.run()
-        dialog.hide()
-
-        callback = self._sendPIREPCallback
-        self._sendPIREPCallback = None
-        if callback is not None:
-            callback(returned, result)
-
-    def sendBugReport(self, summary, description, email, callback = None):
-        """Send the bug report with the given data."""
-        description += "\n\n" + ("=" * 40)
-        description += "\n\nThe contents of the log:\n\n"
-
-        for (timestampString, text) in self._logger.lines:
-            description += unicode(formatFlightLogLine(timestampString, text))
-
-        description += "\n\n" + ("=" * 40)
-        description += "\n\nThe contents of the debug log:\n\n"
-
-        buffer = self._debugLogView.get_buffer()
-        description += buffer.get_text(buffer.get_start_iter(),
-                                       buffer.get_end_iter(), True)
-
-        self.beginBusy(xstr("sendBugReport_busy"))
-        self._sendBugReportCallback = callback
-        self.webHandler.sendBugReport(self._bugReportSentCallback,
-                                      summary, description, email)
-
-    def _cefInitialized(self):
-        """Called when CEF has been initialized."""
-        self._acars.start()
-        cef.initializeSimBrief()
-
-    def _bugReportSentCallback(self, returned, result):
-        """Callback function for the bug report sending result."""
-        gobject.idle_add(self._handleBugReportSent, returned, result)
-
-    def _handleBugReportSent(self, returned, result):
-        """Callback for the bug report sending result."""
-        self.endBusy()
-        secondaryMarkup = None
-        type = MESSAGETYPE_ERROR
-        if returned:
-            if result.success:
-                type = MESSAGETYPE_INFO
-                messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
-                secondaryMarkup = xstr("sendBugReport_success_sec")
-            else:
-                messageFormat = xstr("sendBugReport_error")
-                secondaryMarkup = xstr("sendBugReport_siteerror_sec")
-        else:
-            messageFormat = xstr("sendBugReport_error")
-            secondaryMarkup = xstr("sendBugReport_error_sec")
-
-        dialog = gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
-                                   type = type, message_format = messageFormat)
-        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-        dialog.set_title(WINDOW_TITLE_BASE)
-        if secondaryMarkup is not None:
-            dialog.format_secondary_markup(secondaryMarkup)
-
-        dialog.run()
-        dialog.hide()
-
-        callback = self._sendBugReportCallback
-        self._sendBugReportCallback = None
-        if callback is not None:
-            callback(returned, result)
-
-    def _listenHotkeys(self):
-        """Setup the hotkeys based on the configuration."""
-        if self._hotkeySetID is None and self._simulator is not None:
-            self._pilotHotkeyIndex = None
-            self._checklistHotkeyIndex = None
-
-            hotkeys = []
-
-            config = self.config
-            if config.enableSounds and config.pilotControlsSounds:
-                self._pilotHotkeyIndex = len(hotkeys)
-                hotkeys.append(config.pilotHotkey)
-
-            if config.enableChecklists:
-                self._checklistHotkeyIndex = len(hotkeys)
-                hotkeys.append(config.checklistHotkey)
-
-            if hotkeys:
-                self._hotkeySetID = \
-                    self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
-
-    def _clearHotkeys(self):
-        """Clear the hotkeys."""
-        if self._hotkeySetID is not None:
-            self._hotkeySetID=None
-            self._simulator.clearHotkeys()
-
-    def _handleHotkeys(self, id, hotkeys):
-        """Handle the hotkeys."""
-        if id==self._hotkeySetID:
-            for index in hotkeys:
-                if index==self._pilotHotkeyIndex:
-                    print "gui.GUI._handleHotkeys: pilot hotkey pressed"
-                    self._flight.pilotHotkeyPressed()
-                elif index==self._checklistHotkeyIndex:
-                    print "gui.GUI._handleHotkeys: checklist hotkey pressed"
-                    self._flight.checklistHotkeyPressed()
-                else:
-                    print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
-
-    def _showManual(self, menuitem):
-        """Show the user's manual."""
-        webbrowser.open(url ="file://" +
-                        os.path.join(self._programDirectory, "doc", "manual",
-                                     getLanguage(), "index.html"),
-                        new = 1)
-
-    def _showAbout(self, menuitem):
-        """Show the about dialog."""
-        dialog = self._getAboutDialog()
-        dialog.show_all()
-        dialog.run()
-        dialog.hide()
-
-    def _getAboutDialog(self):
-        """Get the about dialog.
-
-        If it does not exist yet, it will be created."""
-        if self._aboutDialog is None:
-            dialog = gtk.AboutDialog()
-            dialog.set_transient_for(self._mainWindow)
-            dialog.set_modal(True)
-
-            logoPath = os.path.join(self._programDirectory, "logo.png")
-            logo = pixbuf_new_from_file(logoPath)
-            dialog.set_logo(logo)
-
-            dialog.set_program_name(PROGRAM_NAME)
-            dialog.set_version(const.VERSION)
-            dialog.set_copyright("(c) 2012 by István Váradi")
-            dialog.set_website("http://mlx.varadiistvan.hu")
-            dialog.set_website_label(xstr("about_website"))
-
-            isHungarian = getLanguage()=="hu"
-            authors = []
-            for (familyName, firstName, role) in GUI._authors:
-                author = "%s %s" % \
-                         (familyName if isHungarian else firstName,
-                          firstName if isHungarian else familyName)
-                role = xstr("about_role_" + role)
-                authors.append(author + " (" + role + ")")
-            dialog.set_authors(authors)
-
-            dialog.set_license(xstr("about_license"))
-
-            if not pygobject:
-                gtk.about_dialog_set_url_hook(self._showAboutURL, None)
-
-            self._aboutDialog = dialog
-
-        return self._aboutDialog
-
-    def _showAboutURL(self, dialog, link, user_data):
-        """Show the about URL."""
-        webbrowser.open(url = link, new = 1)
-
-    def _setTakeoffAntiIceOn(self, value):
-        """Set the anti-ice on indicator."""
-        self._wizard.takeoffAntiIceOn = value
-
-    def _setLandingAntiIceOn(self, value):
-        """Set the anti-ice on indicator."""
-        self._wizard.landingAntiIceOn = value
-
-    def _getCredentialsCallback(self):
-        """Called when the web handler asks for the credentials."""
-        # FIXME: this is almost the same as
-        # SimBriefSetupPage._getCredentialsCallback
-        with self._credentialsCondition:
-            self._credentialsAvailable = False
-
-            gobject.idle_add(self._getCredentials)
-
-            while not self._credentialsAvailable:
-                self._credentialsCondition.wait()
-
-            return (self._credentialsUserName, self._credentialsPassword)
-
-    def _getCredentials(self):
-        """Get the credentials."""
-        # FIXME: this is almost the same as
-        # SimBriefSetupPage._getCredentials
-        with self._credentialsCondition:
-            config = self.config
-
-            dialog = CredentialsDialog(self, config.pilotID, config.password,
-                                       xstr("login_title"),
-                                       xstr("button_cancel"),
-                                       xstr("button_ok"),
-                                       xstr("label_pilotID"),
-                                       xstr("login_pilotID_tooltip"),
-                                       xstr("label_password"),
-                                       xstr("login_password_tooltip"),
-                                       xstr("login_info"),
-                                       config.rememberPassword,
-                                       xstr("remember_password"),
-                                       xstr("login_remember_tooltip"))
-            response = dialog.run()
-
-            if response==RESPONSETYPE_OK:
-                self._credentialsUserName = dialog.userName
-                self._credentialsPassword = dialog.password
-                rememberPassword = dialog.rememberPassword
-
-                config.pilotID = self._credentialsUserName
-
-                config.password = \
-                  self._credentialsPassword if rememberPassword else ""
-                config.rememberPassword = rememberPassword
-
-                config.save()
-            else:
-                self._credentialsUserName = None
-                self._credentialsPassword = None
-
-            self._credentialsAvailable = True
-            self._credentialsCondition.notify()
-
-    def _updateDone(self):
-        """Called when the update is done.
-
-        It checks if we already know the PID, and if not, asks the user whether
-        to register."""
-        if not self.config.pilotID and not self.config.password:
-            dialog = gtk.MessageDialog(parent = self._mainWindow,
-                                       type = MESSAGETYPE_QUESTION,
-                                       message_format = xstr("register_ask"))
-
-            dialog.set_title(WINDOW_TITLE_BASE)
-            dialog.format_secondary_markup(xstr("register_ask_sec"))
-
-            dialog.add_button(xstr("button_cancel"), 0)
-            dialog.add_button(xstr("button_register"), 1)
-            dialog.set_default_response(1)
-
-            result = dialog.run()
-            dialog.hide()
-            if result == 1:
-                self._wizard.jumpPage("register")
Index: src/mlx/gui/info.py
===================================================================
--- src/mlx/gui/info.py	(revision 846)
+++ src/mlx/gui/info.py	(revision 846)
@@ -0,0 +1,203 @@
+
+from common import *
+
+from mlx.gui.delaycodes import DelayCodeTable
+from mlx.gui.faultexplain import FaultExplainWidget
+
+from mlx.i18n import xstr
+import mlx.const as const
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.info
+#
+# The flight info tab.
+#
+# This module implements to \ref FlightInfo class, which is the widget for the
+# extra information related to the flight. It contains a text area for the
+# comments, the fault list widget, and the frame for the delay codes at the
+# bottom in the centre.
+
+#------------------------------------------------------------------------------
+
+class FlightInfo(gtk.VBox):
+    """The flight info tab."""
+    @staticmethod
+    def _createCommentArea(label):
+        """Create a comment area.
+
+        Returns a tuple of two items:
+        - the top-level widget of the comment area, and
+        - the comment text editor."""
+
+        frame = gtk.Frame(label = label)
+        label = frame.get_label_widget()
+        label.set_use_underline(True)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 4,
+                              padding_left = 8, padding_right = 8)
+
+        scroller = gtk.ScrolledWindow()
+        scroller.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+        scroller.set_shadow_type(SHADOW_IN)
+
+        comments = gtk.TextView()
+        comments.set_wrap_mode(WRAP_WORD)
+        scroller.add(comments)
+        alignment.add(scroller)
+        frame.add(alignment)
+
+        label.set_mnemonic_widget(comments)
+
+        return (frame, comments)
+
+    def __init__(self, gui, callbackObject = None):
+        """Construct the flight info tab."""
+        super(FlightInfo, self).__init__()
+        self._gui = gui
+        self._callbackObject = callbackObject
+
+        self._commentsAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                                xscale = 1.0, yscale = 1.0)
+        commentsBox = gtk.HBox()
+        commentsBox.set_homogeneous(True)
+
+        (frame, self._comments) = FlightInfo._createCommentArea(xstr("info_comments"))
+        commentsBox.pack_start(frame, True, True, 8)
+        self._comments.get_buffer().connect("changed", self._commentsChanged)
+
+        self._faultExplainWidget = FaultExplainWidget(gui)
+        self._faultExplainWidget.connect("explanations-changed",
+                                         self._faultExplanationsChanged)
+        commentsBox.pack_start(self._faultExplainWidget, True, True, 8)
+
+        self._commentsAlignment.add(commentsBox)
+        self.pack_start(self._commentsAlignment, True, True, 8)
+
+        frame = gtk.Frame(label = xstr("info_delay"))
+        label = frame.get_label_widget()
+        label.set_use_underline(True)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 4,
+                              padding_left = 8, padding_right = 8)
+
+        self._delayCodeTable = table = DelayCodeTable(self)
+        self._delayWindow = scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.add(table)
+        scrolledWindow.set_size_request(-1, 185)
+        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+        scrolledWindow.set_shadow_type(SHADOW_IN)
+
+        alignment.add(scrolledWindow)
+        frame.add(alignment)
+
+        self._delayAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                             xscale = 1.0, yscale = 1.0)
+        self._delayAlignment.add(frame)
+        self._delayAlignment.set_padding(padding_top = 0, padding_bottom = 0,
+                                         padding_left = 8, padding_right = 8)
+
+        self.pack_start(self._delayAlignment, False, False, 8)
+
+    @property
+    def comments(self):
+        """Get the comments."""
+        buffer = self._comments.get_buffer()
+        return text2unicode(buffer.get_text(buffer.get_start_iter(),
+                                            buffer.get_end_iter(), True))
+
+    @comments.setter
+    def comments(self, comments):
+        """Set the comments."""
+        self._comments.get_buffer().set_text(comments)
+
+    @property
+    def hasComments(self):
+        """Get whether there is any text in comments field."""
+        return self._comments.get_buffer().get_char_count()>0
+
+    @property
+    def faultsAndExplanations(self):
+        """Get the faults and explanations as HTML."""
+        return self._faultExplainWidget.html
+
+    @property
+    def delayCodes(self):
+        """Get the list of delay codes checked by the user."""
+        return self._delayCodeTable.delayCodes
+
+    @property
+    def hasDelayCode(self):
+        """Determine if there is at least one delay code selected."""
+        return self._delayCodeTable.hasDelayCode
+
+    @property
+    def faultsFullyExplained(self):
+        """Determine if all the faults have been explained by the pilot."""
+        return self._faultExplainWidget.fullyExplained
+
+    def addFault(self, id, faultText):
+        """Add a fault to the list of faults."""
+        self._faultExplainWidget.addFault(id, faultText)
+
+    def updateFault(self, id, faultText):
+        """Update a fault to the list of faults."""
+        self._faultExplainWidget.updateFault(id, faultText)
+
+    def clearFault(self, id):
+        """Clear a fault to the list of faults."""
+        self._faultExplainWidget.clearFault(id)
+
+    def setExplanation(self, id, explanation):
+        """Set the explanation of the given fault."""
+        self._faultExplainWidget.setExplanation(id, explanation)
+
+    def enable(self, aircraftType):
+        """Enable the flight info tab."""
+        self._comments.set_sensitive(True)
+        self._faultExplainWidget.set_sensitive(True)
+        self._delayCodeTable.setType(aircraftType)
+        self._delayWindow.set_sensitive(True)
+        self._delayCodeTable.setStyle()
+
+    def disable(self):
+        """Enable the flight info tab."""
+        self._comments.set_sensitive(False)
+        self._faultExplainWidget.set_sensitive(False)
+        self._delayWindow.set_sensitive(False)
+        self._delayCodeTable.setStyle()
+
+    def reset(self):
+        """Reset the flight info tab."""
+        self._comments.get_buffer().set_text("")
+        self._faultExplainWidget.reset()
+        self._delayCodeTable.reset()
+
+    def activateDelayCode(self, code):
+        """Active the checkbox corresponding to the given code."""
+        self._delayCodeTable.activateCode(code)
+
+    def delayCodesChanged(self):
+        """Callewd when the delay codes have changed."""
+        if self._callbackObject is None:
+            self._gui.delayCodesChanged()
+        else:
+            self._callbackObject.delayCodesChanged()
+
+    def _commentsChanged(self, textbuffer):
+        """Called when the comments have changed."""
+        if self._callbackObject is None:
+            self._gui.commentsChanged()
+        else:
+            self._callbackObject.commentsChanged()
+
+    def _faultExplanationsChanged(self, faultExplainWidget, fullyExplained):
+        """Called when the status of the fault explanations has changed."""
+        if self._callbackObject is None:
+            self._gui.faultExplanationsChanged()
+        else:
+            self._callbackObject.faultExplanationsChanged()
Index: src/mlx/gui/info.py
===================================================================
--- src/mlx/gui/info.py	(revision 846)
+++ 	(revision )
@@ -1,203 +1,0 @@
-
-from common import *
-
-from mlx.gui.delaycodes import DelayCodeTable
-from mlx.gui.faultexplain import FaultExplainWidget
-
-from mlx.i18n import xstr
-import mlx.const as const
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.info
-#
-# The flight info tab.
-#
-# This module implements to \ref FlightInfo class, which is the widget for the
-# extra information related to the flight. It contains a text area for the
-# comments, the fault list widget, and the frame for the delay codes at the
-# bottom in the centre.
-
-#------------------------------------------------------------------------------
-
-class FlightInfo(gtk.VBox):
-    """The flight info tab."""
-    @staticmethod
-    def _createCommentArea(label):
-        """Create a comment area.
-
-        Returns a tuple of two items:
-        - the top-level widget of the comment area, and
-        - the comment text editor."""
-
-        frame = gtk.Frame(label = label)
-        label = frame.get_label_widget()
-        label.set_use_underline(True)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 4,
-                              padding_left = 8, padding_right = 8)
-
-        scroller = gtk.ScrolledWindow()
-        scroller.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-        scroller.set_shadow_type(SHADOW_IN)
-
-        comments = gtk.TextView()
-        comments.set_wrap_mode(WRAP_WORD)
-        scroller.add(comments)
-        alignment.add(scroller)
-        frame.add(alignment)
-
-        label.set_mnemonic_widget(comments)
-
-        return (frame, comments)
-
-    def __init__(self, gui, callbackObject = None):
-        """Construct the flight info tab."""
-        super(FlightInfo, self).__init__()
-        self._gui = gui
-        self._callbackObject = callbackObject
-
-        self._commentsAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                                xscale = 1.0, yscale = 1.0)
-        commentsBox = gtk.HBox()
-        commentsBox.set_homogeneous(True)
-
-        (frame, self._comments) = FlightInfo._createCommentArea(xstr("info_comments"))
-        commentsBox.pack_start(frame, True, True, 8)
-        self._comments.get_buffer().connect("changed", self._commentsChanged)
-
-        self._faultExplainWidget = FaultExplainWidget(gui)
-        self._faultExplainWidget.connect("explanations-changed",
-                                         self._faultExplanationsChanged)
-        commentsBox.pack_start(self._faultExplainWidget, True, True, 8)
-
-        self._commentsAlignment.add(commentsBox)
-        self.pack_start(self._commentsAlignment, True, True, 8)
-
-        frame = gtk.Frame(label = xstr("info_delay"))
-        label = frame.get_label_widget()
-        label.set_use_underline(True)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 4,
-                              padding_left = 8, padding_right = 8)
-
-        self._delayCodeTable = table = DelayCodeTable(self)
-        self._delayWindow = scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.add(table)
-        scrolledWindow.set_size_request(-1, 185)
-        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-        scrolledWindow.set_shadow_type(SHADOW_IN)
-
-        alignment.add(scrolledWindow)
-        frame.add(alignment)
-
-        self._delayAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                             xscale = 1.0, yscale = 1.0)
-        self._delayAlignment.add(frame)
-        self._delayAlignment.set_padding(padding_top = 0, padding_bottom = 0,
-                                         padding_left = 8, padding_right = 8)
-
-        self.pack_start(self._delayAlignment, False, False, 8)
-
-    @property
-    def comments(self):
-        """Get the comments."""
-        buffer = self._comments.get_buffer()
-        return text2unicode(buffer.get_text(buffer.get_start_iter(),
-                                            buffer.get_end_iter(), True))
-
-    @comments.setter
-    def comments(self, comments):
-        """Set the comments."""
-        self._comments.get_buffer().set_text(comments)
-
-    @property
-    def hasComments(self):
-        """Get whether there is any text in comments field."""
-        return self._comments.get_buffer().get_char_count()>0
-
-    @property
-    def faultsAndExplanations(self):
-        """Get the faults and explanations as HTML."""
-        return self._faultExplainWidget.html
-
-    @property
-    def delayCodes(self):
-        """Get the list of delay codes checked by the user."""
-        return self._delayCodeTable.delayCodes
-
-    @property
-    def hasDelayCode(self):
-        """Determine if there is at least one delay code selected."""
-        return self._delayCodeTable.hasDelayCode
-
-    @property
-    def faultsFullyExplained(self):
-        """Determine if all the faults have been explained by the pilot."""
-        return self._faultExplainWidget.fullyExplained
-
-    def addFault(self, id, faultText):
-        """Add a fault to the list of faults."""
-        self._faultExplainWidget.addFault(id, faultText)
-
-    def updateFault(self, id, faultText):
-        """Update a fault to the list of faults."""
-        self._faultExplainWidget.updateFault(id, faultText)
-
-    def clearFault(self, id):
-        """Clear a fault to the list of faults."""
-        self._faultExplainWidget.clearFault(id)
-
-    def setExplanation(self, id, explanation):
-        """Set the explanation of the given fault."""
-        self._faultExplainWidget.setExplanation(id, explanation)
-
-    def enable(self, aircraftType):
-        """Enable the flight info tab."""
-        self._comments.set_sensitive(True)
-        self._faultExplainWidget.set_sensitive(True)
-        self._delayCodeTable.setType(aircraftType)
-        self._delayWindow.set_sensitive(True)
-        self._delayCodeTable.setStyle()
-
-    def disable(self):
-        """Enable the flight info tab."""
-        self._comments.set_sensitive(False)
-        self._faultExplainWidget.set_sensitive(False)
-        self._delayWindow.set_sensitive(False)
-        self._delayCodeTable.setStyle()
-
-    def reset(self):
-        """Reset the flight info tab."""
-        self._comments.get_buffer().set_text("")
-        self._faultExplainWidget.reset()
-        self._delayCodeTable.reset()
-
-    def activateDelayCode(self, code):
-        """Active the checkbox corresponding to the given code."""
-        self._delayCodeTable.activateCode(code)
-
-    def delayCodesChanged(self):
-        """Callewd when the delay codes have changed."""
-        if self._callbackObject is None:
-            self._gui.delayCodesChanged()
-        else:
-            self._callbackObject.delayCodesChanged()
-
-    def _commentsChanged(self, textbuffer):
-        """Called when the comments have changed."""
-        if self._callbackObject is None:
-            self._gui.commentsChanged()
-        else:
-            self._callbackObject.commentsChanged()
-
-    def _faultExplanationsChanged(self, faultExplainWidget, fullyExplained):
-        """Called when the status of the fault explanations has changed."""
-        if self._callbackObject is None:
-            self._gui.faultExplanationsChanged()
-        else:
-            self._callbackObject.faultExplanationsChanged()
Index: src/mlx/gui/monitor.py
===================================================================
--- src/mlx/gui/monitor.py	(revision 765)
+++ src/mlx/gui/monitor.py	(revision 765)
@@ -0,0 +1,442 @@
+
+from mlx.gui.common import *
+
+import mlx.const as const
+import mlx.util as util
+
+import time
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.monitor
+#
+# The monitoring window
+#
+# The \ref MonitorWindow class is a window containing the data received from
+# the simulator by the logger.
+
+#------------------------------------------------------------------------------
+
+class MonitorWindow(gtk.Window):
+    """The window for the data monitor."""
+    def __init__(self, gui, iconDirectory):
+        """Construct the monitor window."""
+        super(MonitorWindow, self).__init__()
+
+        self._gui = gui
+
+        self.set_resizable(False)
+        self.set_title(WINDOW_TITLE_BASE + " - Data Monitor")
+        self.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
+        self.connect("delete-event",
+                     lambda a, b: self._gui.hideMonitorWindow())
+
+        alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
+
+        alignment.set_padding(padding_top = 4, padding_bottom = 10,
+                              padding_left = 16, padding_right = 16)
+
+        table = gtk.Table(rows = 9, columns = 14)
+        table.set_homogeneous(False)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+
+        (label, self._timestamp) = self._createLabeledEntry("Time:")
+        table.attach(label, 0, 1, 0, 1)
+        table.attach(self._timestamp, 1, 2, 0, 1)
+
+        self._paused = gtk.Label("PAUSED")
+        table.attach(self._paused, 2, 4, 0, 1)
+
+        self._trickMode = gtk.Label("TRICKMODE")
+        table.attach(self._trickMode, 4, 6, 0, 1, xoptions = 0)
+
+        self._overspeed = gtk.Label("OVERSPEED")
+        table.attach(self._overspeed, 6, 8, 0, 1)
+
+        self._stalled = gtk.Label("STALLED")
+        table.attach(self._stalled, 8, 10, 0, 1)
+
+        self._onTheGround = gtk.Label("ONTHEGROUND")
+        table.attach(self._onTheGround, 10, 11, 0, 1)
+
+        self._antiIceOn = gtk.Label("ANTI-ICE")
+        table.attach(self._antiIceOn, 11, 12, 0, 1)
+
+        (label, self._zfw) = self._createLabeledEntry("ZFW:", 6)
+        table.attach(label, 0, 1, 1, 2)
+        table.attach(self._zfw, 1, 2, 1, 2)
+
+        (label, self._grossWeight) = self._createLabeledEntry("Weight:", 6)
+        table.attach(label, 2, 3, 1, 2)
+        table.attach(self._grossWeight, 3, 4, 1, 2)
+
+        (label, self._heading) = self._createLabeledEntry("Heading:", 3)
+        table.attach(label, 4, 5, 1, 2)
+        table.attach(self._heading, 5, 6, 1, 2)
+
+        (label, self._pitch) = self._createLabeledEntry("Pitch:", 3)
+        table.attach(label, 6, 7, 1, 2)
+        table.attach(self._pitch, 7, 8, 1, 2)
+
+        (label, self._bank) = self._createLabeledEntry("Bank:", 3)
+        table.attach(label, 8, 9, 1, 2)
+        table.attach(self._bank, 9, 10, 1, 2)
+
+        (label, self._vs) = self._createLabeledEntry("VS:", 13)
+        table.attach(label, 10, 11, 1, 2)
+        table.attach(self._vs, 11, 12, 1, 2)
+
+        (label, self._ias) = self._createLabeledEntry("IAS:", 11)
+        table.attach(label, 0, 1, 2, 3)
+        table.attach(self._ias, 1, 2, 2, 3)
+
+        (label, self._mach) = self._createLabeledEntry("Mach:", 4)
+        table.attach(label, 2, 3, 2, 3)
+        table.attach(self._mach, 3, 4, 2, 3)
+
+        (label, self._groundSpeed) = self._createLabeledEntry("GS:", 4)
+        table.attach(label, 4, 5, 2, 3)
+        table.attach(self._groundSpeed, 5, 6, 2, 3)
+
+        (label, self._radioAltitude) = self._createLabeledEntry("Radio alt.:", 6)
+        table.attach(label, 6, 7, 2, 3)
+        table.attach(self._radioAltitude, 7, 8, 2, 3)
+
+        (label, self._altitude) = self._createLabeledEntry("Altitude:", 6)
+        table.attach(label, 8, 9, 2, 3)
+        table.attach(self._altitude, 9, 10, 2, 3)
+
+        (label, self._gLoad) = self._createLabeledEntry("G-Load:", 4)
+        table.attach(label, 10, 11, 2, 3)
+        table.attach(self._gLoad, 11, 12, 2, 3)
+
+        (label, self._flapsSet) = self._createLabeledEntry("Flaps set:", 2)
+        table.attach(label, 0, 1, 3, 4)
+        table.attach(self._flapsSet, 1, 2, 3, 4)
+
+        (label, self._flaps) = self._createLabeledEntry("Flaps:", 2)
+        table.attach(label, 2, 3, 3, 4)
+        table.attach(self._flaps, 3, 4, 3, 4)
+
+        (label, self._altimeter) = self._createLabeledEntry("Altimeter:", 4)
+        table.attach(label, 4, 5, 3, 4)
+        table.attach(self._altimeter, 5, 6, 3, 4)
+
+        (label, self._squawk) = self._createLabeledEntry("Squawk:", 4)
+        table.attach(label, 6, 7, 3, 4)
+        table.attach(self._squawk, 7, 8, 3, 4)
+
+        (label, self._nav1) = self._createLabeledEntry("NAV1:", 5)
+        table.attach(label, 8, 9, 3, 4)
+        table.attach(self._nav1, 9, 10, 3, 4)
+
+        (label, self._nav2) = self._createLabeledEntry("NAV2:", 5)
+        table.attach(label, 10, 11, 3, 4)
+        table.attach(self._nav2, 11, 12, 3, 4)
+
+        (label, self._fuel) = self._createLabeledEntry("Fuel:", 40, xalign = 0.0)
+        table.attach(label, 0, 1, 4, 5)
+        table.attach(self._fuel, 1, 4, 4, 5)
+
+        (label, self._n1) = self._createLabeledEntry("N1/RPM:", 20, xalign = 0.0)
+        table.attach(label, 4, 5, 4, 5)
+        table.attach(self._n1, 5, 8, 4, 5)
+
+        (label, self._reverser) = self._createLabeledEntry("Reverser:", 20, xalign = 0.0)
+        table.attach(label, 8, 9, 4, 5)
+        table.attach(self._reverser, 9, 12, 4, 5)
+
+        self._navLightsOn = gtk.Label("NAV")
+        table.attach(self._navLightsOn, 0, 1, 5, 6)
+
+        self._antiCollisionLightsOn = gtk.Label("ANTICOLLISION")
+        table.attach(self._antiCollisionLightsOn, 1, 2, 5, 6)
+
+        self._strobeLightsOn = gtk.Label("STROBE")
+        table.attach(self._strobeLightsOn, 2, 3, 5, 6)
+
+        self._landingLightsOn = gtk.Label("LANDING")
+        table.attach(self._landingLightsOn, 3, 4, 5, 6)
+
+        self._pitotHeatOn = gtk.Label("PITOT HEAT")
+        table.attach(self._pitotHeatOn, 4, 5, 5, 6)
+
+        self._parking = gtk.Label("PARKING")
+        table.attach(self._parking, 5, 6, 5, 6)
+
+        self._gearControlDown = gtk.Label("GEAR LEVER DOWN")
+        table.attach(self._gearControlDown, 6, 8, 5, 6)
+
+        self._gearsDown = gtk.Label("GEARS DOWN")
+        table.attach(self._gearsDown, 8, 10, 5, 6)
+
+        self._spoilersArmed = gtk.Label("SPOILERS ARMED")
+        table.attach(self._spoilersArmed, 10, 12, 5, 6)
+
+        (label, self._spoilersExtension) = self._createLabeledEntry("Spoilers:", 3)
+        table.attach(label, 0, 1, 6, 7)
+        table.attach(self._spoilersExtension, 1, 2, 6, 7)
+
+        (label, self._windSpeed) = self._createLabeledEntry("Wind speed:", 3)
+        table.attach(label, 2, 3, 6, 7)
+        table.attach(self._windSpeed, 3, 4, 6, 7)
+
+        (label, self._windDirection) = self._createLabeledEntry("Wind from:", 3)
+        table.attach(label, 4, 5, 6, 7)
+        table.attach(self._windDirection, 5, 6, 6, 7)
+
+        (label, self._position) = self._createLabeledEntry("Position:", 25)
+        table.attach(label, 6, 7, 6, 7)
+        table.attach(self._position, 7, 10, 6, 7)
+
+        (label, self._elevatorTrim) = self._createLabeledEntry("Trim:", 5)
+        table.attach(label, 10, 11, 6, 7)
+        table.attach(self._elevatorTrim, 11, 12, 6, 7)
+
+        self._apMaster = gtk.Label("AP MASTER")
+        table.attach(self._apMaster, 0, 1, 7, 8)
+
+        self._apHeadingHold = gtk.Label("AP HDG HOLD")
+        table.attach(self._apHeadingHold, 1, 2, 7, 8)
+
+        (label, self._apHeading) = self._createLabeledEntry("AP HDG:", 5)
+        table.attach(label, 2, 3, 7, 8)
+        table.attach(self._apHeading, 3, 4, 7, 8)
+
+        self._xpdrC = gtk.Label("XPDR C")
+        table.attach(self._xpdrC, 4, 5, 7, 8)
+
+        self._apAltitudeHold = gtk.Label("AP ALT HOLD")
+        table.attach(self._apAltitudeHold, 5, 6, 7, 8)
+
+        (label, self._apAltitude) = self._createLabeledEntry("AP ALT:", 10)
+        table.attach(label, 6, 7, 7, 8)
+        table.attach(self._apAltitude, 7, 8, 7, 8)
+
+        (label, self._adf1) = self._createLabeledEntry("ADF1:", 5)
+        table.attach(label, 8, 9, 7, 8)
+        table.attach(self._adf1, 9, 10, 7, 8)
+
+        (label, self._adf2) = self._createLabeledEntry("ADF2:", 5)
+        table.attach(label, 10, 11, 7, 8)
+        table.attach(self._adf2, 11, 12, 7, 8)
+
+        (label, self._qnh) = self._createLabeledEntry("QNH:", 7)
+        table.attach(label, 0, 1, 8, 9)
+        table.attach(self._qnh, 1, 2, 8, 9)
+
+        (label, self._cog) = self._createLabeledEntry("CoG:", 7)
+        table.attach(label, 2, 3, 8, 9)
+        table.attach(self._cog, 3, 4, 8, 9)
+
+        (label, self._ils) = self._createLabeledEntry("ILS:", 7)
+        table.attach(label, 4, 5, 8, 9)
+        table.attach(self._ils, 5, 6, 8, 9)
+
+        (label, self._crs1) = self._createLabeledEntry("CRS1:", 7)
+        table.attach(label, 6, 7, 8, 9)
+        table.attach(self._crs1, 7, 8, 8, 9)
+
+        (label, self._crs2) = self._createLabeledEntry("CRS2:", 7)
+        table.attach(label, 8, 9, 8, 9)
+        table.attach(self._crs2, 9, 10, 8, 9)
+
+        (label, self._crsi) = self._createLabeledEntry("CRSI:", 7)
+        table.attach(label, 10, 11, 8, 9)
+        table.attach(self._crsi, 11, 12, 8, 9)
+
+        alignment.add(table)
+
+        self.add(alignment)
+
+        self._previousState = None
+
+        self.setData()
+
+    def _createLabeledEntry(self, label, width = 8, xalign = 1.0):
+        """Create a labeled entry.
+
+        Return a tuple consisting of:
+        - the box
+        - the entry."""
+
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5, xscale = 1.0)
+        alignment.set_padding(padding_top = 0, padding_bottom = 0,
+                              padding_left = 0, padding_right = 16)
+        alignment.add(gtk.Label(label))
+
+        entry = gtk.Entry()
+        entry.set_editable(False)
+        entry.set_width_chars(width)
+        entry.set_max_length(width)
+        entry.set_alignment(xalign)
+
+        return (alignment, entry)
+
+    def setData(self, aircraftState = None):
+        """Set the data.
+
+        If aircraftState is None, everything will be set to its default."""
+        if aircraftState is None:
+            self._timestamp.set_text("--:--:--")
+            self._paused.set_sensitive(False)
+            self._trickMode.set_sensitive(False)
+            self._overspeed.set_sensitive(False)
+            self._stalled.set_sensitive(False)
+            self._onTheGround.set_sensitive(False)
+            self._antiIceOn.set_sensitive(False)
+            self._zfw.set_text("-")
+            self._grossWeight.set_text("-")
+            self._heading.set_text("-")
+            self._pitch.set_text("-")
+            self._bank.set_text("-")
+            self._vs.set_text("-")
+            self._ias.set_text("-")
+            self._mach.set_text("-")
+            self._groundSpeed.set_text("-")
+            self._radioAltitude.set_text("-")
+            self._altitude.set_text("-")
+            self._gLoad.set_text("-")
+            self._flapsSet.set_text("-")
+            self._flaps.set_text("-")
+            self._altimeter.set_text("-")
+            self._squawk.set_text("-")
+            self._nav1.set_text("-")
+            self._nav2.set_text("-")
+            self._fuel.set_text("-")
+            self._n1.set_text("-")
+            self._reverser.set_text("-")
+            self._navLightsOn.set_sensitive(False)
+            self._antiCollisionLightsOn.set_sensitive(False)
+            self._strobeLightsOn.set_sensitive(False)
+            self._landingLightsOn.set_text("LANDING")
+            self._landingLightsOn.set_sensitive(False)
+            self._pitotHeatOn.set_sensitive(False)
+            self._parking.set_sensitive(False)
+            self._gearControlDown.set_sensitive(False)
+            self._gearsDown.set_sensitive(False)
+            self._spoilersArmed.set_sensitive(False)
+            self._spoilersExtension.set_text("-")
+            self._windSpeed.set_text("-")
+            self._windDirection.set_text("-")
+            self._position.set_text("-")
+            self._elevatorTrim.set_text("-")
+            self._apMaster.set_sensitive(False)
+            self._apHeadingHold.set_sensitive(False)
+            self._apHeading.set_text("-")
+            self._xpdrC.set_sensitive(False)
+            self._apAltitudeHold.set_sensitive(False)
+            self._apAltitude.set_text("-")
+            self._adf1.set_text("-")
+            self._adf2.set_text("-")
+            self._qnh.set_text("-")
+            self._cog.set_text("-")
+            self._ils.set_text("-")
+            self._crs1.set_text("-")
+            self._crs2.set_text("-")
+            self._crsi.set_text("-")
+        else:
+            self._timestamp.set_text(time.strftime("%H:%M:%S",
+                                                   time.gmtime(aircraftState.timestamp)))
+            self._paused.set_sensitive(aircraftState.paused)
+            self._trickMode.set_sensitive(aircraftState.trickMode)
+            self._overspeed.set_sensitive(aircraftState.overspeed)
+            self._stalled.set_sensitive(aircraftState.stalled)
+            self._onTheGround.set_sensitive(aircraftState.onTheGround)
+            self._antiIceOn.set_sensitive(aircraftState.antiIceOn is True)
+            self._zfw.set_text("%.0f" % (aircraftState.zfw,))
+            self._grossWeight.set_text("%.0f" % (aircraftState.grossWeight,))
+            self._heading.set_text("%03.0f" % (aircraftState.heading,))
+            self._pitch.set_text("%.0f" % (aircraftState.pitch,))
+            self._bank.set_text("%.0f" % (aircraftState.bank,))
+            self._vs.set_text("%.0f (%.0f)" % (aircraftState.vs,
+                                               aircraftState.smoothedVS))
+            self._ias.set_text("%.0f (%.0f)" % (aircraftState.ias,
+                                                aircraftState.smoothedIAS))
+            self._mach.set_text("%.2f" % (aircraftState.mach,))
+            self._groundSpeed.set_text("%.0f" % (aircraftState.groundSpeed,))
+            self._radioAltitude.set_text("%.0f" % (aircraftState.radioAltitude,))
+            self._altitude.set_text("%.0f" % (aircraftState.altitude,))
+            self._gLoad.set_text("%.2f" % (aircraftState.gLoad,))
+            self._flapsSet.set_text("%.0f" % (aircraftState.flapsSet,))
+            self._flaps.set_text("%.0f" % (aircraftState.flaps,))
+            self._altimeter.set_text("%.0f" % (aircraftState.altimeter,))
+            self._squawk.set_text(aircraftState.squawk)
+            self._nav1.set_text("-" if aircraftState.nav1 is None else aircraftState.nav1)
+            self._nav2.set_text("-" if aircraftState.nav2 is None else aircraftState.nav2)
+            self._qnh.set_text("%.0f" % (aircraftState.qnh,))
+
+            fuelStr = ""
+            for (_tank, fuel) in aircraftState.fuel:
+                if fuelStr: fuelStr += ", "
+                fuelStr += "%.0f" % (fuel,)
+            self._fuel.set_text(fuelStr)
+
+            if aircraftState.n1 is not None:
+                n1Str = ""
+                for n1 in aircraftState.n1:
+                    if n1Str: n1Str += ", "
+                    if n1 is None:
+                        n1Str += "?"
+                    else:
+                        n1Str += "%.0f" % (n1,)
+            elif aircraftState.rpm is not None:
+                n1Str = ""
+                for rpm in aircraftState.rpm:
+                    if n1Str: n1Str += ", "
+                    n1Str += "%.0f" % (rpm,)
+            else:
+                n1Str = "-"
+            self._n1.set_text(n1Str)
+
+            reverserStr = ""
+            for reverser in aircraftState.reverser:
+                if reverserStr: reverserStr += ", "
+                reverserStr += "ON" if reverser else "OFF"
+            self._reverser.set_text(reverserStr)
+
+            self._navLightsOn.set_sensitive(aircraftState.navLightsOn is True)
+            self._antiCollisionLightsOn.set_sensitive(aircraftState.antiCollisionLightsOn is True)
+            self._strobeLightsOn.set_sensitive(aircraftState.strobeLightsOn is True)
+
+            if self._previousState is None or \
+               ((self._previousState.landingLightsOn is None)!=
+                (aircraftState.landingLightsOn is None)):
+                if aircraftState.landingLightsOn is None:
+                    self._landingLightsOn.set_markup('<span strikethrough="true">LANDING</span>')
+                else:
+                    self._landingLightsOn.set_text("LANDING")
+            self._landingLightsOn.set_sensitive(aircraftState.landingLightsOn is True)
+
+            self._pitotHeatOn.set_sensitive(aircraftState.pitotHeatOn is True)
+            self._parking.set_sensitive(aircraftState.parking)
+            self._gearControlDown.set_sensitive(aircraftState.gearControlDown)
+            self._gearsDown.set_sensitive(aircraftState.gearsDown)
+            self._spoilersArmed.set_sensitive(aircraftState.spoilersArmed is True)
+            self._spoilersExtension.set_text("%.0f" % (aircraftState.spoilersExtension,))
+            self._windSpeed.set_text("%.0f" % (aircraftState.windSpeed,))
+            self._windDirection.set_text("%03.0f" % (aircraftState.windDirection,))
+            self._position.set_text(util.getCoordinateString((aircraftState.latitude,
+                                                              aircraftState.longitude)))
+            self._elevatorTrim.set_text("%.2f" % (aircraftState.elevatorTrim,))
+            self._apMaster.set_sensitive(aircraftState.apMaster is True)
+            self._apHeadingHold.set_sensitive(aircraftState.apHeadingHold is True)
+            self._apHeading.set_text("-" if aircraftState.apHeading is None
+                                     else "%03.0f" % (aircraftState.apHeading,))
+            self._xpdrC.set_sensitive(aircraftState.xpdrC is True)
+            self._apAltitudeHold.set_sensitive(aircraftState.apAltitudeHold is True)
+            self._apAltitude.set_text("-" if aircraftState.apAltitude is None
+                                      else "%5.0f" % (aircraftState.apAltitude,))
+
+            self._adf1.set_text("-" if aircraftState.adf1 is None else aircraftState.adf1)
+            self._adf2.set_text("-" if aircraftState.adf2 is None else aircraftState.adf2)
+            self._cog.set_text("%.2f%%" % (aircraftState.cog*100.0,))
+
+            self._ils.set_text("-" if aircraftState.ils is None else aircraftState.ils)
+
+            self._crs1.set_text("-" if aircraftState.nav1_obs is None else str(aircraftState.nav1_obs))
+            self._crs2.set_text("-" if aircraftState.nav2_obs is None else str(aircraftState.nav2_obs))
+            self._crsi.set_text("-" if aircraftState.ils_obs is None else str(aircraftState.ils_obs))
+
+#------------------------------------------------------------------------------
Index: src/mlx/gui/monitor.py
===================================================================
--- src/mlx/gui/monitor.py	(revision 765)
+++ 	(revision )
@@ -1,442 +1,0 @@
-
-from mlx.gui.common import *
-
-import mlx.const as const
-import mlx.util as util
-
-import time
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.monitor
-#
-# The monitoring window
-#
-# The \ref MonitorWindow class is a window containing the data received from
-# the simulator by the logger.
-
-#------------------------------------------------------------------------------
-
-class MonitorWindow(gtk.Window):
-    """The window for the data monitor."""
-    def __init__(self, gui, iconDirectory):
-        """Construct the monitor window."""
-        super(MonitorWindow, self).__init__()
-
-        self._gui = gui
-
-        self.set_resizable(False)
-        self.set_title(WINDOW_TITLE_BASE + " - Data Monitor")
-        self.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
-        self.connect("delete-event",
-                     lambda a, b: self._gui.hideMonitorWindow())
-
-        alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
-
-        alignment.set_padding(padding_top = 4, padding_bottom = 10,
-                              padding_left = 16, padding_right = 16)
-
-        table = gtk.Table(rows = 9, columns = 14)
-        table.set_homogeneous(False)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-
-        (label, self._timestamp) = self._createLabeledEntry("Time:")
-        table.attach(label, 0, 1, 0, 1)
-        table.attach(self._timestamp, 1, 2, 0, 1)
-
-        self._paused = gtk.Label("PAUSED")
-        table.attach(self._paused, 2, 4, 0, 1)
-
-        self._trickMode = gtk.Label("TRICKMODE")
-        table.attach(self._trickMode, 4, 6, 0, 1, xoptions = 0)
-
-        self._overspeed = gtk.Label("OVERSPEED")
-        table.attach(self._overspeed, 6, 8, 0, 1)
-
-        self._stalled = gtk.Label("STALLED")
-        table.attach(self._stalled, 8, 10, 0, 1)
-
-        self._onTheGround = gtk.Label("ONTHEGROUND")
-        table.attach(self._onTheGround, 10, 11, 0, 1)
-
-        self._antiIceOn = gtk.Label("ANTI-ICE")
-        table.attach(self._antiIceOn, 11, 12, 0, 1)
-
-        (label, self._zfw) = self._createLabeledEntry("ZFW:", 6)
-        table.attach(label, 0, 1, 1, 2)
-        table.attach(self._zfw, 1, 2, 1, 2)
-
-        (label, self._grossWeight) = self._createLabeledEntry("Weight:", 6)
-        table.attach(label, 2, 3, 1, 2)
-        table.attach(self._grossWeight, 3, 4, 1, 2)
-
-        (label, self._heading) = self._createLabeledEntry("Heading:", 3)
-        table.attach(label, 4, 5, 1, 2)
-        table.attach(self._heading, 5, 6, 1, 2)
-
-        (label, self._pitch) = self._createLabeledEntry("Pitch:", 3)
-        table.attach(label, 6, 7, 1, 2)
-        table.attach(self._pitch, 7, 8, 1, 2)
-
-        (label, self._bank) = self._createLabeledEntry("Bank:", 3)
-        table.attach(label, 8, 9, 1, 2)
-        table.attach(self._bank, 9, 10, 1, 2)
-
-        (label, self._vs) = self._createLabeledEntry("VS:", 13)
-        table.attach(label, 10, 11, 1, 2)
-        table.attach(self._vs, 11, 12, 1, 2)
-
-        (label, self._ias) = self._createLabeledEntry("IAS:", 11)
-        table.attach(label, 0, 1, 2, 3)
-        table.attach(self._ias, 1, 2, 2, 3)
-
-        (label, self._mach) = self._createLabeledEntry("Mach:", 4)
-        table.attach(label, 2, 3, 2, 3)
-        table.attach(self._mach, 3, 4, 2, 3)
-
-        (label, self._groundSpeed) = self._createLabeledEntry("GS:", 4)
-        table.attach(label, 4, 5, 2, 3)
-        table.attach(self._groundSpeed, 5, 6, 2, 3)
-
-        (label, self._radioAltitude) = self._createLabeledEntry("Radio alt.:", 6)
-        table.attach(label, 6, 7, 2, 3)
-        table.attach(self._radioAltitude, 7, 8, 2, 3)
-
-        (label, self._altitude) = self._createLabeledEntry("Altitude:", 6)
-        table.attach(label, 8, 9, 2, 3)
-        table.attach(self._altitude, 9, 10, 2, 3)
-
-        (label, self._gLoad) = self._createLabeledEntry("G-Load:", 4)
-        table.attach(label, 10, 11, 2, 3)
-        table.attach(self._gLoad, 11, 12, 2, 3)
-
-        (label, self._flapsSet) = self._createLabeledEntry("Flaps set:", 2)
-        table.attach(label, 0, 1, 3, 4)
-        table.attach(self._flapsSet, 1, 2, 3, 4)
-
-        (label, self._flaps) = self._createLabeledEntry("Flaps:", 2)
-        table.attach(label, 2, 3, 3, 4)
-        table.attach(self._flaps, 3, 4, 3, 4)
-
-        (label, self._altimeter) = self._createLabeledEntry("Altimeter:", 4)
-        table.attach(label, 4, 5, 3, 4)
-        table.attach(self._altimeter, 5, 6, 3, 4)
-
-        (label, self._squawk) = self._createLabeledEntry("Squawk:", 4)
-        table.attach(label, 6, 7, 3, 4)
-        table.attach(self._squawk, 7, 8, 3, 4)
-
-        (label, self._nav1) = self._createLabeledEntry("NAV1:", 5)
-        table.attach(label, 8, 9, 3, 4)
-        table.attach(self._nav1, 9, 10, 3, 4)
-
-        (label, self._nav2) = self._createLabeledEntry("NAV2:", 5)
-        table.attach(label, 10, 11, 3, 4)
-        table.attach(self._nav2, 11, 12, 3, 4)
-
-        (label, self._fuel) = self._createLabeledEntry("Fuel:", 40, xalign = 0.0)
-        table.attach(label, 0, 1, 4, 5)
-        table.attach(self._fuel, 1, 4, 4, 5)
-
-        (label, self._n1) = self._createLabeledEntry("N1/RPM:", 20, xalign = 0.0)
-        table.attach(label, 4, 5, 4, 5)
-        table.attach(self._n1, 5, 8, 4, 5)
-
-        (label, self._reverser) = self._createLabeledEntry("Reverser:", 20, xalign = 0.0)
-        table.attach(label, 8, 9, 4, 5)
-        table.attach(self._reverser, 9, 12, 4, 5)
-
-        self._navLightsOn = gtk.Label("NAV")
-        table.attach(self._navLightsOn, 0, 1, 5, 6)
-
-        self._antiCollisionLightsOn = gtk.Label("ANTICOLLISION")
-        table.attach(self._antiCollisionLightsOn, 1, 2, 5, 6)
-
-        self._strobeLightsOn = gtk.Label("STROBE")
-        table.attach(self._strobeLightsOn, 2, 3, 5, 6)
-
-        self._landingLightsOn = gtk.Label("LANDING")
-        table.attach(self._landingLightsOn, 3, 4, 5, 6)
-
-        self._pitotHeatOn = gtk.Label("PITOT HEAT")
-        table.attach(self._pitotHeatOn, 4, 5, 5, 6)
-
-        self._parking = gtk.Label("PARKING")
-        table.attach(self._parking, 5, 6, 5, 6)
-
-        self._gearControlDown = gtk.Label("GEAR LEVER DOWN")
-        table.attach(self._gearControlDown, 6, 8, 5, 6)
-
-        self._gearsDown = gtk.Label("GEARS DOWN")
-        table.attach(self._gearsDown, 8, 10, 5, 6)
-
-        self._spoilersArmed = gtk.Label("SPOILERS ARMED")
-        table.attach(self._spoilersArmed, 10, 12, 5, 6)
-
-        (label, self._spoilersExtension) = self._createLabeledEntry("Spoilers:", 3)
-        table.attach(label, 0, 1, 6, 7)
-        table.attach(self._spoilersExtension, 1, 2, 6, 7)
-
-        (label, self._windSpeed) = self._createLabeledEntry("Wind speed:", 3)
-        table.attach(label, 2, 3, 6, 7)
-        table.attach(self._windSpeed, 3, 4, 6, 7)
-
-        (label, self._windDirection) = self._createLabeledEntry("Wind from:", 3)
-        table.attach(label, 4, 5, 6, 7)
-        table.attach(self._windDirection, 5, 6, 6, 7)
-
-        (label, self._position) = self._createLabeledEntry("Position:", 25)
-        table.attach(label, 6, 7, 6, 7)
-        table.attach(self._position, 7, 10, 6, 7)
-
-        (label, self._elevatorTrim) = self._createLabeledEntry("Trim:", 5)
-        table.attach(label, 10, 11, 6, 7)
-        table.attach(self._elevatorTrim, 11, 12, 6, 7)
-
-        self._apMaster = gtk.Label("AP MASTER")
-        table.attach(self._apMaster, 0, 1, 7, 8)
-
-        self._apHeadingHold = gtk.Label("AP HDG HOLD")
-        table.attach(self._apHeadingHold, 1, 2, 7, 8)
-
-        (label, self._apHeading) = self._createLabeledEntry("AP HDG:", 5)
-        table.attach(label, 2, 3, 7, 8)
-        table.attach(self._apHeading, 3, 4, 7, 8)
-
-        self._xpdrC = gtk.Label("XPDR C")
-        table.attach(self._xpdrC, 4, 5, 7, 8)
-
-        self._apAltitudeHold = gtk.Label("AP ALT HOLD")
-        table.attach(self._apAltitudeHold, 5, 6, 7, 8)
-
-        (label, self._apAltitude) = self._createLabeledEntry("AP ALT:", 10)
-        table.attach(label, 6, 7, 7, 8)
-        table.attach(self._apAltitude, 7, 8, 7, 8)
-
-        (label, self._adf1) = self._createLabeledEntry("ADF1:", 5)
-        table.attach(label, 8, 9, 7, 8)
-        table.attach(self._adf1, 9, 10, 7, 8)
-
-        (label, self._adf2) = self._createLabeledEntry("ADF2:", 5)
-        table.attach(label, 10, 11, 7, 8)
-        table.attach(self._adf2, 11, 12, 7, 8)
-
-        (label, self._qnh) = self._createLabeledEntry("QNH:", 7)
-        table.attach(label, 0, 1, 8, 9)
-        table.attach(self._qnh, 1, 2, 8, 9)
-
-        (label, self._cog) = self._createLabeledEntry("CoG:", 7)
-        table.attach(label, 2, 3, 8, 9)
-        table.attach(self._cog, 3, 4, 8, 9)
-
-        (label, self._ils) = self._createLabeledEntry("ILS:", 7)
-        table.attach(label, 4, 5, 8, 9)
-        table.attach(self._ils, 5, 6, 8, 9)
-
-        (label, self._crs1) = self._createLabeledEntry("CRS1:", 7)
-        table.attach(label, 6, 7, 8, 9)
-        table.attach(self._crs1, 7, 8, 8, 9)
-
-        (label, self._crs2) = self._createLabeledEntry("CRS2:", 7)
-        table.attach(label, 8, 9, 8, 9)
-        table.attach(self._crs2, 9, 10, 8, 9)
-
-        (label, self._crsi) = self._createLabeledEntry("CRSI:", 7)
-        table.attach(label, 10, 11, 8, 9)
-        table.attach(self._crsi, 11, 12, 8, 9)
-
-        alignment.add(table)
-
-        self.add(alignment)
-
-        self._previousState = None
-
-        self.setData()
-
-    def _createLabeledEntry(self, label, width = 8, xalign = 1.0):
-        """Create a labeled entry.
-
-        Return a tuple consisting of:
-        - the box
-        - the entry."""
-
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5, xscale = 1.0)
-        alignment.set_padding(padding_top = 0, padding_bottom = 0,
-                              padding_left = 0, padding_right = 16)
-        alignment.add(gtk.Label(label))
-
-        entry = gtk.Entry()
-        entry.set_editable(False)
-        entry.set_width_chars(width)
-        entry.set_max_length(width)
-        entry.set_alignment(xalign)
-
-        return (alignment, entry)
-
-    def setData(self, aircraftState = None):
-        """Set the data.
-
-        If aircraftState is None, everything will be set to its default."""
-        if aircraftState is None:
-            self._timestamp.set_text("--:--:--")
-            self._paused.set_sensitive(False)
-            self._trickMode.set_sensitive(False)
-            self._overspeed.set_sensitive(False)
-            self._stalled.set_sensitive(False)
-            self._onTheGround.set_sensitive(False)
-            self._antiIceOn.set_sensitive(False)
-            self._zfw.set_text("-")
-            self._grossWeight.set_text("-")
-            self._heading.set_text("-")
-            self._pitch.set_text("-")
-            self._bank.set_text("-")
-            self._vs.set_text("-")
-            self._ias.set_text("-")
-            self._mach.set_text("-")
-            self._groundSpeed.set_text("-")
-            self._radioAltitude.set_text("-")
-            self._altitude.set_text("-")
-            self._gLoad.set_text("-")
-            self._flapsSet.set_text("-")
-            self._flaps.set_text("-")
-            self._altimeter.set_text("-")
-            self._squawk.set_text("-")
-            self._nav1.set_text("-")
-            self._nav2.set_text("-")
-            self._fuel.set_text("-")
-            self._n1.set_text("-")
-            self._reverser.set_text("-")
-            self._navLightsOn.set_sensitive(False)
-            self._antiCollisionLightsOn.set_sensitive(False)
-            self._strobeLightsOn.set_sensitive(False)
-            self._landingLightsOn.set_text("LANDING")
-            self._landingLightsOn.set_sensitive(False)
-            self._pitotHeatOn.set_sensitive(False)
-            self._parking.set_sensitive(False)
-            self._gearControlDown.set_sensitive(False)
-            self._gearsDown.set_sensitive(False)
-            self._spoilersArmed.set_sensitive(False)
-            self._spoilersExtension.set_text("-")
-            self._windSpeed.set_text("-")
-            self._windDirection.set_text("-")
-            self._position.set_text("-")
-            self._elevatorTrim.set_text("-")
-            self._apMaster.set_sensitive(False)
-            self._apHeadingHold.set_sensitive(False)
-            self._apHeading.set_text("-")
-            self._xpdrC.set_sensitive(False)
-            self._apAltitudeHold.set_sensitive(False)
-            self._apAltitude.set_text("-")
-            self._adf1.set_text("-")
-            self._adf2.set_text("-")
-            self._qnh.set_text("-")
-            self._cog.set_text("-")
-            self._ils.set_text("-")
-            self._crs1.set_text("-")
-            self._crs2.set_text("-")
-            self._crsi.set_text("-")
-        else:
-            self._timestamp.set_text(time.strftime("%H:%M:%S",
-                                                   time.gmtime(aircraftState.timestamp)))
-            self._paused.set_sensitive(aircraftState.paused)
-            self._trickMode.set_sensitive(aircraftState.trickMode)
-            self._overspeed.set_sensitive(aircraftState.overspeed)
-            self._stalled.set_sensitive(aircraftState.stalled)
-            self._onTheGround.set_sensitive(aircraftState.onTheGround)
-            self._antiIceOn.set_sensitive(aircraftState.antiIceOn is True)
-            self._zfw.set_text("%.0f" % (aircraftState.zfw,))
-            self._grossWeight.set_text("%.0f" % (aircraftState.grossWeight,))
-            self._heading.set_text("%03.0f" % (aircraftState.heading,))
-            self._pitch.set_text("%.0f" % (aircraftState.pitch,))
-            self._bank.set_text("%.0f" % (aircraftState.bank,))
-            self._vs.set_text("%.0f (%.0f)" % (aircraftState.vs,
-                                               aircraftState.smoothedVS))
-            self._ias.set_text("%.0f (%.0f)" % (aircraftState.ias,
-                                                aircraftState.smoothedIAS))
-            self._mach.set_text("%.2f" % (aircraftState.mach,))
-            self._groundSpeed.set_text("%.0f" % (aircraftState.groundSpeed,))
-            self._radioAltitude.set_text("%.0f" % (aircraftState.radioAltitude,))
-            self._altitude.set_text("%.0f" % (aircraftState.altitude,))
-            self._gLoad.set_text("%.2f" % (aircraftState.gLoad,))
-            self._flapsSet.set_text("%.0f" % (aircraftState.flapsSet,))
-            self._flaps.set_text("%.0f" % (aircraftState.flaps,))
-            self._altimeter.set_text("%.0f" % (aircraftState.altimeter,))
-            self._squawk.set_text(aircraftState.squawk)
-            self._nav1.set_text("-" if aircraftState.nav1 is None else aircraftState.nav1)
-            self._nav2.set_text("-" if aircraftState.nav2 is None else aircraftState.nav2)
-            self._qnh.set_text("%.0f" % (aircraftState.qnh,))
-
-            fuelStr = ""
-            for (_tank, fuel) in aircraftState.fuel:
-                if fuelStr: fuelStr += ", "
-                fuelStr += "%.0f" % (fuel,)
-            self._fuel.set_text(fuelStr)
-
-            if aircraftState.n1 is not None:
-                n1Str = ""
-                for n1 in aircraftState.n1:
-                    if n1Str: n1Str += ", "
-                    if n1 is None:
-                        n1Str += "?"
-                    else:
-                        n1Str += "%.0f" % (n1,)
-            elif aircraftState.rpm is not None:
-                n1Str = ""
-                for rpm in aircraftState.rpm:
-                    if n1Str: n1Str += ", "
-                    n1Str += "%.0f" % (rpm,)
-            else:
-                n1Str = "-"
-            self._n1.set_text(n1Str)
-
-            reverserStr = ""
-            for reverser in aircraftState.reverser:
-                if reverserStr: reverserStr += ", "
-                reverserStr += "ON" if reverser else "OFF"
-            self._reverser.set_text(reverserStr)
-
-            self._navLightsOn.set_sensitive(aircraftState.navLightsOn is True)
-            self._antiCollisionLightsOn.set_sensitive(aircraftState.antiCollisionLightsOn is True)
-            self._strobeLightsOn.set_sensitive(aircraftState.strobeLightsOn is True)
-
-            if self._previousState is None or \
-               ((self._previousState.landingLightsOn is None)!=
-                (aircraftState.landingLightsOn is None)):
-                if aircraftState.landingLightsOn is None:
-                    self._landingLightsOn.set_markup('<span strikethrough="true">LANDING</span>')
-                else:
-                    self._landingLightsOn.set_text("LANDING")
-            self._landingLightsOn.set_sensitive(aircraftState.landingLightsOn is True)
-
-            self._pitotHeatOn.set_sensitive(aircraftState.pitotHeatOn is True)
-            self._parking.set_sensitive(aircraftState.parking)
-            self._gearControlDown.set_sensitive(aircraftState.gearControlDown)
-            self._gearsDown.set_sensitive(aircraftState.gearsDown)
-            self._spoilersArmed.set_sensitive(aircraftState.spoilersArmed is True)
-            self._spoilersExtension.set_text("%.0f" % (aircraftState.spoilersExtension,))
-            self._windSpeed.set_text("%.0f" % (aircraftState.windSpeed,))
-            self._windDirection.set_text("%03.0f" % (aircraftState.windDirection,))
-            self._position.set_text(util.getCoordinateString((aircraftState.latitude,
-                                                              aircraftState.longitude)))
-            self._elevatorTrim.set_text("%.2f" % (aircraftState.elevatorTrim,))
-            self._apMaster.set_sensitive(aircraftState.apMaster is True)
-            self._apHeadingHold.set_sensitive(aircraftState.apHeadingHold is True)
-            self._apHeading.set_text("-" if aircraftState.apHeading is None
-                                     else "%03.0f" % (aircraftState.apHeading,))
-            self._xpdrC.set_sensitive(aircraftState.xpdrC is True)
-            self._apAltitudeHold.set_sensitive(aircraftState.apAltitudeHold is True)
-            self._apAltitude.set_text("-" if aircraftState.apAltitude is None
-                                      else "%5.0f" % (aircraftState.apAltitude,))
-
-            self._adf1.set_text("-" if aircraftState.adf1 is None else aircraftState.adf1)
-            self._adf2.set_text("-" if aircraftState.adf2 is None else aircraftState.adf2)
-            self._cog.set_text("%.2f%%" % (aircraftState.cog*100.0,))
-
-            self._ils.set_text("-" if aircraftState.ils is None else aircraftState.ils)
-
-            self._crs1.set_text("-" if aircraftState.nav1_obs is None else str(aircraftState.nav1_obs))
-            self._crs2.set_text("-" if aircraftState.nav2_obs is None else str(aircraftState.nav2_obs))
-            self._crsi.set_text("-" if aircraftState.ils_obs is None else str(aircraftState.ils_obs))
-
-#------------------------------------------------------------------------------
Index: src/mlx/gui/pirep.py
===================================================================
--- src/mlx/gui/pirep.py	(revision 855)
+++ src/mlx/gui/pirep.py	(revision 855)
@@ -0,0 +1,1574 @@
+
+from common import *
+from dcdata import getTable
+from info import FlightInfo
+from flight import comboModel
+
+from mlx.pirep import PIREP
+from mlx.flight import Flight
+from mlx.const import *
+
+import time
+import re
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.pirep
+#
+# The detailed PIREP viewer and editor windows.
+#
+# The \ref PIREPViewer class is a dialog displaying all information found in a
+# PIREP. It consists of three tabs. The Data tab displays the simple,
+# itemizable data. The Comments & defects tab contains the flight comments and
+# defects, while the Log tab contains the flight log collected by the
+# \ref mlx.logger.Logger "logger".
+
+#------------------------------------------------------------------------------
+
+class MessageFrame(gtk.Frame):
+    """A frame containing the information about a PIREP message.
+
+    It consists of a text view with the heading information (author, time) and
+    another text view with the actual message."""
+    def __init__(self, message, senderPID, senderName):
+        """Construct the frame."""
+        gtk.Frame.__init__(self)
+
+        vbox = gtk.VBox()
+
+        self._heading = heading = gtk.TextView()
+        heading.set_editable(False)
+        heading.set_can_focus(False)
+        heading.set_wrap_mode(WRAP_WORD)
+
+        buffer = heading.get_buffer()
+        self._headingTag  = buffer.create_tag("heading", weight=WEIGHT_BOLD)
+        buffer.set_text("%s - %s" % (senderPID, senderName))
+        buffer.apply_tag(self._headingTag,
+                         buffer.get_start_iter(), buffer.get_end_iter())
+
+        headingAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                         xscale = 1.0, yscale = 0.0)
+        headingAlignment.set_padding(padding_top = 0, padding_bottom = 0,
+                                     padding_left = 2, padding_right = 2)
+        headingAlignment.add(heading)
+        vbox.pack_start(headingAlignment, True, True, 4)
+
+        self._messageView = messageView = gtk.TextView()
+        messageView.set_wrap_mode(WRAP_WORD)
+        messageView.set_accepts_tab(False)
+        messageView.set_size_request(-1, 60)
+
+        buffer = messageView.get_buffer()
+        buffer.set_text(message)
+
+        vbox.pack_start(messageView, True, True, 4)
+
+        self.add(vbox)
+        self.show_all()
+
+        if pygobject:
+            styleContext = self.get_style_context()
+            color = styleContext.get_background_color(gtk.StateFlags.NORMAL)
+            heading.override_background_color(0, color)
+        else:
+            style = self.rc_get_style()
+            heading.modify_base(0, style.bg[0])
+
+
+#-------------------------------------------------------------------------------
+
+class MessagesWidget(gtk.Frame):
+    """The widget for the messages."""
+    @staticmethod
+    def getFaultFrame(alignment):
+        """Get the fault frame from the given alignment."""
+        return alignment.get_children()[0]
+
+    def __init__(self, gui):
+        gtk.Frame.__init__(self)
+
+        self._gui = gui
+        self.set_label(xstr("pirep_messages"))
+        label = self.get_label_widget()
+        label.set_use_underline(True)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 4,
+                              padding_left = 4, padding_right = 4)
+
+        self._outerBox = outerBox = gtk.EventBox()
+        outerBox.add(alignment)
+
+        self._innerBox = innerBox = gtk.EventBox()
+        alignment.add(self._innerBox)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 1.0, yscale = 1.0)
+        alignment.set_padding(padding_top = 0, padding_bottom = 0,
+                              padding_left = 0, padding_right = 0)
+
+        innerBox.add(alignment)
+
+        scroller = gtk.ScrolledWindow()
+        scroller.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+        scroller.set_shadow_type(SHADOW_NONE)
+
+        self._messages = gtk.VBox()
+        self._messages.set_homogeneous(False)
+        scroller.add_with_viewport(self._messages)
+
+        alignment.add(scroller)
+
+        self._messageWidgets = []
+
+        self.add(outerBox)
+        self.show_all()
+
+    def addMessage(self, message):
+        """Add a message from the given sender."""
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                      xscale = 1.0, yscale = 0.0)
+        alignment.set_padding(padding_top = 2, padding_bottom = 2,
+                              padding_left = 4, padding_right = 4)
+
+        messageFrame = MessageFrame(message.message,
+                                    message.senderPID,
+                                    message.senderName)
+
+        alignment.add(messageFrame)
+        self._messages.pack_start(alignment, False, False, 4)
+        self._messages.show_all()
+
+        self._messageWidgets.append((alignment, messageFrame))
+
+    def reset(self):
+        """Reset the widget by removing all messages."""
+        for (alignment, messageFrame) in self._messageWidgets:
+            self._messages.remove(alignment)
+        self._messages.show_all()
+
+        self._messageWidgets = []
+
+#------------------------------------------------------------------------------
+
+class PIREPViewer(gtk.Dialog):
+    """The dialog for PIREP viewing."""
+    @staticmethod
+    def createFrame(label):
+        """Create a frame with the given label.
+
+        The frame will contain an alignment to properly distance the
+        insides. The alignment will contain a VBox to contain the real
+        contents.
+
+        The function returns a tuple with the following items:
+        - the frame,
+        - the inner VBox."""
+        frame = gtk.Frame(label = label)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 1.0, yscale = 1.0)
+        frame.add(alignment)
+        alignment.set_padding(padding_top = 4, padding_bottom = 4,
+                              padding_left = 4, padding_right = 4)
+        box = gtk.VBox()
+        alignment.add(box)
+
+        return (frame, box)
+
+    @staticmethod
+    def getLabel(text, extraText = ""):
+        """Get a bold label with the given text."""
+        label = gtk.Label("<b>" + text + "</b>" + extraText)
+        label.set_use_markup(True)
+        label.set_alignment(0.0, 0.5)
+        return label
+
+    @staticmethod
+    def getDataLabel(width = None, xAlignment = 0.0):
+        """Get a bold label with the given text."""
+        label = gtk.Label()
+        if width is not None:
+            label.set_width_chars(width)
+        label.set_alignment(xAlignment, 0.5)
+        return label
+
+    @staticmethod
+    def getTextWindow(heightRequest = 40, editable = False):
+        """Get a scrollable text window.
+
+        Returns a tuple of the following items:
+        - the window,
+        - the text view."""
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.set_shadow_type(SHADOW_IN)
+        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+
+        textView = gtk.TextView()
+        textView.set_wrap_mode(WRAP_WORD)
+        textView.set_editable(editable)
+        textView.set_cursor_visible(editable)
+        textView.set_size_request(-1, heightRequest)
+        scrolledWindow.add(textView)
+
+        return (scrolledWindow, textView)
+
+    @staticmethod
+    def tableAttach(table, column, row, labelText, width = None,
+                    dataLabelXAlignment = 0.0):
+        """Attach a labeled data to the given column and row of the
+        table.
+
+        If width is given, that will be the width of the data
+        label.
+
+        Returns the data label attached."""
+        dataBox = gtk.HBox()
+        table.attach(dataBox, column, column+1, row, row+1)
+
+        dataLabel = PIREPViewer.addLabeledData(dataBox, labelText,
+                                               width = width)
+        dataLabel.set_alignment(dataLabelXAlignment, 0.5)
+
+        return dataLabel
+
+    @staticmethod
+    def addLabeledData(hBox, labelText, width = None, dataPadding = 8):
+        """Add a label and a data label to the given HBox.
+
+        Returns the data label."""
+        label = PIREPViewer.getLabel(labelText)
+        hBox.pack_start(label, False, False, 0)
+
+        dataLabel = PIREPViewer.getDataLabel(width = width)
+        hBox.pack_start(dataLabel, False, False, dataPadding)
+
+        return dataLabel
+
+    @staticmethod
+    def addHFiller(hBox, width = 8):
+        """Add a filler to the given horizontal box."""
+        filler = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                               xscale = 1.0, yscale = 1.0)
+        filler.set_size_request(width, -1)
+        hBox.pack_start(filler, False, False, 0)
+
+    @staticmethod
+    def addVFiller(vBox, height = 4):
+        """Add a filler to the given vertical box."""
+        filler = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                               xscale = 1.0, yscale = 1.0)
+        filler.set_size_request(-1, height)
+        vBox.pack_start(filler, False, False, 0)
+
+    @staticmethod
+    def timestamp2text(label, timestamp):
+        """Convert the given timestamp into a text containing the hour
+        and minute in UTC and put that text into the given label."""
+        tm = time.gmtime(timestamp)
+        label.set_text("%02d:%02d" % (tm.tm_hour, tm.tm_min))
+
+    def __init__(self, gui, showMessages = False):
+        """Construct the PIREP viewer."""
+        super(PIREPViewer, self).__init__(title = WINDOW_TITLE_BASE +
+                                          " - " +
+                                          xstr("pirepView_title"),
+                                          parent = gui.mainWindow)
+
+        self.set_resizable(False)
+
+        self._gui = gui
+
+        contentArea = self.get_content_area()
+
+        self._notebook = gtk.Notebook()
+        contentArea.pack_start(self._notebook, False, False, 4)
+
+        dataTab = self._buildDataTab()
+        label = gtk.Label(xstr("pirepView_tab_data"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("pirepView_tab_data_tooltip"))
+        self._notebook.append_page(dataTab, label)
+
+        commentsTab = self._buildCommentsTab()
+        label = gtk.Label(xstr("pirepView_tab_comments"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("pirepView_tab_comments_tooltip"))
+        self._notebook.append_page(commentsTab, label)
+
+        logTab = self._buildLogTab()
+        label = gtk.Label(xstr("pirepView_tab_log"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("pirepView_tab_log_tooltip"))
+        self._notebook.append_page(logTab, label)
+
+        self._showMessages = showMessages
+        if showMessages:
+            messagesTab = self._buildMessagesTab()
+            label = gtk.Label(xstr("pirepView_tab_messages"))
+            label.set_use_underline(True)
+            label.set_tooltip_text(xstr("pirepView_tab_messages_tooltip"))
+            self._notebook.append_page(messagesTab, label)
+
+        self._okButton = self.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+        self._okButton.set_can_default(True)
+
+    def setPIREP(self, pirep):
+        """Setup the data in the dialog from the given PIREP."""
+        bookedFlight = pirep.bookedFlight
+
+        self._callsign.set_text(bookedFlight.callsign)
+        self._tailNumber.set_text(bookedFlight.tailNumber)
+        aircraftType = xstr("aircraft_" + icaoCodes[bookedFlight.aircraftType].lower())
+        self._aircraftType.set_text(aircraftType)
+
+        self._departureICAO.set_text(bookedFlight.departureICAO)
+        self._departureTime.set_text("%02d:%02d" % \
+                                     (bookedFlight.departureTime.hour,
+                                      bookedFlight.departureTime.minute))
+
+        self._arrivalICAO.set_text(bookedFlight.arrivalICAO)
+        self._arrivalTime.set_text("%02d:%02d" % \
+                                   (bookedFlight.arrivalTime.hour,
+                                    bookedFlight.arrivalTime.minute))
+
+        self._numPassengers.set_text(str(bookedFlight.numPassengers))
+        self._numCrew.set_text(str(bookedFlight.numCrew))
+        self._bagWeight.set_text(str(bookedFlight.bagWeight))
+        self._cargoWeight.set_text(str(bookedFlight.cargoWeight))
+        self._mailWeight.set_text(str(bookedFlight.mailWeight))
+
+        self._route.get_buffer().set_text(bookedFlight.route)
+
+        self._filedCruiseLevel.set_text("FL" + str(pirep.filedCruiseAltitude/100))
+
+        if pirep.cruiseAltitude != pirep.filedCruiseAltitude:
+            self._modifiedCruiseLevel.set_text("FL" + str(pirep.cruiseAltitude/100))
+        else:
+            self._modifiedCruiseLevel.set_text("-")
+
+        self._userRoute.get_buffer().set_text(pirep.route)
+
+        self._departureMETAR.get_buffer().set_text(pirep.departureMETAR)
+
+        self._arrivalMETAR.get_buffer().set_text(pirep.arrivalMETAR)
+        self._departureRunway.set_text(pirep.departureRunway)
+        self._sid.set_text(pirep.sid)
+
+        self._star.set_text("-" if pirep.star is None else pirep.star)
+        self._transition.set_text("-" if pirep.transition is None else pirep.transition)
+        self._approachType.set_text(pirep.approachType)
+        self._arrivalRunway.set_text(pirep.arrivalRunway)
+
+        PIREPViewer.timestamp2text(self._blockTimeStart, pirep.blockTimeStart)
+        PIREPViewer.timestamp2text(self._blockTimeEnd, pirep.blockTimeEnd)
+        PIREPViewer.timestamp2text(self._flightTimeStart, pirep.flightTimeStart)
+        PIREPViewer.timestamp2text(self._flightTimeEnd, pirep.flightTimeEnd)
+
+        self._flownDistance.set_text("%.1f" % (pirep.flownDistance,))
+        self._fuelUsed.set_text("%.0f" % (pirep.fuelUsed,))
+
+        rating = pirep.rating
+        if rating<0:
+            self._rating.set_markup('<b><span foreground="red">NO GO</span></b>')
+        else:
+            self._rating.set_text("%.1f %%" % (rating,))
+
+        self._flownNumCrew.set_text("%d" % (pirep.numCrew,))
+        self._flownNumPassengers.set_text("%d" % (pirep.numPassengers,))
+        self._flownBagWeight.set_text("%.0f" % (pirep.bagWeight,))
+        self._flownCargoWeight.set_text("%.0f" % (pirep.cargoWeight,))
+        self._flownMailWeight.set_text("%.0f" % (pirep.mailWeight,))
+        self._flightType.set_text(xstr("flighttype_" +
+                                       flightType2string(pirep.flightType)))
+        self._online.set_text(xstr("pirepView_" +
+                                   ("yes" if pirep.online else "no")))
+
+        delayCodes = ""
+        for code in pirep.delayCodes:
+            if delayCodes: delayCodes += ", "
+            delayCodes += code
+
+        self._delayCodes.get_buffer().set_text(delayCodes)
+
+        self._comments.get_buffer().set_text(pirep.comments)
+        self._flightDefects.get_buffer().set_text(pirep.flightDefects)
+
+        logBuffer = self._log.get_buffer()
+        logBuffer.set_text("")
+        lineIndex = 0
+        for (timeStr, line) in pirep.logLines:
+            isFault = lineIndex in pirep.faultLineIndexes
+            appendTextBuffer(logBuffer,
+                             formatFlightLogLine(timeStr, line),
+                             isFault = isFault)
+            lineIndex += 1
+
+        if self._showMessages:
+            self._messages.reset()
+            for message in pirep.messages:
+                self._messages.addMessage(message)
+
+        self._notebook.set_current_page(0)
+        self._okButton.grab_default()
+
+    def _buildDataTab(self):
+        """Build the data tab of the viewer."""
+        table = gtk.Table(1, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(True)
+
+        box1 = gtk.VBox()
+        table.attach(box1, 0, 1, 0, 1)
+
+        box2 = gtk.VBox()
+        table.attach(box2, 1, 2, 0, 1)
+
+        flightFrame = self._buildFlightFrame()
+        box1.pack_start(flightFrame, False, False, 4)
+
+        routeFrame = self._buildRouteFrame()
+        box1.pack_start(routeFrame, False, False, 4)
+
+        departureFrame = self._buildDepartureFrame()
+        box2.pack_start(departureFrame, True, True, 4)
+
+        arrivalFrame = self._buildArrivalFrame()
+        box2.pack_start(arrivalFrame, True, True, 4)
+
+        statisticsFrame = self._buildStatisticsFrame()
+        box2.pack_start(statisticsFrame, False, False, 4)
+
+        miscellaneousFrame = self._buildMiscellaneousFrame()
+        box1.pack_start(miscellaneousFrame, False, False, 4)
+
+        return table
+
+    def _buildFlightFrame(self):
+        """Build the frame for the flight data."""
+
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_flight"))
+
+        dataBox = gtk.HBox()
+        mainBox.pack_start(dataBox, False, False, 0)
+
+        self._callsign = \
+            PIREPViewer.addLabeledData(dataBox,
+                                       xstr("pirepView_callsign"),
+                                       width = 8)
+
+        self._tailNumber = \
+            PIREPViewer.addLabeledData(dataBox,
+                                       xstr("pirepView_tailNumber"),
+                                       width = 7)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        dataBox = gtk.HBox()
+        mainBox.pack_start(dataBox, False, False, 0)
+
+        self._aircraftType = \
+            PIREPViewer.addLabeledData(dataBox,
+                                       xstr("pirepView_aircraftType"),
+                                       width = 25)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        table = gtk.Table(3, 2)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+
+        self._departureICAO = \
+            PIREPViewer.tableAttach(table, 0, 0,
+                                    xstr("pirepView_departure"),
+                                    width = 5)
+
+        self._departureTime = \
+            PIREPViewer.tableAttach(table, 1, 0,
+                                    xstr("pirepView_departure_time"),
+                                    width = 6)
+
+        self._arrivalICAO = \
+            PIREPViewer.tableAttach(table, 0, 1,
+                                    xstr("pirepView_arrival"),
+                                    width = 5)
+
+        self._arrivalTime = \
+            PIREPViewer.tableAttach(table, 1, 1,
+                                    xstr("pirepView_arrival_time"),
+                                    width = 6)
+
+        table = gtk.Table(3, 2)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+
+        self._numPassengers = \
+            PIREPViewer.tableAttach(table, 0, 0,
+                                    xstr("pirepView_numPassengers"),
+                                    width = 4)
+
+        self._numCrew = \
+            PIREPViewer.tableAttach(table, 1, 0,
+                                    xstr("pirepView_numCrew"),
+                                    width = 3)
+
+        self._bagWeight = \
+            PIREPViewer.tableAttach(table, 0, 1,
+                                    xstr("pirepView_bagWeight"),
+                                    width = 5)
+
+        self._cargoWeight = \
+            PIREPViewer.tableAttach(table, 1, 1,
+                                    xstr("pirepView_cargoWeight"),
+                                    width = 5)
+
+        self._mailWeight = \
+            PIREPViewer.tableAttach(table, 2, 1,
+                                    xstr("pirepView_mailWeight"),
+                                    width = 5)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_route")),
+                           False, False, 0)
+
+        (routeWindow, self._route) = PIREPViewer.getTextWindow()
+        mainBox.pack_start(routeWindow, False, False, 0)
+
+        return frame
+
+    def _buildRouteFrame(self):
+        """Build the frame for the user-specified route and flight
+        level."""
+
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_route"))
+
+        levelBox = gtk.HBox()
+        mainBox.pack_start(levelBox, False, False, 0)
+
+        self._filedCruiseLevel = \
+            PIREPViewer.addLabeledData(levelBox,
+                                       xstr("pirepView_filedCruiseLevel"),
+                                       width = 6)
+
+        self._modifiedCruiseLevel = \
+            PIREPViewer.addLabeledData(levelBox,
+                                       xstr("pirepView_modifiedCruiseLevel"),
+                                       width = 6)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        (routeWindow, self._userRoute) = PIREPViewer.getTextWindow()
+        mainBox.pack_start(routeWindow, False, False, 0)
+
+        return frame
+
+    def _buildDepartureFrame(self):
+        """Build the frame for the departure data."""
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_departure"))
+
+        mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
+                           False, False, 0)
+        (metarWindow, self._departureMETAR) = \
+            PIREPViewer.getTextWindow(heightRequest = -1)
+        mainBox.pack_start(metarWindow, True, True, 0)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        dataBox = gtk.HBox()
+        mainBox.pack_start(dataBox, False, False, 0)
+
+        self._departureRunway = \
+            PIREPViewer.addLabeledData(dataBox,
+                                       xstr("pirepView_runway"),
+                                       width = 5)
+
+        self._sid = \
+            PIREPViewer.addLabeledData(dataBox,
+                                       xstr("pirepView_sid"),
+                                       width = 12)
+
+        return frame
+
+    def _buildArrivalFrame(self):
+        """Build the frame for the arrival data."""
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_arrival"))
+
+        mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
+                           False, False, 0)
+        (metarWindow, self._arrivalMETAR) = \
+            PIREPViewer.getTextWindow(heightRequest = -1)
+        mainBox.pack_start(metarWindow, True, True, 0)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        table = gtk.Table(2, 2)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+
+        self._star = \
+            PIREPViewer.tableAttach(table, 0, 0,
+                                    xstr("pirepView_star"),
+                                    width = 12)
+
+        self._transition = \
+            PIREPViewer.tableAttach(table, 1, 0,
+                                    xstr("pirepView_transition"),
+                                    width = 12)
+
+        self._approachType = \
+            PIREPViewer.tableAttach(table, 0, 1,
+                                    xstr("pirepView_approachType"),
+                                    width = 7)
+
+        self._arrivalRunway = \
+            PIREPViewer.tableAttach(table, 1, 1,
+                                    xstr("pirepView_runway"),
+                                    width = 5)
+
+        return frame
+
+    def _buildStatisticsFrame(self):
+        """Build the frame for the statistics data."""
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_statistics"))
+
+        table = gtk.Table(4, 2)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+        table.set_homogeneous(False)
+
+        self._blockTimeStart = \
+            PIREPViewer.tableAttach(table, 0, 0,
+                                    xstr("pirepView_blockTimeStart"),
+                                    width = 6)
+
+        self._blockTimeEnd = \
+            PIREPViewer.tableAttach(table, 1, 0,
+                                    xstr("pirepView_blockTimeEnd"),
+                                    width = 8)
+
+        self._flightTimeStart = \
+            PIREPViewer.tableAttach(table, 0, 1,
+                                    xstr("pirepView_flightTimeStart"),
+                                    width = 6)
+
+        self._flightTimeEnd = \
+            PIREPViewer.tableAttach(table, 1, 1,
+                                    xstr("pirepView_flightTimeEnd"),
+                                    width = 6)
+
+        self._flownDistance = \
+            PIREPViewer.tableAttach(table, 0, 2,
+                                    xstr("pirepView_flownDistance"),
+                                    width = 8)
+
+        self._fuelUsed = \
+            PIREPViewer.tableAttach(table, 1, 2,
+                                    xstr("pirepView_fuelUsed"),
+                                    width = 6)
+
+        self._rating = \
+            PIREPViewer.tableAttach(table, 0, 3,
+                                    xstr("pirepView_rating"),
+                                    width = 7)
+        return frame
+
+    def _buildMiscellaneousFrame(self):
+        """Build the frame for the miscellaneous data."""
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_miscellaneous"))
+
+        table = gtk.Table(3, 2)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+
+        self._flownNumPassengers = \
+            PIREPViewer.tableAttach(table, 0, 0,
+                                    xstr("pirepView_numPassengers"),
+                                    width = 4)
+
+        self._flownNumCrew = \
+            PIREPViewer.tableAttach(table, 1, 0,
+                                    xstr("pirepView_numCrew"),
+                                    width = 3)
+
+        self._flownBagWeight = \
+            PIREPViewer.tableAttach(table, 0, 1,
+                                    xstr("pirepView_bagWeight"),
+                                    width = 5)
+
+        self._flownCargoWeight = \
+            PIREPViewer.tableAttach(table, 1, 1,
+                                    xstr("pirepView_cargoWeight"),
+                                    width = 6)
+
+        self._flownMailWeight = \
+            PIREPViewer.tableAttach(table, 2, 1,
+                                    xstr("pirepView_mailWeight"),
+                                    width = 5)
+
+        self._flightType = \
+            PIREPViewer.tableAttach(table, 0, 2,
+                                    xstr("pirepView_flightType"),
+                                    width = 15)
+
+        self._online = \
+            PIREPViewer.tableAttach(table, 1, 2,
+                                    xstr("pirepView_online"),
+                                    width = 5)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_delayCodes")),
+                           False, False, 0)
+
+        (textWindow, self._delayCodes) = PIREPViewer.getTextWindow()
+        mainBox.pack_start(textWindow, False, False, 0)
+
+        return frame
+
+    def _buildCommentsTab(self):
+        """Build the tab with the comments and flight defects."""
+        table = gtk.Table(2, 1)
+        table.set_col_spacings(16)
+
+        (frame, commentsBox) = \
+            PIREPViewer.createFrame(xstr("pirepView_comments"))
+        table.attach(frame, 0, 1, 0, 1)
+
+        (commentsWindow, self._comments) = \
+            PIREPViewer.getTextWindow(heightRequest = -1)
+        commentsBox.pack_start(commentsWindow, True, True, 0)
+
+        (frame, flightDefectsBox) = \
+            PIREPViewer.createFrame(xstr("pirepView_flightDefects"))
+        table.attach(frame, 1, 2, 0, 1)
+
+        (flightDefectsWindow, self._flightDefects) = \
+            PIREPViewer.getTextWindow(heightRequest = -1)
+        flightDefectsBox.pack_start(flightDefectsWindow, True, True, 0)
+
+        return table
+
+    def _buildLogTab(self):
+        """Build the log tab."""
+        mainBox = gtk.VBox()
+
+        (logWindow, self._log) = PIREPViewer.getTextWindow(heightRequest = -1)
+        addFaultTag(self._log.get_buffer())
+        mainBox.pack_start(logWindow, True, True, 0)
+
+        return mainBox
+
+    def _buildMessagesTab(self):
+        """Build the messages tab."""
+        mainBox = gtk.VBox()
+
+        self._messages = MessagesWidget(self._gui)
+        mainBox.pack_start(self._messages, True, True, 0)
+
+        return mainBox
+
+#------------------------------------------------------------------------------
+
+class PIREPEditor(gtk.Dialog):
+    """A PIREP editor dialog."""
+    _delayCodeRE = re.compile("([0-9]{2,3})( \([^\)]*\))")
+
+    @staticmethod
+    def tableAttachWidget(table, column, row, labelText, widget):
+        """Attach the given widget with the given label to the given table.
+
+        The label will got to cell (column, row), the widget to cell
+        (column+1, row)."""
+        label = gtk.Label("<b>" + labelText + "</b>")
+        label.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, column, column + 1, row, row + 1)
+
+        table.attach(widget, column + 1, column + 2, row, row + 1)
+
+    @staticmethod
+    def tableAttachSpinButton(table, column, row, labelText, maxValue,
+                              minValue = 0, stepIncrement = 1,
+                              pageIncrement = 10, numeric = True,
+                              width = 3):
+        """Attach a spin button with the given label to the given table.
+
+        The label will got to cell (column, row), the spin button to cell
+        (column+1, row)."""
+        button = gtk.SpinButton()
+        button.set_range(min = minValue, max = maxValue)
+        button.set_increments(step = stepIncrement, page = pageIncrement)
+        button.set_numeric(True)
+        button.set_width_chars(width)
+        button.set_alignment(1.0)
+
+        PIREPEditor.tableAttachWidget(table, column, row, labelText, button)
+
+        return button
+
+    @staticmethod
+    def tableAttachTimeEntry(table, column, row, labelText):
+        """Attach a time entry widget with the given label to the given table.
+
+        The label will got to cell (column, row), the spin button to cell
+        (column+1, row)."""
+        entry = TimeEntry()
+        entry.set_width_chars(5)
+        entry.set_alignment(1.0)
+
+        PIREPEditor.tableAttachWidget(table, column, row, labelText, entry)
+
+        return entry
+
+    def __init__(self, gui):
+        """Construct the PIREP viewer."""
+        super(PIREPEditor, self).__init__(title = WINDOW_TITLE_BASE +
+                                          " - " +
+                                          xstr("pirepEdit_title"),
+                                          parent = gui.mainWindow)
+
+        self.set_resizable(False)
+
+        self._gui = gui
+
+        self._pirep = None
+
+        contentArea = self.get_content_area()
+
+        self._notebook = gtk.Notebook()
+        contentArea.pack_start(self._notebook, False, False, 4)
+
+        dataTab = self._buildDataTab()
+        label = gtk.Label(xstr("pirepView_tab_data"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("pirepView_tab_data_tooltip"))
+        self._notebook.append_page(dataTab, label)
+
+        self._flightInfo = self._buildCommentsTab()
+        label = gtk.Label(xstr("pirepView_tab_comments"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("pirepView_tab_comments_tooltip"))
+        self._notebook.append_page(self._flightInfo, label)
+
+        logTab = self._buildLogTab()
+        label = gtk.Label(xstr("pirepView_tab_log"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("pirepView_tab_log_tooltip"))
+        self._notebook.append_page(logTab, label)
+
+        self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
+
+        self._okButton = self.add_button(xstr("button_save"), RESPONSETYPE_NONE)
+        self._okButton.connect("clicked", self._okClicked)
+        self._okButton.set_can_default(True)
+        self._modified = False
+        self._toSave = False
+
+    def setPIREP(self, pirep):
+        """Setup the data in the dialog from the given PIREP."""
+        self._pirep = pirep
+
+        bookedFlight = pirep.bookedFlight
+
+        self._callsign.set_text(bookedFlight.callsign)
+        self._tailNumber.set_text(bookedFlight.tailNumber)
+        aircraftType = xstr("aircraft_" + icaoCodes[bookedFlight.aircraftType].lower())
+        self._aircraftType.set_text(aircraftType)
+
+        self._departureICAO.set_text(bookedFlight.departureICAO)
+        self._departureTime.set_text("%02d:%02d" % \
+                                     (bookedFlight.departureTime.hour,
+                                      bookedFlight.departureTime.minute))
+
+        self._arrivalICAO.set_text(bookedFlight.arrivalICAO)
+        self._arrivalTime.set_text("%02d:%02d" % \
+                                   (bookedFlight.arrivalTime.hour,
+                                    bookedFlight.arrivalTime.minute))
+
+        self._numPassengers.set_text(str(bookedFlight.numPassengers))
+        self._numCrew.set_text(str(bookedFlight.numCrew))
+        self._bagWeight.set_text(str(bookedFlight.bagWeight))
+        self._cargoWeight.set_text(str(bookedFlight.cargoWeight))
+        self._mailWeight.set_text(str(bookedFlight.mailWeight))
+
+        self._route.get_buffer().set_text(bookedFlight.route)
+
+        self._filedCruiseLevel.set_value(pirep.filedCruiseAltitude/100)
+        self._modifiedCruiseLevel.set_value(pirep.cruiseAltitude/100)
+
+        self._userRoute.get_buffer().set_text(pirep.route)
+
+        self._departureMETAR.get_buffer().set_text(pirep.departureMETAR)
+
+        self._arrivalMETAR.get_buffer().set_text(pirep.arrivalMETAR)
+        self._departureRunway.set_text(pirep.departureRunway)
+        self._sid.get_child().set_text(pirep.sid)
+
+        if not pirep.star:
+            self._star.set_active(0)
+        else:
+            self._star.get_child().set_text(pirep.star)
+
+        if not pirep.transition:
+            self._transition.set_active(0)
+        else:
+            self._transition.get_child().set_text(pirep.transition)
+        self._approachType.set_text(pirep.approachType)
+        self._arrivalRunway.set_text(pirep.arrivalRunway)
+
+        self._blockTimeStart.setTimestamp(pirep.blockTimeStart)
+        self._blockTimeEnd.setTimestamp(pirep.blockTimeEnd)
+        self._flightTimeStart.setTimestamp(pirep.flightTimeStart)
+        self._flightTimeEnd.setTimestamp(pirep.flightTimeEnd)
+
+        self._flownDistance.set_text("%.1f" % (pirep.flownDistance,))
+        self._fuelUsed.set_value(int(pirep.fuelUsed))
+
+        rating = pirep.rating
+        if rating<0:
+            self._rating.set_markup('<b><span foreground="red">NO GO</span></b>')
+        else:
+            self._rating.set_text("%.1f %%" % (rating,))
+
+        self._flownNumCrew.set_value(pirep.numCrew)
+        self._flownNumPassengers.set_value(pirep.numPassengers)
+        self._flownBagWeight.set_value(pirep.bagWeight)
+        self._flownCargoWeight.set_value(pirep.cargoWeight)
+        self._flownMailWeight.set_value(pirep.mailWeight)
+        self._flightType.set_active(flightType2index(pirep.flightType))
+        self._online.set_active(pirep.online)
+
+        self._flightInfo.reset()
+        self._flightInfo.enable(bookedFlight.aircraftType)
+
+        delayCodes = ""
+        for code in pirep.delayCodes:
+            if delayCodes: delayCodes += ", "
+            delayCodes += code
+            m = PIREPEditor._delayCodeRE.match(code)
+            if m:
+                self._flightInfo.activateDelayCode(m.group(1))
+
+        self._delayCodes.get_buffer().set_text(delayCodes)
+
+        self._flightInfo.comments = pirep.comments
+        if pirep.flightDefects.find("<br/></b>")!=-1:
+            flightDefects = pirep.flightDefects.split("<br/></b>")
+            caption = flightDefects[0]
+            index = 0
+            for defect in flightDefects[1:]:
+                if defect.find("<b>")!=-1:
+                    (explanation, nextCaption) = defect.split("<b>")
+                else:
+                    explanation = defect
+                    nextCaption = None
+                self._flightInfo.addFault(index, caption)
+                self._flightInfo.setExplanation(index, explanation)
+                index += 1
+                caption = nextCaption
+
+        # self._comments.get_buffer().set_text(pirep.comments)
+        # self._flightDefects.get_buffer().set_text(pirep.flightDefects)
+
+        logBuffer = self._log.get_buffer()
+        logBuffer.set_text("")
+        lineIndex = 0
+        for (timeStr, line) in pirep.logLines:
+            isFault = lineIndex in pirep.faultLineIndexes
+            appendTextBuffer(logBuffer,
+                             formatFlightLogLine(timeStr, line),
+                             isFault = isFault)
+            lineIndex += 1
+
+        self._notebook.set_current_page(0)
+        self._okButton.grab_default()
+
+        self._modified = False
+        self._updateButtons()
+        self._modified = True
+        self._toSave = False
+
+    def delayCodesChanged(self):
+        """Called when the delay codes have changed."""
+        self._updateButtons()
+
+    def commentsChanged(self):
+        """Called when the comments have changed."""
+        self._updateButtons()
+
+    def faultExplanationsChanged(self):
+        """Called when the fault explanations have changed."""
+        self._updateButtons()
+
+    def _buildDataTab(self):
+        """Build the data tab of the viewer."""
+        table = gtk.Table(1, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(True)
+
+        box1 = gtk.VBox()
+        table.attach(box1, 0, 1, 0, 1)
+
+        box2 = gtk.VBox()
+        table.attach(box2, 1, 2, 0, 1)
+
+        flightFrame = self._buildFlightFrame()
+        box1.pack_start(flightFrame, False, False, 4)
+
+        routeFrame = self._buildRouteFrame()
+        box1.pack_start(routeFrame, False, False, 4)
+
+        departureFrame = self._buildDepartureFrame()
+        box2.pack_start(departureFrame, True, True, 4)
+
+        arrivalFrame = self._buildArrivalFrame()
+        box2.pack_start(arrivalFrame, True, True, 4)
+
+        statisticsFrame = self._buildStatisticsFrame()
+        box2.pack_start(statisticsFrame, False, False, 4)
+
+        miscellaneousFrame = self._buildMiscellaneousFrame()
+        box1.pack_start(miscellaneousFrame, False, False, 4)
+
+        return table
+
+    def _buildFlightFrame(self):
+        """Build the frame for the flight data."""
+
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_flight"))
+
+        dataBox = gtk.HBox()
+        mainBox.pack_start(dataBox, False, False, 0)
+
+        self._callsign = \
+            PIREPViewer.addLabeledData(dataBox,
+                                       xstr("pirepView_callsign"),
+                                       width = 8)
+
+        self._tailNumber = \
+            PIREPViewer.addLabeledData(dataBox,
+                                       xstr("pirepView_tailNumber"),
+                                       width = 7)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        dataBox = gtk.HBox()
+        mainBox.pack_start(dataBox, False, False, 0)
+
+        self._aircraftType = \
+            PIREPViewer.addLabeledData(dataBox,
+                                       xstr("pirepView_aircraftType"),
+                                       width = 25)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        table = gtk.Table(3, 2)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+
+        self._departureICAO = \
+            PIREPViewer.tableAttach(table, 0, 0,
+                                    xstr("pirepView_departure"),
+                                    width = 5)
+
+        self._departureTime = \
+            PIREPViewer.tableAttach(table, 1, 0,
+                                    xstr("pirepView_departure_time"),
+                                    width = 6)
+
+        self._arrivalICAO = \
+            PIREPViewer.tableAttach(table, 0, 1,
+                                    xstr("pirepView_arrival"),
+                                    width = 5)
+
+        self._arrivalTime = \
+            PIREPViewer.tableAttach(table, 1, 1,
+                                    xstr("pirepView_arrival_time"),
+                                    width = 6)
+
+        table = gtk.Table(3, 2)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+
+        self._numPassengers = \
+            PIREPViewer.tableAttach(table, 0, 0,
+                                    xstr("pirepView_numPassengers"),
+                                    width = 4)
+        self._numCrew = \
+            PIREPViewer.tableAttach(table, 1, 0,
+                                    xstr("pirepView_numCrew"),
+                                    width = 3)
+
+        self._bagWeight = \
+            PIREPViewer.tableAttach(table, 0, 1,
+                                    xstr("pirepView_bagWeight"),
+                                    width = 5)
+
+        self._cargoWeight = \
+            PIREPViewer.tableAttach(table, 1, 1,
+                                    xstr("pirepView_cargoWeight"),
+                                    width = 5)
+
+        self._mailWeight = \
+            PIREPViewer.tableAttach(table, 2, 1,
+                                    xstr("pirepView_mailWeight"),
+                                    width = 5)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_route")),
+                           False, False, 0)
+
+        (routeWindow, self._route) = PIREPViewer.getTextWindow()
+        mainBox.pack_start(routeWindow, False, False, 0)
+
+        return frame
+
+    def _buildRouteFrame(self):
+        """Build the frame for the user-specified route and flight
+        level."""
+
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_route"))
+
+        levelBox = gtk.HBox()
+        mainBox.pack_start(levelBox, False, False, 0)
+
+        label = PIREPViewer.getLabel(xstr("pirepView_filedCruiseLevel"),
+                                     xstr("pirepEdit_FL"))
+        levelBox.pack_start(label, False, False, 0)
+
+        self._filedCruiseLevel = gtk.SpinButton()
+        self._filedCruiseLevel.set_increments(step = 10, page = 100)
+        self._filedCruiseLevel.set_range(min = 0, max = 500)
+        self._filedCruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
+        self._filedCruiseLevel.set_numeric(True)
+        self._filedCruiseLevel.connect("value-changed", self._updateButtons)
+
+        levelBox.pack_start(self._filedCruiseLevel, False, False, 0)
+
+        PIREPViewer.addHFiller(levelBox)
+
+        label = PIREPViewer.getLabel(xstr("pirepView_modifiedCruiseLevel"),
+                                     xstr("pirepEdit_FL"))
+        levelBox.pack_start(label, False, False, 0)
+
+        self._modifiedCruiseLevel = gtk.SpinButton()
+        self._modifiedCruiseLevel.set_increments(step = 10, page = 100)
+        self._modifiedCruiseLevel.set_range(min = 0, max = 500)
+        self._modifiedCruiseLevel.set_tooltip_text(xstr("pirepEdit_modified_route_level_tooltip"))
+        self._modifiedCruiseLevel.set_numeric(True)
+        self._modifiedCruiseLevel.connect("value-changed", self._updateButtons)
+
+        levelBox.pack_start(self._modifiedCruiseLevel, False, False, 0)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        (routeWindow, self._userRoute) = \
+          PIREPViewer.getTextWindow(editable = True)
+        mainBox.pack_start(routeWindow, False, False, 0)
+        self._userRoute.get_buffer().connect("changed", self._updateButtons)
+        self._userRoute.set_tooltip_text(xstr("route_route_tooltip"))
+
+        return frame
+
+    def _buildDepartureFrame(self):
+        """Build the frame for the departure data."""
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_departure"))
+
+        mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
+                           False, False, 0)
+        (metarWindow, self._departureMETAR) = \
+            PIREPViewer.getTextWindow(heightRequest = -1,
+                                      editable = True)
+        self._departureMETAR.get_buffer().connect("changed", self._updateButtons)
+        self._departureMETAR.set_tooltip_text(xstr("takeoff_metar_tooltip"))
+        mainBox.pack_start(metarWindow, True, True, 0)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        dataBox = gtk.HBox()
+        mainBox.pack_start(dataBox, False, False, 0)
+
+        label = gtk.Label("<b>" + xstr("pirepView_runway") + "</b>")
+        label.set_use_markup(True)
+        dataBox.pack_start(label, False, False, 0)
+
+        # FIXME: quite the same as the runway entry boxes in the wizard
+        self._departureRunway = gtk.Entry()
+        self._departureRunway.set_width_chars(5)
+        self._departureRunway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
+        self._departureRunway.connect("changed", self._upperChanged)
+        dataBox.pack_start(self._departureRunway, False, False, 8)
+
+        label = gtk.Label("<b>" + xstr("pirepView_sid") + "</b>")
+        label.set_use_markup(True)
+        dataBox.pack_start(label, False, False, 0)
+
+        # FIXME: quite the same as the SID combo box in
+        # the flight wizard
+        if pygobject:
+            self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
+        else:
+            self._sid = gtk.ComboBoxEntry(comboModel)
+
+        self._sid.set_entry_text_column(0)
+        self._sid.get_child().set_width_chars(10)
+        self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
+        self._sid.connect("changed", self._upperChangedComboBox)
+
+        dataBox.pack_start(self._sid, False, False, 8)
+
+        return frame
+
+    def _buildArrivalFrame(self):
+        """Build the frame for the arrival data."""
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_arrival"))
+
+        mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
+                           False, False, 0)
+        (metarWindow, self._arrivalMETAR) = \
+            PIREPViewer.getTextWindow(heightRequest = -1,
+                                      editable = True)
+        self._arrivalMETAR.get_buffer().connect("changed", self._updateButtons)
+        self._arrivalMETAR.set_tooltip_text(xstr("landing_metar_tooltip"))
+        mainBox.pack_start(metarWindow, True, True, 0)
+
+        PIREPViewer.addVFiller(mainBox)
+
+        table = gtk.Table(2, 4)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+
+        # FIXME: quite the same as in the wizard
+        if pygobject:
+            self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
+        else:
+            self._star = gtk.ComboBoxEntry(comboModel)
+
+        self._star.set_entry_text_column(0)
+        self._star.get_child().set_width_chars(10)
+        self._star.set_tooltip_text(xstr("landing_star_tooltip"))
+        self._star.connect("changed", self._upperChangedComboBox)
+
+        PIREPEditor.tableAttachWidget(table, 0, 0,
+                                      xstr("pirepView_star"),
+                                      self._star)
+
+        # FIXME: quite the same as in the wizard
+        if pygobject:
+            self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
+        else:
+            self._transition = gtk.ComboBoxEntry(comboModel)
+
+        self._transition.set_entry_text_column(0)
+        self._transition.get_child().set_width_chars(10)
+        self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
+        self._transition.connect("changed", self._upperChangedComboBox)
+
+        PIREPEditor.tableAttachWidget(table, 2, 0,
+                                      xstr("pirepView_transition"),
+                                      self._transition)
+
+
+        # FIXME: quite the same as in the wizard
+        self._approachType = gtk.Entry()
+        self._approachType.set_width_chars(10)
+        self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
+        self._approachType.connect("changed", self._upperChanged)
+
+        PIREPEditor.tableAttachWidget(table, 0, 1,
+                                      xstr("pirepView_approachType"),
+                                      self._approachType)
+
+        # FIXME: quite the same as in the wizard
+        self._arrivalRunway = gtk.Entry()
+        self._arrivalRunway.set_width_chars(10)
+        self._arrivalRunway.set_tooltip_text(xstr("landing_runway_tooltip"))
+        self._arrivalRunway.connect("changed", self._upperChanged)
+
+        PIREPEditor.tableAttachWidget(table, 2, 1,
+                                      xstr("pirepView_runway"),
+                                      self._arrivalRunway)
+
+        return frame
+
+    def _buildStatisticsFrame(self):
+        """Build the frame for the statistics data."""
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_statistics"))
+
+        table = gtk.Table(4, 4)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+        table.set_homogeneous(False)
+
+        self._blockTimeStart = \
+            PIREPEditor.tableAttachTimeEntry(table, 0, 0,
+                                             xstr("pirepView_blockTimeStart"))
+        self._blockTimeStart.connect("changed", self._updateButtons)
+        self._blockTimeStart.set_tooltip_text(xstr("pirepEdit_block_time_start_tooltip"))
+
+        self._blockTimeEnd = \
+            PIREPEditor.tableAttachTimeEntry(table, 2, 0,
+                                             xstr("pirepView_blockTimeEnd"))
+        self._blockTimeEnd.connect("changed", self._updateButtons)
+        self._blockTimeEnd.set_tooltip_text(xstr("pirepEdit_block_time_end_tooltip"))
+
+        self._flightTimeStart = \
+            PIREPEditor.tableAttachTimeEntry(table, 0, 1,
+                                             xstr("pirepView_flightTimeStart"))
+        self._flightTimeStart.connect("changed", self._updateButtons)
+        self._flightTimeStart.set_tooltip_text(xstr("pirepEdit_flight_time_start_tooltip"))
+
+        self._flightTimeEnd = \
+            PIREPEditor.tableAttachTimeEntry(table, 2, 1,
+                                             xstr("pirepView_flightTimeEnd"))
+        self._flightTimeEnd.connect("changed", self._updateButtons)
+        self._flightTimeEnd.set_tooltip_text(xstr("pirepEdit_flight_time_end_tooltip"))
+
+        self._flownDistance = PIREPViewer.getDataLabel(width = 3)
+        PIREPEditor.tableAttachWidget(table, 0, 2,
+                                      xstr("pirepView_flownDistance"),
+                                      self._flownDistance)
+
+        self._fuelUsed = \
+            PIREPEditor.tableAttachSpinButton(table, 2, 2,
+                                              xstr("pirepView_fuelUsed"),
+                                              1000000)
+        self._fuelUsed.connect("value-changed", self._updateButtons)
+        self._fuelUsed.set_tooltip_text(xstr("pirepEdit_fuel_used_tooltip"))
+
+        self._rating = PIREPViewer.getDataLabel(width = 3)
+        PIREPEditor.tableAttachWidget(table, 0, 3,
+                                      xstr("pirepView_rating"),
+                                      self._rating)
+        return frame
+
+    def _buildMiscellaneousFrame(self):
+        """Build the frame for the miscellaneous data."""
+        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_miscellaneous"))
+
+        table = gtk.Table(6, 2)
+        mainBox.pack_start(table, False, False, 0)
+        table.set_row_spacings(4)
+        table.set_col_spacings(8)
+        table.set_homogeneous(False)
+
+        self._flownNumPassengers = \
+            PIREPEditor.tableAttachSpinButton(table, 0, 0,
+                                              xstr("pirepView_numPassengers"),
+                                              300)
+        self._flownNumPassengers.connect("value-changed", self._updateButtons)
+        self._flownNumPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
+
+        self._flownNumCrew = \
+            PIREPEditor.tableAttachSpinButton(table, 2, 0,
+                                              xstr("pirepView_numCrew"),
+                                              10)
+        self._flownNumCrew.connect("value-changed", self._updateButtons)
+        self._flownNumCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
+
+        self._flownBagWeight = \
+            PIREPEditor.tableAttachSpinButton(table, 0, 1,
+                                              xstr("pirepView_bagWeight"),
+                                              100000, width = 6)
+        self._flownBagWeight.connect("value-changed", self._updateButtons)
+        self._flownBagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
+
+        self._flownCargoWeight = \
+            PIREPEditor.tableAttachSpinButton(table, 2, 1,
+                                              xstr("pirepView_cargoWeight"),
+                                              100000, width = 6)
+        self._flownCargoWeight.connect("value-changed", self._updateButtons)
+        self._flownCargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
+
+        self._flownMailWeight = \
+            PIREPEditor.tableAttachSpinButton(table, 4, 1,
+                                              xstr("pirepView_mailWeight"),
+                                              100000, width = 6)
+        self._flownMailWeight.connect("value-changed", self._updateButtons)
+        self._flownMailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
+
+        self._flightType = createFlightTypeComboBox()
+        PIREPEditor.tableAttachWidget(table, 0, 2,
+                                      xstr("pirepView_flightType"),
+                                      self._flightType)
+        self._flightType.connect("changed", self._updateButtons)
+        self._flightType.set_tooltip_text(xstr("pirepEdit_flight_type_tooltip"))
+
+        self._online = gtk.CheckButton(xstr("pirepEdit_online"))
+        table.attach(self._online, 2, 3, 2, 3)
+        self._online.connect("toggled", self._updateButtons)
+        self._online.set_tooltip_text(xstr("pirepEdit_online_tooltip"))
+
+        PIREPViewer.addVFiller(mainBox)
+
+        mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_delayCodes")),
+                           False, False, 0)
+
+        (textWindow, self._delayCodes) = PIREPViewer.getTextWindow()
+        mainBox.pack_start(textWindow, False, False, 0)
+        self._delayCodes.set_tooltip_text(xstr("pirepEdit_delayCodes_tooltip"))
+
+        return frame
+
+    def _buildCommentsTab(self):
+        """Build the tab with the comments and flight defects."""
+        return FlightInfo(self._gui, callbackObject = self)
+
+    def _buildLogTab(self):
+        """Build the log tab."""
+        mainBox = gtk.VBox()
+
+        (logWindow, self._log) = PIREPViewer.getTextWindow(heightRequest = -1)
+        addFaultTag(self._log.get_buffer())
+        mainBox.pack_start(logWindow, True, True, 0)
+
+        return mainBox
+
+    def _upperChanged(self, entry, arg = None):
+        """Called when the value of some entry widget has changed and the value
+        should be converted to uppercase."""
+        entry.set_text(entry.get_text().upper())
+        self._updateButtons()
+        #self._valueChanged(entry, arg)
+
+    def _upperChangedComboBox(self, comboBox):
+        """Called for combo box widgets that must be converted to uppercase."""
+        entry = comboBox.get_child()
+        if comboBox.get_active()==-1:
+            entry.set_text(entry.get_text().upper())
+        self._updateButtons()
+        #self._valueChanged(entry)
+
+    def _updateButtons(self, *kwargs):
+        """Update the activity state of the buttons."""
+        pirep = self._pirep
+        bookedFlight = pirep.bookedFlight
+
+        departureMinutes = \
+            bookedFlight.departureTime.hour*60 + bookedFlight.departureTime.minute
+        departureDifference = abs(Flight.getMinutesDifference(self._blockTimeStart.minutes,
+                                                              departureMinutes))
+        flightStartDifference = \
+            Flight.getMinutesDifference(self._flightTimeStart.minutes,
+                                        self._blockTimeStart.minutes)
+        arrivalMinutes = \
+            bookedFlight.arrivalTime.hour*60 + bookedFlight.arrivalTime.minute
+        arrivalDifference = abs(Flight.getMinutesDifference(self._blockTimeEnd.minutes,
+                                                            arrivalMinutes))
+        flightEndDifference = \
+            Flight.getMinutesDifference(self._blockTimeEnd.minutes,
+                                        self._flightTimeEnd.minutes)
+
+        timesOK = self._flightInfo.hasComments or \
+                  self._flightInfo.hasDelayCode or \
+                  (departureDifference<=Flight.TIME_ERROR_DIFFERENCE and
+                   arrivalDifference<=Flight.TIME_ERROR_DIFFERENCE and
+                   flightStartDifference>=0 and flightStartDifference<30 and
+                   flightEndDifference>=0 and flightEndDifference<30)
+
+        text = self._sid.get_child().get_text()
+        sid = text if self._sid.get_active()!=0 and text and text!="N/A" \
+               else None
+
+        text = self._star.get_child().get_text()
+        star = text if self._star.get_active()!=0 and text and text!="N/A" \
+               else None
+
+        text = self._transition.get_child().get_text()
+        transition = text if self._transition.get_active()!=0 \
+                     and text and text!="N/A" else None
+
+
+        buffer = self._userRoute.get_buffer()
+        route =  buffer.get_text(buffer.get_start_iter(),
+                                 buffer.get_end_iter(), True)
+
+        self._okButton.set_sensitive(self._modified and timesOK and
+                                     self._flightInfo.faultsFullyExplained and
+                                     self._flownNumPassengers.get_value_as_int()>0 and
+                                     self._flownNumCrew.get_value_as_int()>2 and
+                                     self._fuelUsed.get_value_as_int()>0 and
+                                     self._departureRunway.get_text_length()>0 and
+                                     self._arrivalRunway.get_text_length()>0 and
+                                     self._departureMETAR.get_buffer().get_char_count()>0 and
+                                     self._arrivalMETAR.get_buffer().get_char_count()>0 and
+                                     self._filedCruiseLevel.get_value_as_int()>=50 and
+                                     self._modifiedCruiseLevel.get_value_as_int()>=50 and
+                                     sid is not None and (star is not None or
+                                     transition is not None) and route!="" and
+                                     self._approachType.get_text()!="")
+
+    def _okClicked(self, button):
+        """Called when the OK button has been clicked.
+
+        The PIREP is updated from the data in the window."""
+        if not askYesNo(xstr("pirepEdit_save_question"), parent = self):
+            self.response(RESPONSETYPE_CANCEL)
+
+        pirep = self._pirep
+
+        pirep.filedCruiseAltitude = \
+          self._filedCruiseLevel.get_value_as_int() * 100
+        pirep.cruiseAltitude = \
+          self._modifiedCruiseLevel.get_value_as_int() * 100
+
+        pirep.route = getTextViewText(self._userRoute)
+
+        pirep.departureMETAR = getTextViewText(self._departureMETAR)
+        pirep.departureRunway = self._departureRunway.get_text()
+        pirep.sid = self._sid.get_child().get_text()
+
+        pirep.arrivalMETAR = getTextViewText(self._arrivalMETAR)
+        pirep.star = None if self._star.get_active()==0 \
+          else self._star.get_child().get_text()
+        pirep.transition = None if self._transition.get_active()==0 \
+          else self._transition.get_child().get_text()
+        pirep.approachType = self._approachType.get_text()
+        pirep.arrivalRunway = self._arrivalRunway.get_text()
+
+        pirep.blockTimeStart = \
+          self._blockTimeStart.getTimestampFrom(pirep.blockTimeStart)
+        pirep.blockTimeEnd = \
+          self._blockTimeEnd.getTimestampFrom(pirep.blockTimeEnd)
+        pirep.flightTimeStart = \
+          self._flightTimeStart.getTimestampFrom(pirep.flightTimeStart)
+        pirep.flightTimeEnd = \
+          self._flightTimeEnd.getTimestampFrom(pirep.flightTimeEnd)
+
+        pirep.fuelUsed = self._fuelUsed.get_value()
+
+        pirep.numCrew = self._flownNumCrew.get_value()
+        pirep.numPassengers = self._flownNumPassengers.get_value()
+        pirep.bagWeight = self._flownBagWeight.get_value()
+        pirep.cargoWeight = self._flownCargoWeight.get_value()
+        pirep.mailWeight = self._flownMailWeight.get_value()
+
+        pirep.flightType = flightTypes[self._flightType.get_active()]
+        pirep.online = self._online.get_active()
+
+        pirep.delayCodes = self._flightInfo.delayCodes
+        pirep.comments = self._flightInfo.comments
+        pirep.flightDefects = self._flightInfo.faultsAndExplanations
+
+        self.response(RESPONSETYPE_OK)
+
+
+#------------------------------------------------------------------------------
Index: src/mlx/gui/pirep.py
===================================================================
--- src/mlx/gui/pirep.py	(revision 855)
+++ 	(revision )
@@ -1,1574 +1,0 @@
-
-from common import *
-from dcdata import getTable
-from info import FlightInfo
-from flight import comboModel
-
-from mlx.pirep import PIREP
-from mlx.flight import Flight
-from mlx.const import *
-
-import time
-import re
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.pirep
-#
-# The detailed PIREP viewer and editor windows.
-#
-# The \ref PIREPViewer class is a dialog displaying all information found in a
-# PIREP. It consists of three tabs. The Data tab displays the simple,
-# itemizable data. The Comments & defects tab contains the flight comments and
-# defects, while the Log tab contains the flight log collected by the
-# \ref mlx.logger.Logger "logger".
-
-#------------------------------------------------------------------------------
-
-class MessageFrame(gtk.Frame):
-    """A frame containing the information about a PIREP message.
-
-    It consists of a text view with the heading information (author, time) and
-    another text view with the actual message."""
-    def __init__(self, message, senderPID, senderName):
-        """Construct the frame."""
-        gtk.Frame.__init__(self)
-
-        vbox = gtk.VBox()
-
-        self._heading = heading = gtk.TextView()
-        heading.set_editable(False)
-        heading.set_can_focus(False)
-        heading.set_wrap_mode(WRAP_WORD)
-
-        buffer = heading.get_buffer()
-        self._headingTag  = buffer.create_tag("heading", weight=WEIGHT_BOLD)
-        buffer.set_text("%s - %s" % (senderPID, senderName))
-        buffer.apply_tag(self._headingTag,
-                         buffer.get_start_iter(), buffer.get_end_iter())
-
-        headingAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                         xscale = 1.0, yscale = 0.0)
-        headingAlignment.set_padding(padding_top = 0, padding_bottom = 0,
-                                     padding_left = 2, padding_right = 2)
-        headingAlignment.add(heading)
-        vbox.pack_start(headingAlignment, True, True, 4)
-
-        self._messageView = messageView = gtk.TextView()
-        messageView.set_wrap_mode(WRAP_WORD)
-        messageView.set_accepts_tab(False)
-        messageView.set_size_request(-1, 60)
-
-        buffer = messageView.get_buffer()
-        buffer.set_text(message)
-
-        vbox.pack_start(messageView, True, True, 4)
-
-        self.add(vbox)
-        self.show_all()
-
-        if pygobject:
-            styleContext = self.get_style_context()
-            color = styleContext.get_background_color(gtk.StateFlags.NORMAL)
-            heading.override_background_color(0, color)
-        else:
-            style = self.rc_get_style()
-            heading.modify_base(0, style.bg[0])
-
-
-#-------------------------------------------------------------------------------
-
-class MessagesWidget(gtk.Frame):
-    """The widget for the messages."""
-    @staticmethod
-    def getFaultFrame(alignment):
-        """Get the fault frame from the given alignment."""
-        return alignment.get_children()[0]
-
-    def __init__(self, gui):
-        gtk.Frame.__init__(self)
-
-        self._gui = gui
-        self.set_label(xstr("pirep_messages"))
-        label = self.get_label_widget()
-        label.set_use_underline(True)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 4,
-                              padding_left = 4, padding_right = 4)
-
-        self._outerBox = outerBox = gtk.EventBox()
-        outerBox.add(alignment)
-
-        self._innerBox = innerBox = gtk.EventBox()
-        alignment.add(self._innerBox)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 1.0, yscale = 1.0)
-        alignment.set_padding(padding_top = 0, padding_bottom = 0,
-                              padding_left = 0, padding_right = 0)
-
-        innerBox.add(alignment)
-
-        scroller = gtk.ScrolledWindow()
-        scroller.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-        scroller.set_shadow_type(SHADOW_NONE)
-
-        self._messages = gtk.VBox()
-        self._messages.set_homogeneous(False)
-        scroller.add_with_viewport(self._messages)
-
-        alignment.add(scroller)
-
-        self._messageWidgets = []
-
-        self.add(outerBox)
-        self.show_all()
-
-    def addMessage(self, message):
-        """Add a message from the given sender."""
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                      xscale = 1.0, yscale = 0.0)
-        alignment.set_padding(padding_top = 2, padding_bottom = 2,
-                              padding_left = 4, padding_right = 4)
-
-        messageFrame = MessageFrame(message.message,
-                                    message.senderPID,
-                                    message.senderName)
-
-        alignment.add(messageFrame)
-        self._messages.pack_start(alignment, False, False, 4)
-        self._messages.show_all()
-
-        self._messageWidgets.append((alignment, messageFrame))
-
-    def reset(self):
-        """Reset the widget by removing all messages."""
-        for (alignment, messageFrame) in self._messageWidgets:
-            self._messages.remove(alignment)
-        self._messages.show_all()
-
-        self._messageWidgets = []
-
-#------------------------------------------------------------------------------
-
-class PIREPViewer(gtk.Dialog):
-    """The dialog for PIREP viewing."""
-    @staticmethod
-    def createFrame(label):
-        """Create a frame with the given label.
-
-        The frame will contain an alignment to properly distance the
-        insides. The alignment will contain a VBox to contain the real
-        contents.
-
-        The function returns a tuple with the following items:
-        - the frame,
-        - the inner VBox."""
-        frame = gtk.Frame(label = label)
-
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 1.0, yscale = 1.0)
-        frame.add(alignment)
-        alignment.set_padding(padding_top = 4, padding_bottom = 4,
-                              padding_left = 4, padding_right = 4)
-        box = gtk.VBox()
-        alignment.add(box)
-
-        return (frame, box)
-
-    @staticmethod
-    def getLabel(text, extraText = ""):
-        """Get a bold label with the given text."""
-        label = gtk.Label("<b>" + text + "</b>" + extraText)
-        label.set_use_markup(True)
-        label.set_alignment(0.0, 0.5)
-        return label
-
-    @staticmethod
-    def getDataLabel(width = None, xAlignment = 0.0):
-        """Get a bold label with the given text."""
-        label = gtk.Label()
-        if width is not None:
-            label.set_width_chars(width)
-        label.set_alignment(xAlignment, 0.5)
-        return label
-
-    @staticmethod
-    def getTextWindow(heightRequest = 40, editable = False):
-        """Get a scrollable text window.
-
-        Returns a tuple of the following items:
-        - the window,
-        - the text view."""
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.set_shadow_type(SHADOW_IN)
-        scrolledWindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-
-        textView = gtk.TextView()
-        textView.set_wrap_mode(WRAP_WORD)
-        textView.set_editable(editable)
-        textView.set_cursor_visible(editable)
-        textView.set_size_request(-1, heightRequest)
-        scrolledWindow.add(textView)
-
-        return (scrolledWindow, textView)
-
-    @staticmethod
-    def tableAttach(table, column, row, labelText, width = None,
-                    dataLabelXAlignment = 0.0):
-        """Attach a labeled data to the given column and row of the
-        table.
-
-        If width is given, that will be the width of the data
-        label.
-
-        Returns the data label attached."""
-        dataBox = gtk.HBox()
-        table.attach(dataBox, column, column+1, row, row+1)
-
-        dataLabel = PIREPViewer.addLabeledData(dataBox, labelText,
-                                               width = width)
-        dataLabel.set_alignment(dataLabelXAlignment, 0.5)
-
-        return dataLabel
-
-    @staticmethod
-    def addLabeledData(hBox, labelText, width = None, dataPadding = 8):
-        """Add a label and a data label to the given HBox.
-
-        Returns the data label."""
-        label = PIREPViewer.getLabel(labelText)
-        hBox.pack_start(label, False, False, 0)
-
-        dataLabel = PIREPViewer.getDataLabel(width = width)
-        hBox.pack_start(dataLabel, False, False, dataPadding)
-
-        return dataLabel
-
-    @staticmethod
-    def addHFiller(hBox, width = 8):
-        """Add a filler to the given horizontal box."""
-        filler = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                               xscale = 1.0, yscale = 1.0)
-        filler.set_size_request(width, -1)
-        hBox.pack_start(filler, False, False, 0)
-
-    @staticmethod
-    def addVFiller(vBox, height = 4):
-        """Add a filler to the given vertical box."""
-        filler = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                               xscale = 1.0, yscale = 1.0)
-        filler.set_size_request(-1, height)
-        vBox.pack_start(filler, False, False, 0)
-
-    @staticmethod
-    def timestamp2text(label, timestamp):
-        """Convert the given timestamp into a text containing the hour
-        and minute in UTC and put that text into the given label."""
-        tm = time.gmtime(timestamp)
-        label.set_text("%02d:%02d" % (tm.tm_hour, tm.tm_min))
-
-    def __init__(self, gui, showMessages = False):
-        """Construct the PIREP viewer."""
-        super(PIREPViewer, self).__init__(title = WINDOW_TITLE_BASE +
-                                          " - " +
-                                          xstr("pirepView_title"),
-                                          parent = gui.mainWindow)
-
-        self.set_resizable(False)
-
-        self._gui = gui
-
-        contentArea = self.get_content_area()
-
-        self._notebook = gtk.Notebook()
-        contentArea.pack_start(self._notebook, False, False, 4)
-
-        dataTab = self._buildDataTab()
-        label = gtk.Label(xstr("pirepView_tab_data"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("pirepView_tab_data_tooltip"))
-        self._notebook.append_page(dataTab, label)
-
-        commentsTab = self._buildCommentsTab()
-        label = gtk.Label(xstr("pirepView_tab_comments"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("pirepView_tab_comments_tooltip"))
-        self._notebook.append_page(commentsTab, label)
-
-        logTab = self._buildLogTab()
-        label = gtk.Label(xstr("pirepView_tab_log"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("pirepView_tab_log_tooltip"))
-        self._notebook.append_page(logTab, label)
-
-        self._showMessages = showMessages
-        if showMessages:
-            messagesTab = self._buildMessagesTab()
-            label = gtk.Label(xstr("pirepView_tab_messages"))
-            label.set_use_underline(True)
-            label.set_tooltip_text(xstr("pirepView_tab_messages_tooltip"))
-            self._notebook.append_page(messagesTab, label)
-
-        self._okButton = self.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-        self._okButton.set_can_default(True)
-
-    def setPIREP(self, pirep):
-        """Setup the data in the dialog from the given PIREP."""
-        bookedFlight = pirep.bookedFlight
-
-        self._callsign.set_text(bookedFlight.callsign)
-        self._tailNumber.set_text(bookedFlight.tailNumber)
-        aircraftType = xstr("aircraft_" + icaoCodes[bookedFlight.aircraftType].lower())
-        self._aircraftType.set_text(aircraftType)
-
-        self._departureICAO.set_text(bookedFlight.departureICAO)
-        self._departureTime.set_text("%02d:%02d" % \
-                                     (bookedFlight.departureTime.hour,
-                                      bookedFlight.departureTime.minute))
-
-        self._arrivalICAO.set_text(bookedFlight.arrivalICAO)
-        self._arrivalTime.set_text("%02d:%02d" % \
-                                   (bookedFlight.arrivalTime.hour,
-                                    bookedFlight.arrivalTime.minute))
-
-        self._numPassengers.set_text(str(bookedFlight.numPassengers))
-        self._numCrew.set_text(str(bookedFlight.numCrew))
-        self._bagWeight.set_text(str(bookedFlight.bagWeight))
-        self._cargoWeight.set_text(str(bookedFlight.cargoWeight))
-        self._mailWeight.set_text(str(bookedFlight.mailWeight))
-
-        self._route.get_buffer().set_text(bookedFlight.route)
-
-        self._filedCruiseLevel.set_text("FL" + str(pirep.filedCruiseAltitude/100))
-
-        if pirep.cruiseAltitude != pirep.filedCruiseAltitude:
-            self._modifiedCruiseLevel.set_text("FL" + str(pirep.cruiseAltitude/100))
-        else:
-            self._modifiedCruiseLevel.set_text("-")
-
-        self._userRoute.get_buffer().set_text(pirep.route)
-
-        self._departureMETAR.get_buffer().set_text(pirep.departureMETAR)
-
-        self._arrivalMETAR.get_buffer().set_text(pirep.arrivalMETAR)
-        self._departureRunway.set_text(pirep.departureRunway)
-        self._sid.set_text(pirep.sid)
-
-        self._star.set_text("-" if pirep.star is None else pirep.star)
-        self._transition.set_text("-" if pirep.transition is None else pirep.transition)
-        self._approachType.set_text(pirep.approachType)
-        self._arrivalRunway.set_text(pirep.arrivalRunway)
-
-        PIREPViewer.timestamp2text(self._blockTimeStart, pirep.blockTimeStart)
-        PIREPViewer.timestamp2text(self._blockTimeEnd, pirep.blockTimeEnd)
-        PIREPViewer.timestamp2text(self._flightTimeStart, pirep.flightTimeStart)
-        PIREPViewer.timestamp2text(self._flightTimeEnd, pirep.flightTimeEnd)
-
-        self._flownDistance.set_text("%.1f" % (pirep.flownDistance,))
-        self._fuelUsed.set_text("%.0f" % (pirep.fuelUsed,))
-
-        rating = pirep.rating
-        if rating<0:
-            self._rating.set_markup('<b><span foreground="red">NO GO</span></b>')
-        else:
-            self._rating.set_text("%.1f %%" % (rating,))
-
-        self._flownNumCrew.set_text("%d" % (pirep.numCrew,))
-        self._flownNumPassengers.set_text("%d" % (pirep.numPassengers,))
-        self._flownBagWeight.set_text("%.0f" % (pirep.bagWeight,))
-        self._flownCargoWeight.set_text("%.0f" % (pirep.cargoWeight,))
-        self._flownMailWeight.set_text("%.0f" % (pirep.mailWeight,))
-        self._flightType.set_text(xstr("flighttype_" +
-                                       flightType2string(pirep.flightType)))
-        self._online.set_text(xstr("pirepView_" +
-                                   ("yes" if pirep.online else "no")))
-
-        delayCodes = ""
-        for code in pirep.delayCodes:
-            if delayCodes: delayCodes += ", "
-            delayCodes += code
-
-        self._delayCodes.get_buffer().set_text(delayCodes)
-
-        self._comments.get_buffer().set_text(pirep.comments)
-        self._flightDefects.get_buffer().set_text(pirep.flightDefects)
-
-        logBuffer = self._log.get_buffer()
-        logBuffer.set_text("")
-        lineIndex = 0
-        for (timeStr, line) in pirep.logLines:
-            isFault = lineIndex in pirep.faultLineIndexes
-            appendTextBuffer(logBuffer,
-                             formatFlightLogLine(timeStr, line),
-                             isFault = isFault)
-            lineIndex += 1
-
-        if self._showMessages:
-            self._messages.reset()
-            for message in pirep.messages:
-                self._messages.addMessage(message)
-
-        self._notebook.set_current_page(0)
-        self._okButton.grab_default()
-
-    def _buildDataTab(self):
-        """Build the data tab of the viewer."""
-        table = gtk.Table(1, 2)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(True)
-
-        box1 = gtk.VBox()
-        table.attach(box1, 0, 1, 0, 1)
-
-        box2 = gtk.VBox()
-        table.attach(box2, 1, 2, 0, 1)
-
-        flightFrame = self._buildFlightFrame()
-        box1.pack_start(flightFrame, False, False, 4)
-
-        routeFrame = self._buildRouteFrame()
-        box1.pack_start(routeFrame, False, False, 4)
-
-        departureFrame = self._buildDepartureFrame()
-        box2.pack_start(departureFrame, True, True, 4)
-
-        arrivalFrame = self._buildArrivalFrame()
-        box2.pack_start(arrivalFrame, True, True, 4)
-
-        statisticsFrame = self._buildStatisticsFrame()
-        box2.pack_start(statisticsFrame, False, False, 4)
-
-        miscellaneousFrame = self._buildMiscellaneousFrame()
-        box1.pack_start(miscellaneousFrame, False, False, 4)
-
-        return table
-
-    def _buildFlightFrame(self):
-        """Build the frame for the flight data."""
-
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_flight"))
-
-        dataBox = gtk.HBox()
-        mainBox.pack_start(dataBox, False, False, 0)
-
-        self._callsign = \
-            PIREPViewer.addLabeledData(dataBox,
-                                       xstr("pirepView_callsign"),
-                                       width = 8)
-
-        self._tailNumber = \
-            PIREPViewer.addLabeledData(dataBox,
-                                       xstr("pirepView_tailNumber"),
-                                       width = 7)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        dataBox = gtk.HBox()
-        mainBox.pack_start(dataBox, False, False, 0)
-
-        self._aircraftType = \
-            PIREPViewer.addLabeledData(dataBox,
-                                       xstr("pirepView_aircraftType"),
-                                       width = 25)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        table = gtk.Table(3, 2)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-
-        self._departureICAO = \
-            PIREPViewer.tableAttach(table, 0, 0,
-                                    xstr("pirepView_departure"),
-                                    width = 5)
-
-        self._departureTime = \
-            PIREPViewer.tableAttach(table, 1, 0,
-                                    xstr("pirepView_departure_time"),
-                                    width = 6)
-
-        self._arrivalICAO = \
-            PIREPViewer.tableAttach(table, 0, 1,
-                                    xstr("pirepView_arrival"),
-                                    width = 5)
-
-        self._arrivalTime = \
-            PIREPViewer.tableAttach(table, 1, 1,
-                                    xstr("pirepView_arrival_time"),
-                                    width = 6)
-
-        table = gtk.Table(3, 2)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-
-        self._numPassengers = \
-            PIREPViewer.tableAttach(table, 0, 0,
-                                    xstr("pirepView_numPassengers"),
-                                    width = 4)
-
-        self._numCrew = \
-            PIREPViewer.tableAttach(table, 1, 0,
-                                    xstr("pirepView_numCrew"),
-                                    width = 3)
-
-        self._bagWeight = \
-            PIREPViewer.tableAttach(table, 0, 1,
-                                    xstr("pirepView_bagWeight"),
-                                    width = 5)
-
-        self._cargoWeight = \
-            PIREPViewer.tableAttach(table, 1, 1,
-                                    xstr("pirepView_cargoWeight"),
-                                    width = 5)
-
-        self._mailWeight = \
-            PIREPViewer.tableAttach(table, 2, 1,
-                                    xstr("pirepView_mailWeight"),
-                                    width = 5)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_route")),
-                           False, False, 0)
-
-        (routeWindow, self._route) = PIREPViewer.getTextWindow()
-        mainBox.pack_start(routeWindow, False, False, 0)
-
-        return frame
-
-    def _buildRouteFrame(self):
-        """Build the frame for the user-specified route and flight
-        level."""
-
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_route"))
-
-        levelBox = gtk.HBox()
-        mainBox.pack_start(levelBox, False, False, 0)
-
-        self._filedCruiseLevel = \
-            PIREPViewer.addLabeledData(levelBox,
-                                       xstr("pirepView_filedCruiseLevel"),
-                                       width = 6)
-
-        self._modifiedCruiseLevel = \
-            PIREPViewer.addLabeledData(levelBox,
-                                       xstr("pirepView_modifiedCruiseLevel"),
-                                       width = 6)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        (routeWindow, self._userRoute) = PIREPViewer.getTextWindow()
-        mainBox.pack_start(routeWindow, False, False, 0)
-
-        return frame
-
-    def _buildDepartureFrame(self):
-        """Build the frame for the departure data."""
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_departure"))
-
-        mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
-                           False, False, 0)
-        (metarWindow, self._departureMETAR) = \
-            PIREPViewer.getTextWindow(heightRequest = -1)
-        mainBox.pack_start(metarWindow, True, True, 0)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        dataBox = gtk.HBox()
-        mainBox.pack_start(dataBox, False, False, 0)
-
-        self._departureRunway = \
-            PIREPViewer.addLabeledData(dataBox,
-                                       xstr("pirepView_runway"),
-                                       width = 5)
-
-        self._sid = \
-            PIREPViewer.addLabeledData(dataBox,
-                                       xstr("pirepView_sid"),
-                                       width = 12)
-
-        return frame
-
-    def _buildArrivalFrame(self):
-        """Build the frame for the arrival data."""
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_arrival"))
-
-        mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
-                           False, False, 0)
-        (metarWindow, self._arrivalMETAR) = \
-            PIREPViewer.getTextWindow(heightRequest = -1)
-        mainBox.pack_start(metarWindow, True, True, 0)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        table = gtk.Table(2, 2)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-
-        self._star = \
-            PIREPViewer.tableAttach(table, 0, 0,
-                                    xstr("pirepView_star"),
-                                    width = 12)
-
-        self._transition = \
-            PIREPViewer.tableAttach(table, 1, 0,
-                                    xstr("pirepView_transition"),
-                                    width = 12)
-
-        self._approachType = \
-            PIREPViewer.tableAttach(table, 0, 1,
-                                    xstr("pirepView_approachType"),
-                                    width = 7)
-
-        self._arrivalRunway = \
-            PIREPViewer.tableAttach(table, 1, 1,
-                                    xstr("pirepView_runway"),
-                                    width = 5)
-
-        return frame
-
-    def _buildStatisticsFrame(self):
-        """Build the frame for the statistics data."""
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_statistics"))
-
-        table = gtk.Table(4, 2)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-        table.set_homogeneous(False)
-
-        self._blockTimeStart = \
-            PIREPViewer.tableAttach(table, 0, 0,
-                                    xstr("pirepView_blockTimeStart"),
-                                    width = 6)
-
-        self._blockTimeEnd = \
-            PIREPViewer.tableAttach(table, 1, 0,
-                                    xstr("pirepView_blockTimeEnd"),
-                                    width = 8)
-
-        self._flightTimeStart = \
-            PIREPViewer.tableAttach(table, 0, 1,
-                                    xstr("pirepView_flightTimeStart"),
-                                    width = 6)
-
-        self._flightTimeEnd = \
-            PIREPViewer.tableAttach(table, 1, 1,
-                                    xstr("pirepView_flightTimeEnd"),
-                                    width = 6)
-
-        self._flownDistance = \
-            PIREPViewer.tableAttach(table, 0, 2,
-                                    xstr("pirepView_flownDistance"),
-                                    width = 8)
-
-        self._fuelUsed = \
-            PIREPViewer.tableAttach(table, 1, 2,
-                                    xstr("pirepView_fuelUsed"),
-                                    width = 6)
-
-        self._rating = \
-            PIREPViewer.tableAttach(table, 0, 3,
-                                    xstr("pirepView_rating"),
-                                    width = 7)
-        return frame
-
-    def _buildMiscellaneousFrame(self):
-        """Build the frame for the miscellaneous data."""
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_miscellaneous"))
-
-        table = gtk.Table(3, 2)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-
-        self._flownNumPassengers = \
-            PIREPViewer.tableAttach(table, 0, 0,
-                                    xstr("pirepView_numPassengers"),
-                                    width = 4)
-
-        self._flownNumCrew = \
-            PIREPViewer.tableAttach(table, 1, 0,
-                                    xstr("pirepView_numCrew"),
-                                    width = 3)
-
-        self._flownBagWeight = \
-            PIREPViewer.tableAttach(table, 0, 1,
-                                    xstr("pirepView_bagWeight"),
-                                    width = 5)
-
-        self._flownCargoWeight = \
-            PIREPViewer.tableAttach(table, 1, 1,
-                                    xstr("pirepView_cargoWeight"),
-                                    width = 6)
-
-        self._flownMailWeight = \
-            PIREPViewer.tableAttach(table, 2, 1,
-                                    xstr("pirepView_mailWeight"),
-                                    width = 5)
-
-        self._flightType = \
-            PIREPViewer.tableAttach(table, 0, 2,
-                                    xstr("pirepView_flightType"),
-                                    width = 15)
-
-        self._online = \
-            PIREPViewer.tableAttach(table, 1, 2,
-                                    xstr("pirepView_online"),
-                                    width = 5)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_delayCodes")),
-                           False, False, 0)
-
-        (textWindow, self._delayCodes) = PIREPViewer.getTextWindow()
-        mainBox.pack_start(textWindow, False, False, 0)
-
-        return frame
-
-    def _buildCommentsTab(self):
-        """Build the tab with the comments and flight defects."""
-        table = gtk.Table(2, 1)
-        table.set_col_spacings(16)
-
-        (frame, commentsBox) = \
-            PIREPViewer.createFrame(xstr("pirepView_comments"))
-        table.attach(frame, 0, 1, 0, 1)
-
-        (commentsWindow, self._comments) = \
-            PIREPViewer.getTextWindow(heightRequest = -1)
-        commentsBox.pack_start(commentsWindow, True, True, 0)
-
-        (frame, flightDefectsBox) = \
-            PIREPViewer.createFrame(xstr("pirepView_flightDefects"))
-        table.attach(frame, 1, 2, 0, 1)
-
-        (flightDefectsWindow, self._flightDefects) = \
-            PIREPViewer.getTextWindow(heightRequest = -1)
-        flightDefectsBox.pack_start(flightDefectsWindow, True, True, 0)
-
-        return table
-
-    def _buildLogTab(self):
-        """Build the log tab."""
-        mainBox = gtk.VBox()
-
-        (logWindow, self._log) = PIREPViewer.getTextWindow(heightRequest = -1)
-        addFaultTag(self._log.get_buffer())
-        mainBox.pack_start(logWindow, True, True, 0)
-
-        return mainBox
-
-    def _buildMessagesTab(self):
-        """Build the messages tab."""
-        mainBox = gtk.VBox()
-
-        self._messages = MessagesWidget(self._gui)
-        mainBox.pack_start(self._messages, True, True, 0)
-
-        return mainBox
-
-#------------------------------------------------------------------------------
-
-class PIREPEditor(gtk.Dialog):
-    """A PIREP editor dialog."""
-    _delayCodeRE = re.compile("([0-9]{2,3})( \([^\)]*\))")
-
-    @staticmethod
-    def tableAttachWidget(table, column, row, labelText, widget):
-        """Attach the given widget with the given label to the given table.
-
-        The label will got to cell (column, row), the widget to cell
-        (column+1, row)."""
-        label = gtk.Label("<b>" + labelText + "</b>")
-        label.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, column, column + 1, row, row + 1)
-
-        table.attach(widget, column + 1, column + 2, row, row + 1)
-
-    @staticmethod
-    def tableAttachSpinButton(table, column, row, labelText, maxValue,
-                              minValue = 0, stepIncrement = 1,
-                              pageIncrement = 10, numeric = True,
-                              width = 3):
-        """Attach a spin button with the given label to the given table.
-
-        The label will got to cell (column, row), the spin button to cell
-        (column+1, row)."""
-        button = gtk.SpinButton()
-        button.set_range(min = minValue, max = maxValue)
-        button.set_increments(step = stepIncrement, page = pageIncrement)
-        button.set_numeric(True)
-        button.set_width_chars(width)
-        button.set_alignment(1.0)
-
-        PIREPEditor.tableAttachWidget(table, column, row, labelText, button)
-
-        return button
-
-    @staticmethod
-    def tableAttachTimeEntry(table, column, row, labelText):
-        """Attach a time entry widget with the given label to the given table.
-
-        The label will got to cell (column, row), the spin button to cell
-        (column+1, row)."""
-        entry = TimeEntry()
-        entry.set_width_chars(5)
-        entry.set_alignment(1.0)
-
-        PIREPEditor.tableAttachWidget(table, column, row, labelText, entry)
-
-        return entry
-
-    def __init__(self, gui):
-        """Construct the PIREP viewer."""
-        super(PIREPEditor, self).__init__(title = WINDOW_TITLE_BASE +
-                                          " - " +
-                                          xstr("pirepEdit_title"),
-                                          parent = gui.mainWindow)
-
-        self.set_resizable(False)
-
-        self._gui = gui
-
-        self._pirep = None
-
-        contentArea = self.get_content_area()
-
-        self._notebook = gtk.Notebook()
-        contentArea.pack_start(self._notebook, False, False, 4)
-
-        dataTab = self._buildDataTab()
-        label = gtk.Label(xstr("pirepView_tab_data"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("pirepView_tab_data_tooltip"))
-        self._notebook.append_page(dataTab, label)
-
-        self._flightInfo = self._buildCommentsTab()
-        label = gtk.Label(xstr("pirepView_tab_comments"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("pirepView_tab_comments_tooltip"))
-        self._notebook.append_page(self._flightInfo, label)
-
-        logTab = self._buildLogTab()
-        label = gtk.Label(xstr("pirepView_tab_log"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("pirepView_tab_log_tooltip"))
-        self._notebook.append_page(logTab, label)
-
-        self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
-
-        self._okButton = self.add_button(xstr("button_save"), RESPONSETYPE_NONE)
-        self._okButton.connect("clicked", self._okClicked)
-        self._okButton.set_can_default(True)
-        self._modified = False
-        self._toSave = False
-
-    def setPIREP(self, pirep):
-        """Setup the data in the dialog from the given PIREP."""
-        self._pirep = pirep
-
-        bookedFlight = pirep.bookedFlight
-
-        self._callsign.set_text(bookedFlight.callsign)
-        self._tailNumber.set_text(bookedFlight.tailNumber)
-        aircraftType = xstr("aircraft_" + icaoCodes[bookedFlight.aircraftType].lower())
-        self._aircraftType.set_text(aircraftType)
-
-        self._departureICAO.set_text(bookedFlight.departureICAO)
-        self._departureTime.set_text("%02d:%02d" % \
-                                     (bookedFlight.departureTime.hour,
-                                      bookedFlight.departureTime.minute))
-
-        self._arrivalICAO.set_text(bookedFlight.arrivalICAO)
-        self._arrivalTime.set_text("%02d:%02d" % \
-                                   (bookedFlight.arrivalTime.hour,
-                                    bookedFlight.arrivalTime.minute))
-
-        self._numPassengers.set_text(str(bookedFlight.numPassengers))
-        self._numCrew.set_text(str(bookedFlight.numCrew))
-        self._bagWeight.set_text(str(bookedFlight.bagWeight))
-        self._cargoWeight.set_text(str(bookedFlight.cargoWeight))
-        self._mailWeight.set_text(str(bookedFlight.mailWeight))
-
-        self._route.get_buffer().set_text(bookedFlight.route)
-
-        self._filedCruiseLevel.set_value(pirep.filedCruiseAltitude/100)
-        self._modifiedCruiseLevel.set_value(pirep.cruiseAltitude/100)
-
-        self._userRoute.get_buffer().set_text(pirep.route)
-
-        self._departureMETAR.get_buffer().set_text(pirep.departureMETAR)
-
-        self._arrivalMETAR.get_buffer().set_text(pirep.arrivalMETAR)
-        self._departureRunway.set_text(pirep.departureRunway)
-        self._sid.get_child().set_text(pirep.sid)
-
-        if not pirep.star:
-            self._star.set_active(0)
-        else:
-            self._star.get_child().set_text(pirep.star)
-
-        if not pirep.transition:
-            self._transition.set_active(0)
-        else:
-            self._transition.get_child().set_text(pirep.transition)
-        self._approachType.set_text(pirep.approachType)
-        self._arrivalRunway.set_text(pirep.arrivalRunway)
-
-        self._blockTimeStart.setTimestamp(pirep.blockTimeStart)
-        self._blockTimeEnd.setTimestamp(pirep.blockTimeEnd)
-        self._flightTimeStart.setTimestamp(pirep.flightTimeStart)
-        self._flightTimeEnd.setTimestamp(pirep.flightTimeEnd)
-
-        self._flownDistance.set_text("%.1f" % (pirep.flownDistance,))
-        self._fuelUsed.set_value(int(pirep.fuelUsed))
-
-        rating = pirep.rating
-        if rating<0:
-            self._rating.set_markup('<b><span foreground="red">NO GO</span></b>')
-        else:
-            self._rating.set_text("%.1f %%" % (rating,))
-
-        self._flownNumCrew.set_value(pirep.numCrew)
-        self._flownNumPassengers.set_value(pirep.numPassengers)
-        self._flownBagWeight.set_value(pirep.bagWeight)
-        self._flownCargoWeight.set_value(pirep.cargoWeight)
-        self._flownMailWeight.set_value(pirep.mailWeight)
-        self._flightType.set_active(flightType2index(pirep.flightType))
-        self._online.set_active(pirep.online)
-
-        self._flightInfo.reset()
-        self._flightInfo.enable(bookedFlight.aircraftType)
-
-        delayCodes = ""
-        for code in pirep.delayCodes:
-            if delayCodes: delayCodes += ", "
-            delayCodes += code
-            m = PIREPEditor._delayCodeRE.match(code)
-            if m:
-                self._flightInfo.activateDelayCode(m.group(1))
-
-        self._delayCodes.get_buffer().set_text(delayCodes)
-
-        self._flightInfo.comments = pirep.comments
-        if pirep.flightDefects.find("<br/></b>")!=-1:
-            flightDefects = pirep.flightDefects.split("<br/></b>")
-            caption = flightDefects[0]
-            index = 0
-            for defect in flightDefects[1:]:
-                if defect.find("<b>")!=-1:
-                    (explanation, nextCaption) = defect.split("<b>")
-                else:
-                    explanation = defect
-                    nextCaption = None
-                self._flightInfo.addFault(index, caption)
-                self._flightInfo.setExplanation(index, explanation)
-                index += 1
-                caption = nextCaption
-
-        # self._comments.get_buffer().set_text(pirep.comments)
-        # self._flightDefects.get_buffer().set_text(pirep.flightDefects)
-
-        logBuffer = self._log.get_buffer()
-        logBuffer.set_text("")
-        lineIndex = 0
-        for (timeStr, line) in pirep.logLines:
-            isFault = lineIndex in pirep.faultLineIndexes
-            appendTextBuffer(logBuffer,
-                             formatFlightLogLine(timeStr, line),
-                             isFault = isFault)
-            lineIndex += 1
-
-        self._notebook.set_current_page(0)
-        self._okButton.grab_default()
-
-        self._modified = False
-        self._updateButtons()
-        self._modified = True
-        self._toSave = False
-
-    def delayCodesChanged(self):
-        """Called when the delay codes have changed."""
-        self._updateButtons()
-
-    def commentsChanged(self):
-        """Called when the comments have changed."""
-        self._updateButtons()
-
-    def faultExplanationsChanged(self):
-        """Called when the fault explanations have changed."""
-        self._updateButtons()
-
-    def _buildDataTab(self):
-        """Build the data tab of the viewer."""
-        table = gtk.Table(1, 2)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(True)
-
-        box1 = gtk.VBox()
-        table.attach(box1, 0, 1, 0, 1)
-
-        box2 = gtk.VBox()
-        table.attach(box2, 1, 2, 0, 1)
-
-        flightFrame = self._buildFlightFrame()
-        box1.pack_start(flightFrame, False, False, 4)
-
-        routeFrame = self._buildRouteFrame()
-        box1.pack_start(routeFrame, False, False, 4)
-
-        departureFrame = self._buildDepartureFrame()
-        box2.pack_start(departureFrame, True, True, 4)
-
-        arrivalFrame = self._buildArrivalFrame()
-        box2.pack_start(arrivalFrame, True, True, 4)
-
-        statisticsFrame = self._buildStatisticsFrame()
-        box2.pack_start(statisticsFrame, False, False, 4)
-
-        miscellaneousFrame = self._buildMiscellaneousFrame()
-        box1.pack_start(miscellaneousFrame, False, False, 4)
-
-        return table
-
-    def _buildFlightFrame(self):
-        """Build the frame for the flight data."""
-
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_flight"))
-
-        dataBox = gtk.HBox()
-        mainBox.pack_start(dataBox, False, False, 0)
-
-        self._callsign = \
-            PIREPViewer.addLabeledData(dataBox,
-                                       xstr("pirepView_callsign"),
-                                       width = 8)
-
-        self._tailNumber = \
-            PIREPViewer.addLabeledData(dataBox,
-                                       xstr("pirepView_tailNumber"),
-                                       width = 7)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        dataBox = gtk.HBox()
-        mainBox.pack_start(dataBox, False, False, 0)
-
-        self._aircraftType = \
-            PIREPViewer.addLabeledData(dataBox,
-                                       xstr("pirepView_aircraftType"),
-                                       width = 25)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        table = gtk.Table(3, 2)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-
-        self._departureICAO = \
-            PIREPViewer.tableAttach(table, 0, 0,
-                                    xstr("pirepView_departure"),
-                                    width = 5)
-
-        self._departureTime = \
-            PIREPViewer.tableAttach(table, 1, 0,
-                                    xstr("pirepView_departure_time"),
-                                    width = 6)
-
-        self._arrivalICAO = \
-            PIREPViewer.tableAttach(table, 0, 1,
-                                    xstr("pirepView_arrival"),
-                                    width = 5)
-
-        self._arrivalTime = \
-            PIREPViewer.tableAttach(table, 1, 1,
-                                    xstr("pirepView_arrival_time"),
-                                    width = 6)
-
-        table = gtk.Table(3, 2)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-
-        self._numPassengers = \
-            PIREPViewer.tableAttach(table, 0, 0,
-                                    xstr("pirepView_numPassengers"),
-                                    width = 4)
-        self._numCrew = \
-            PIREPViewer.tableAttach(table, 1, 0,
-                                    xstr("pirepView_numCrew"),
-                                    width = 3)
-
-        self._bagWeight = \
-            PIREPViewer.tableAttach(table, 0, 1,
-                                    xstr("pirepView_bagWeight"),
-                                    width = 5)
-
-        self._cargoWeight = \
-            PIREPViewer.tableAttach(table, 1, 1,
-                                    xstr("pirepView_cargoWeight"),
-                                    width = 5)
-
-        self._mailWeight = \
-            PIREPViewer.tableAttach(table, 2, 1,
-                                    xstr("pirepView_mailWeight"),
-                                    width = 5)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_route")),
-                           False, False, 0)
-
-        (routeWindow, self._route) = PIREPViewer.getTextWindow()
-        mainBox.pack_start(routeWindow, False, False, 0)
-
-        return frame
-
-    def _buildRouteFrame(self):
-        """Build the frame for the user-specified route and flight
-        level."""
-
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_route"))
-
-        levelBox = gtk.HBox()
-        mainBox.pack_start(levelBox, False, False, 0)
-
-        label = PIREPViewer.getLabel(xstr("pirepView_filedCruiseLevel"),
-                                     xstr("pirepEdit_FL"))
-        levelBox.pack_start(label, False, False, 0)
-
-        self._filedCruiseLevel = gtk.SpinButton()
-        self._filedCruiseLevel.set_increments(step = 10, page = 100)
-        self._filedCruiseLevel.set_range(min = 0, max = 500)
-        self._filedCruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
-        self._filedCruiseLevel.set_numeric(True)
-        self._filedCruiseLevel.connect("value-changed", self._updateButtons)
-
-        levelBox.pack_start(self._filedCruiseLevel, False, False, 0)
-
-        PIREPViewer.addHFiller(levelBox)
-
-        label = PIREPViewer.getLabel(xstr("pirepView_modifiedCruiseLevel"),
-                                     xstr("pirepEdit_FL"))
-        levelBox.pack_start(label, False, False, 0)
-
-        self._modifiedCruiseLevel = gtk.SpinButton()
-        self._modifiedCruiseLevel.set_increments(step = 10, page = 100)
-        self._modifiedCruiseLevel.set_range(min = 0, max = 500)
-        self._modifiedCruiseLevel.set_tooltip_text(xstr("pirepEdit_modified_route_level_tooltip"))
-        self._modifiedCruiseLevel.set_numeric(True)
-        self._modifiedCruiseLevel.connect("value-changed", self._updateButtons)
-
-        levelBox.pack_start(self._modifiedCruiseLevel, False, False, 0)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        (routeWindow, self._userRoute) = \
-          PIREPViewer.getTextWindow(editable = True)
-        mainBox.pack_start(routeWindow, False, False, 0)
-        self._userRoute.get_buffer().connect("changed", self._updateButtons)
-        self._userRoute.set_tooltip_text(xstr("route_route_tooltip"))
-
-        return frame
-
-    def _buildDepartureFrame(self):
-        """Build the frame for the departure data."""
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_departure"))
-
-        mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
-                           False, False, 0)
-        (metarWindow, self._departureMETAR) = \
-            PIREPViewer.getTextWindow(heightRequest = -1,
-                                      editable = True)
-        self._departureMETAR.get_buffer().connect("changed", self._updateButtons)
-        self._departureMETAR.set_tooltip_text(xstr("takeoff_metar_tooltip"))
-        mainBox.pack_start(metarWindow, True, True, 0)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        dataBox = gtk.HBox()
-        mainBox.pack_start(dataBox, False, False, 0)
-
-        label = gtk.Label("<b>" + xstr("pirepView_runway") + "</b>")
-        label.set_use_markup(True)
-        dataBox.pack_start(label, False, False, 0)
-
-        # FIXME: quite the same as the runway entry boxes in the wizard
-        self._departureRunway = gtk.Entry()
-        self._departureRunway.set_width_chars(5)
-        self._departureRunway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
-        self._departureRunway.connect("changed", self._upperChanged)
-        dataBox.pack_start(self._departureRunway, False, False, 8)
-
-        label = gtk.Label("<b>" + xstr("pirepView_sid") + "</b>")
-        label.set_use_markup(True)
-        dataBox.pack_start(label, False, False, 0)
-
-        # FIXME: quite the same as the SID combo box in
-        # the flight wizard
-        if pygobject:
-            self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
-        else:
-            self._sid = gtk.ComboBoxEntry(comboModel)
-
-        self._sid.set_entry_text_column(0)
-        self._sid.get_child().set_width_chars(10)
-        self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
-        self._sid.connect("changed", self._upperChangedComboBox)
-
-        dataBox.pack_start(self._sid, False, False, 8)
-
-        return frame
-
-    def _buildArrivalFrame(self):
-        """Build the frame for the arrival data."""
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_arrival"))
-
-        mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
-                           False, False, 0)
-        (metarWindow, self._arrivalMETAR) = \
-            PIREPViewer.getTextWindow(heightRequest = -1,
-                                      editable = True)
-        self._arrivalMETAR.get_buffer().connect("changed", self._updateButtons)
-        self._arrivalMETAR.set_tooltip_text(xstr("landing_metar_tooltip"))
-        mainBox.pack_start(metarWindow, True, True, 0)
-
-        PIREPViewer.addVFiller(mainBox)
-
-        table = gtk.Table(2, 4)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-
-        # FIXME: quite the same as in the wizard
-        if pygobject:
-            self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
-        else:
-            self._star = gtk.ComboBoxEntry(comboModel)
-
-        self._star.set_entry_text_column(0)
-        self._star.get_child().set_width_chars(10)
-        self._star.set_tooltip_text(xstr("landing_star_tooltip"))
-        self._star.connect("changed", self._upperChangedComboBox)
-
-        PIREPEditor.tableAttachWidget(table, 0, 0,
-                                      xstr("pirepView_star"),
-                                      self._star)
-
-        # FIXME: quite the same as in the wizard
-        if pygobject:
-            self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
-        else:
-            self._transition = gtk.ComboBoxEntry(comboModel)
-
-        self._transition.set_entry_text_column(0)
-        self._transition.get_child().set_width_chars(10)
-        self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
-        self._transition.connect("changed", self._upperChangedComboBox)
-
-        PIREPEditor.tableAttachWidget(table, 2, 0,
-                                      xstr("pirepView_transition"),
-                                      self._transition)
-
-
-        # FIXME: quite the same as in the wizard
-        self._approachType = gtk.Entry()
-        self._approachType.set_width_chars(10)
-        self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
-        self._approachType.connect("changed", self._upperChanged)
-
-        PIREPEditor.tableAttachWidget(table, 0, 1,
-                                      xstr("pirepView_approachType"),
-                                      self._approachType)
-
-        # FIXME: quite the same as in the wizard
-        self._arrivalRunway = gtk.Entry()
-        self._arrivalRunway.set_width_chars(10)
-        self._arrivalRunway.set_tooltip_text(xstr("landing_runway_tooltip"))
-        self._arrivalRunway.connect("changed", self._upperChanged)
-
-        PIREPEditor.tableAttachWidget(table, 2, 1,
-                                      xstr("pirepView_runway"),
-                                      self._arrivalRunway)
-
-        return frame
-
-    def _buildStatisticsFrame(self):
-        """Build the frame for the statistics data."""
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_statistics"))
-
-        table = gtk.Table(4, 4)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-        table.set_homogeneous(False)
-
-        self._blockTimeStart = \
-            PIREPEditor.tableAttachTimeEntry(table, 0, 0,
-                                             xstr("pirepView_blockTimeStart"))
-        self._blockTimeStart.connect("changed", self._updateButtons)
-        self._blockTimeStart.set_tooltip_text(xstr("pirepEdit_block_time_start_tooltip"))
-
-        self._blockTimeEnd = \
-            PIREPEditor.tableAttachTimeEntry(table, 2, 0,
-                                             xstr("pirepView_blockTimeEnd"))
-        self._blockTimeEnd.connect("changed", self._updateButtons)
-        self._blockTimeEnd.set_tooltip_text(xstr("pirepEdit_block_time_end_tooltip"))
-
-        self._flightTimeStart = \
-            PIREPEditor.tableAttachTimeEntry(table, 0, 1,
-                                             xstr("pirepView_flightTimeStart"))
-        self._flightTimeStart.connect("changed", self._updateButtons)
-        self._flightTimeStart.set_tooltip_text(xstr("pirepEdit_flight_time_start_tooltip"))
-
-        self._flightTimeEnd = \
-            PIREPEditor.tableAttachTimeEntry(table, 2, 1,
-                                             xstr("pirepView_flightTimeEnd"))
-        self._flightTimeEnd.connect("changed", self._updateButtons)
-        self._flightTimeEnd.set_tooltip_text(xstr("pirepEdit_flight_time_end_tooltip"))
-
-        self._flownDistance = PIREPViewer.getDataLabel(width = 3)
-        PIREPEditor.tableAttachWidget(table, 0, 2,
-                                      xstr("pirepView_flownDistance"),
-                                      self._flownDistance)
-
-        self._fuelUsed = \
-            PIREPEditor.tableAttachSpinButton(table, 2, 2,
-                                              xstr("pirepView_fuelUsed"),
-                                              1000000)
-        self._fuelUsed.connect("value-changed", self._updateButtons)
-        self._fuelUsed.set_tooltip_text(xstr("pirepEdit_fuel_used_tooltip"))
-
-        self._rating = PIREPViewer.getDataLabel(width = 3)
-        PIREPEditor.tableAttachWidget(table, 0, 3,
-                                      xstr("pirepView_rating"),
-                                      self._rating)
-        return frame
-
-    def _buildMiscellaneousFrame(self):
-        """Build the frame for the miscellaneous data."""
-        (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_miscellaneous"))
-
-        table = gtk.Table(6, 2)
-        mainBox.pack_start(table, False, False, 0)
-        table.set_row_spacings(4)
-        table.set_col_spacings(8)
-        table.set_homogeneous(False)
-
-        self._flownNumPassengers = \
-            PIREPEditor.tableAttachSpinButton(table, 0, 0,
-                                              xstr("pirepView_numPassengers"),
-                                              300)
-        self._flownNumPassengers.connect("value-changed", self._updateButtons)
-        self._flownNumPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
-
-        self._flownNumCrew = \
-            PIREPEditor.tableAttachSpinButton(table, 2, 0,
-                                              xstr("pirepView_numCrew"),
-                                              10)
-        self._flownNumCrew.connect("value-changed", self._updateButtons)
-        self._flownNumCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
-
-        self._flownBagWeight = \
-            PIREPEditor.tableAttachSpinButton(table, 0, 1,
-                                              xstr("pirepView_bagWeight"),
-                                              100000, width = 6)
-        self._flownBagWeight.connect("value-changed", self._updateButtons)
-        self._flownBagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
-
-        self._flownCargoWeight = \
-            PIREPEditor.tableAttachSpinButton(table, 2, 1,
-                                              xstr("pirepView_cargoWeight"),
-                                              100000, width = 6)
-        self._flownCargoWeight.connect("value-changed", self._updateButtons)
-        self._flownCargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
-
-        self._flownMailWeight = \
-            PIREPEditor.tableAttachSpinButton(table, 4, 1,
-                                              xstr("pirepView_mailWeight"),
-                                              100000, width = 6)
-        self._flownMailWeight.connect("value-changed", self._updateButtons)
-        self._flownMailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
-
-        self._flightType = createFlightTypeComboBox()
-        PIREPEditor.tableAttachWidget(table, 0, 2,
-                                      xstr("pirepView_flightType"),
-                                      self._flightType)
-        self._flightType.connect("changed", self._updateButtons)
-        self._flightType.set_tooltip_text(xstr("pirepEdit_flight_type_tooltip"))
-
-        self._online = gtk.CheckButton(xstr("pirepEdit_online"))
-        table.attach(self._online, 2, 3, 2, 3)
-        self._online.connect("toggled", self._updateButtons)
-        self._online.set_tooltip_text(xstr("pirepEdit_online_tooltip"))
-
-        PIREPViewer.addVFiller(mainBox)
-
-        mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_delayCodes")),
-                           False, False, 0)
-
-        (textWindow, self._delayCodes) = PIREPViewer.getTextWindow()
-        mainBox.pack_start(textWindow, False, False, 0)
-        self._delayCodes.set_tooltip_text(xstr("pirepEdit_delayCodes_tooltip"))
-
-        return frame
-
-    def _buildCommentsTab(self):
-        """Build the tab with the comments and flight defects."""
-        return FlightInfo(self._gui, callbackObject = self)
-
-    def _buildLogTab(self):
-        """Build the log tab."""
-        mainBox = gtk.VBox()
-
-        (logWindow, self._log) = PIREPViewer.getTextWindow(heightRequest = -1)
-        addFaultTag(self._log.get_buffer())
-        mainBox.pack_start(logWindow, True, True, 0)
-
-        return mainBox
-
-    def _upperChanged(self, entry, arg = None):
-        """Called when the value of some entry widget has changed and the value
-        should be converted to uppercase."""
-        entry.set_text(entry.get_text().upper())
-        self._updateButtons()
-        #self._valueChanged(entry, arg)
-
-    def _upperChangedComboBox(self, comboBox):
-        """Called for combo box widgets that must be converted to uppercase."""
-        entry = comboBox.get_child()
-        if comboBox.get_active()==-1:
-            entry.set_text(entry.get_text().upper())
-        self._updateButtons()
-        #self._valueChanged(entry)
-
-    def _updateButtons(self, *kwargs):
-        """Update the activity state of the buttons."""
-        pirep = self._pirep
-        bookedFlight = pirep.bookedFlight
-
-        departureMinutes = \
-            bookedFlight.departureTime.hour*60 + bookedFlight.departureTime.minute
-        departureDifference = abs(Flight.getMinutesDifference(self._blockTimeStart.minutes,
-                                                              departureMinutes))
-        flightStartDifference = \
-            Flight.getMinutesDifference(self._flightTimeStart.minutes,
-                                        self._blockTimeStart.minutes)
-        arrivalMinutes = \
-            bookedFlight.arrivalTime.hour*60 + bookedFlight.arrivalTime.minute
-        arrivalDifference = abs(Flight.getMinutesDifference(self._blockTimeEnd.minutes,
-                                                            arrivalMinutes))
-        flightEndDifference = \
-            Flight.getMinutesDifference(self._blockTimeEnd.minutes,
-                                        self._flightTimeEnd.minutes)
-
-        timesOK = self._flightInfo.hasComments or \
-                  self._flightInfo.hasDelayCode or \
-                  (departureDifference<=Flight.TIME_ERROR_DIFFERENCE and
-                   arrivalDifference<=Flight.TIME_ERROR_DIFFERENCE and
-                   flightStartDifference>=0 and flightStartDifference<30 and
-                   flightEndDifference>=0 and flightEndDifference<30)
-
-        text = self._sid.get_child().get_text()
-        sid = text if self._sid.get_active()!=0 and text and text!="N/A" \
-               else None
-
-        text = self._star.get_child().get_text()
-        star = text if self._star.get_active()!=0 and text and text!="N/A" \
-               else None
-
-        text = self._transition.get_child().get_text()
-        transition = text if self._transition.get_active()!=0 \
-                     and text and text!="N/A" else None
-
-
-        buffer = self._userRoute.get_buffer()
-        route =  buffer.get_text(buffer.get_start_iter(),
-                                 buffer.get_end_iter(), True)
-
-        self._okButton.set_sensitive(self._modified and timesOK and
-                                     self._flightInfo.faultsFullyExplained and
-                                     self._flownNumPassengers.get_value_as_int()>0 and
-                                     self._flownNumCrew.get_value_as_int()>2 and
-                                     self._fuelUsed.get_value_as_int()>0 and
-                                     self._departureRunway.get_text_length()>0 and
-                                     self._arrivalRunway.get_text_length()>0 and
-                                     self._departureMETAR.get_buffer().get_char_count()>0 and
-                                     self._arrivalMETAR.get_buffer().get_char_count()>0 and
-                                     self._filedCruiseLevel.get_value_as_int()>=50 and
-                                     self._modifiedCruiseLevel.get_value_as_int()>=50 and
-                                     sid is not None and (star is not None or
-                                     transition is not None) and route!="" and
-                                     self._approachType.get_text()!="")
-
-    def _okClicked(self, button):
-        """Called when the OK button has been clicked.
-
-        The PIREP is updated from the data in the window."""
-        if not askYesNo(xstr("pirepEdit_save_question"), parent = self):
-            self.response(RESPONSETYPE_CANCEL)
-
-        pirep = self._pirep
-
-        pirep.filedCruiseAltitude = \
-          self._filedCruiseLevel.get_value_as_int() * 100
-        pirep.cruiseAltitude = \
-          self._modifiedCruiseLevel.get_value_as_int() * 100
-
-        pirep.route = getTextViewText(self._userRoute)
-
-        pirep.departureMETAR = getTextViewText(self._departureMETAR)
-        pirep.departureRunway = self._departureRunway.get_text()
-        pirep.sid = self._sid.get_child().get_text()
-
-        pirep.arrivalMETAR = getTextViewText(self._arrivalMETAR)
-        pirep.star = None if self._star.get_active()==0 \
-          else self._star.get_child().get_text()
-        pirep.transition = None if self._transition.get_active()==0 \
-          else self._transition.get_child().get_text()
-        pirep.approachType = self._approachType.get_text()
-        pirep.arrivalRunway = self._arrivalRunway.get_text()
-
-        pirep.blockTimeStart = \
-          self._blockTimeStart.getTimestampFrom(pirep.blockTimeStart)
-        pirep.blockTimeEnd = \
-          self._blockTimeEnd.getTimestampFrom(pirep.blockTimeEnd)
-        pirep.flightTimeStart = \
-          self._flightTimeStart.getTimestampFrom(pirep.flightTimeStart)
-        pirep.flightTimeEnd = \
-          self._flightTimeEnd.getTimestampFrom(pirep.flightTimeEnd)
-
-        pirep.fuelUsed = self._fuelUsed.get_value()
-
-        pirep.numCrew = self._flownNumCrew.get_value()
-        pirep.numPassengers = self._flownNumPassengers.get_value()
-        pirep.bagWeight = self._flownBagWeight.get_value()
-        pirep.cargoWeight = self._flownCargoWeight.get_value()
-        pirep.mailWeight = self._flownMailWeight.get_value()
-
-        pirep.flightType = flightTypes[self._flightType.get_active()]
-        pirep.online = self._online.get_active()
-
-        pirep.delayCodes = self._flightInfo.delayCodes
-        pirep.comments = self._flightInfo.comments
-        pirep.flightDefects = self._flightInfo.faultsAndExplanations
-
-        self.response(RESPONSETYPE_OK)
-
-
-#------------------------------------------------------------------------------
Index: src/mlx/gui/prefs.py
===================================================================
--- src/mlx/gui/prefs.py	(revision 738)
+++ src/mlx/gui/prefs.py	(revision 738)
@@ -0,0 +1,855 @@
+
+from common import *
+
+from mlx.i18n import xstr
+import mlx.const as const
+import mlx.config as config
+
+import urlparse
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.prefs
+#
+# The preferences dialog.
+#
+# This module implements the preferences dialog, allowing the editing of the
+# configuration of the program. The preferences are grouped into tabs, each
+# containing the necessary controls to set the various options.
+
+#------------------------------------------------------------------------------
+
+class Hotkey(gtk.HBox):
+    """A widget to handle a hotkey."""
+
+    # Constant to denote that the status of the Ctrl modifier is changed
+    CHANGED_CTRL = 1
+
+    # Constant to denote that the status of the Shift modifier is changed
+    CHANGED_SHIFT = 2
+
+    # Constant to denote that the value of the key is changed
+    CHANGED_KEY = 3
+
+    def __init__(self, labelText, tooltips):
+        """Construct the hotkey widget.
+
+        labelText is the text for the label before the hotkey.
+
+        The tooltips parameter is an array of the tooltips for:
+        - the hotkey combo box,
+        - the control check box, and
+        - the shift check box."""
+        super(Hotkey, self).__init__()
+
+        label = gtk.Label(labelText)
+        label.set_use_underline(True)
+        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        labelAlignment.set_padding(padding_top = 0, padding_bottom = 0,
+                                   padding_left = 0, padding_right = 4)
+        labelAlignment.add(label)
+        self.pack_start(labelAlignment, False, False, 0)
+
+        self._ctrl = gtk.CheckButton("Ctrl")
+        self._ctrl.set_tooltip_text(tooltips[1])
+        self._ctrl.connect("toggled", self._ctrlToggled)
+        self.pack_start(self._ctrl, False, False, 4)
+
+        self._shift = gtk.CheckButton("Shift")
+        self._shift.set_tooltip_text(tooltips[2])
+        self._shift.connect("toggled", self._shiftToggled)
+        self.pack_start(self._shift, False, False, 4)
+
+        self._hotkeyModel = gtk.ListStore(str)
+        for keyCode in range(ord("0"), ord("9")+1) + range(ord("A"), ord("Z")+1):
+            self._hotkeyModel.append([chr(keyCode)])
+
+        self._hotkey = gtk.ComboBox(model = self._hotkeyModel)
+        cell = gtk.CellRendererText()
+        self._hotkey.pack_start(cell, True)
+        self._hotkey.add_attribute(cell, 'text', 0)
+        self._hotkey.set_tooltip_text(tooltips[0])
+        self._hotkey.connect("changed", self._keyChanged)
+        self.pack_start(self._hotkey, False, False, 4)
+
+        label.set_mnemonic_widget(self._hotkey)
+
+        self._setting = False
+
+    @property
+    def ctrl(self):
+        """Get whether the Ctrl modifier is selected."""
+        return self._ctrl.get_active()
+
+    @ctrl.setter
+    def ctrl(self, ctrl):
+        """Get whether the Ctrl modifier is selected."""
+        self._setting = True
+        self._ctrl.set_active(ctrl)
+        self._setting = False
+
+    @property
+    def shift(self):
+        """Get whether the Shift modifier is selected."""
+        return self._shift.get_active()
+
+    @shift.setter
+    def shift(self, shift):
+        """Get whether the Shift modifier is selected."""
+        self._setting = True
+        self._shift.set_active(shift)
+        self._setting = False
+
+    @property
+    def key(self):
+        """Get the value of the key."""
+        return self._hotkeyModel.get_value(self._hotkey.get_active_iter(), 0)
+
+    @key.setter
+    def key(self, key):
+        """Set the value of the key."""
+        self._setting = True
+
+        hotkeyModel = self._hotkeyModel
+        iter = hotkeyModel.get_iter_first()
+        while iter is not None and \
+              hotkeyModel.get_value(iter, 0)!=key:
+            iter = hotkeyModel.iter_next(iter)
+
+        if iter is None:
+            iter = hotkeyModel.get_iter_first()
+
+        self._hotkey.set_active_iter(iter)
+
+        self._setting = False
+
+    def set(self, hotkey):
+        """Set the hotkey widget from the given hotkey."""
+        self.ctrl = hotkey.ctrl
+        self.shift = hotkey.shift
+        self.key = hotkey.key
+
+    def get(self):
+        """Get a hotkey corresponding to the settings in the widghet."""
+
+        key = self._hotkeyModel.get_value(self._hotkey.get_active_iter(), 0)
+
+        return config.Hotkey(ctrl = self.ctrl, shift = self.shift,
+                             key = self.key)
+
+    def _ctrlToggled(self, checkButton):
+        """Called when the status of the Ctrl modifier has changed."""
+        if not self._setting:
+            self.emit("hotkey-changed", Hotkey.CHANGED_CTRL)
+
+    def _shiftToggled(self, checkButton):
+        """Called when the status of the Shift modifier has changed."""
+        if not self._setting:
+            self.emit("hotkey-changed", Hotkey.CHANGED_SHIFT)
+
+    def _keyChanged(self, comboBox):
+        """Called when the value of the key has changed."""
+        if not self._setting:
+            self.emit("hotkey-changed", Hotkey.CHANGED_KEY)
+
+    def __eq__(self, other):
+        """Determine if the two hotkeys are equal."""
+        return self.ctrl==other.ctrl and self.shift==other.shift and \
+               self.key==other.key
+
+#------------------------------------------------------------------------------
+
+gobject.signal_new("hotkey-changed", Hotkey, gobject.SIGNAL_RUN_FIRST,
+                   None, (int,))
+
+#------------------------------------------------------------------------------
+
+class Preferences(gtk.Dialog):
+    """The preferences dialog."""
+    def __init__(self, gui):
+        """Construct the dialog."""
+        super(Preferences, self).__init__(WINDOW_TITLE_BASE + " " +
+                                          xstr("prefs_title"),
+                                          gui.mainWindow,
+                                          DIALOG_MODAL)
+
+        self.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
+        self.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
+        self.set_resizable(False)
+
+        self._gui = gui
+        self._settingFromConfig = False
+
+        contentArea = self.get_content_area()
+
+        notebook = gtk.Notebook()
+        contentArea.pack_start(notebook, True, True, 4)
+
+        general = self._buildGeneral()
+        label = gtk.Label(xstr("prefs_tab_general"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("prefs_tab_general_tooltip"))
+        notebook.append_page(general, label)
+
+        messages = self._buildMessages()
+        label = gtk.Label(xstr("prefs_tab_messages"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("prefs_tab_message_tooltip"))
+        notebook.append_page(messages, label)
+
+        sounds = self._buildSounds()
+        label = gtk.Label(xstr("prefs_tab_sounds"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("prefs_tab_sounds_tooltip"))
+        notebook.append_page(sounds, label)
+
+        advanced = self._buildAdvanced()
+        label = gtk.Label(xstr("prefs_tab_advanced"))
+        label.set_use_underline(True)
+        label.set_tooltip_text(xstr("prefs_tab_advanced_tooltip"))
+        notebook.append_page(advanced, label)
+
+    def run(self, config):
+        """Run the preferences dialog.
+
+        The dialog will be set up from data in the given configuration. If the
+        changes are accepted by the user, the configuration is updated and saved."""
+        self._fromConfig(config)
+
+        self.show_all()
+        response = super(Preferences, self).run()
+        self.hide()
+
+        if response==RESPONSETYPE_ACCEPT:
+            self._toConfig(config)
+            config.save()
+
+    def _fromConfig(self, config):
+        """Setup the dialog from the given configuration."""
+        self._settingFromConfig = True
+
+        self._setLanguage(config.language)
+        self._hideMinimizedWindow.set_active(config.hideMinimizedWindow)
+        self._quitOnClose.set_active(config.quitOnClose)
+        self._onlineGateSystem.set_active(config.onlineGateSystem)
+        self._onlineACARS.set_active(config.onlineACARS)
+        self._flareTimeFromFS.set_active(config.flareTimeFromFS)
+        self._syncFSTime.set_active(config.syncFSTime)
+        self._usingFS2Crew.set_active(config.usingFS2Crew)
+
+        self._setSmoothing(self._iasSmoothingEnabled, self._iasSmoothingLength,
+                           config.iasSmoothingLength)
+        self._setSmoothing(self._vsSmoothingEnabled, self._vsSmoothingLength,
+                           config.vsSmoothingLength)
+
+        self._useSimBrief.set_active(config.useSimBrief)
+
+        pirepDirectory = config.pirepDirectory
+        self._pirepDirectory.set_text("" if pirepDirectory is None
+                                      else pirepDirectory)
+        self._pirepAutoSave.set_active(config.pirepAutoSave)
+        if not pirepDirectory:
+            self._pirepAutoSave.set_sensitive(False)
+
+        for messageType in const.messageTypes:
+            level = config.getMessageTypeLevel(messageType)
+            button = self._msgFSCheckButtons[messageType]
+            button.set_active(level == const.MESSAGELEVEL_FS or
+                              level == const.MESSAGELEVEL_BOTH)
+            button = self._msgSoundCheckButtons[messageType]
+            button.set_active(level == const.MESSAGELEVEL_SOUND or
+                              level == const.MESSAGELEVEL_BOTH)
+
+        self._enableSounds.set_active(config.enableSounds)
+        self._pilotControlsSounds.set_active(config.pilotControlsSounds)
+        self._pilotHotkey.set(config.pilotHotkey)
+        self._enableApproachCallouts.set_active(config.enableApproachCallouts)
+        self._speedbrakeAtTD.set_active(config.speedbrakeAtTD)
+
+        self._enableChecklists.set_active(config.enableChecklists)
+        self._checklistHotkey.set(config.checklistHotkey)
+
+        self._autoUpdate.set_active(config.autoUpdate)
+        if not config.autoUpdate:
+            self._warnedAutoUpdate = True
+
+        self._updateURL.set_text(config.updateURL)
+
+        self._useRPC.set_active(config.useRPC)
+
+        self._settingFromConfig = False
+
+    def _toConfig(self, config):
+        """Setup the given config from the settings in the dialog."""
+        config.language = self._getLanguage()
+        config.hideMinimizedWindow = self._hideMinimizedWindow.get_active()
+        config.quitOnClose = self._quitOnClose.get_active()
+        config.onlineGateSystem = self._onlineGateSystem.get_active()
+        config.onlineACARS = self._onlineACARS.get_active()
+        config.flareTimeFromFS = self._flareTimeFromFS.get_active()
+        config.syncFSTime = self._syncFSTime.get_active()
+        config.usingFS2Crew = self._usingFS2Crew.get_active()
+        config.iasSmoothingLength = self._getSmoothing(self._iasSmoothingEnabled,
+                                                       self._iasSmoothingLength)
+        config.vsSmoothingLength = self._getSmoothing(self._vsSmoothingEnabled,
+                                                       self._vsSmoothingLength)
+        config.useSimBrief = self._useSimBrief.get_active()
+        config.pirepDirectory = text2unicode(self._pirepDirectory.get_text())
+        config.pirepAutoSave = self._pirepAutoSave.get_active()
+
+        for messageType in const.messageTypes:
+            fsButtonActive = self._msgFSCheckButtons[messageType].get_active()
+            soundButtonActive = self._msgSoundCheckButtons[messageType].get_active()
+            if fsButtonActive:
+                level = const.MESSAGELEVEL_BOTH if soundButtonActive \
+                        else const.MESSAGELEVEL_FS
+            elif soundButtonActive:
+                level = const.MESSAGELEVEL_SOUND
+            else:
+                level = const.MESSAGELEVEL_NONE
+            config.setMessageTypeLevel(messageType, level)
+
+        config.enableSounds = self._enableSounds.get_active()
+        config.pilotControlsSounds = self._pilotControlsSounds.get_active()
+        config.pilotHotkey = self._pilotHotkey.get()
+        config.enableApproachCallouts = self._enableApproachCallouts.get_active()
+        config.speedbrakeAtTD = self._speedbrakeAtTD.get_active()
+
+        config.enableChecklists = self._enableChecklists.get_active()
+        config.checklistHotkey = self._checklistHotkey.get()
+
+        config.autoUpdate = self._autoUpdate.get_active()
+        config.updateURL = self._updateURL.get_text()
+        config.useRPC = self._useRPC.get_active()
+
+    def _buildGeneral(self):
+        """Build the page for the general settings."""
+        mainAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                      xscale = 1.0, yscale = 0.0)
+        mainAlignment.set_padding(padding_top = 0, padding_bottom = 8,
+                                  padding_left = 4, padding_right = 4)
+        mainBox = gtk.VBox()
+        mainAlignment.add(mainBox)
+
+        guiBox = self._createFrame(mainBox, xstr("prefs_frame_gui"))
+
+        languageBox = gtk.HBox()
+        guiBox.pack_start(languageBox, False, False, 4)
+
+        label = gtk.Label(xstr("prefs_language"))
+        label.set_use_underline(True)
+
+        languageBox.pack_start(label, False, False, 4)
+
+        self._languageList = gtk.ListStore(str, str)
+        for language in const.languages:
+            self._languageList.append([xstr("prefs_lang_" + language),
+                                       language])
+
+        self._languageComboBox = languageComboBox = \
+            gtk.ComboBox(model = self._languageList)
+        cell = gtk.CellRendererText()
+        languageComboBox.pack_start(cell, True)
+        languageComboBox.add_attribute(cell, 'text', 0)
+        languageComboBox.set_tooltip_text(xstr("prefs_language_tooltip"))
+        languageComboBox.connect("changed", self._languageChanged)
+        languageBox.pack_start(languageComboBox, False, False, 4)
+
+        label.set_mnemonic_widget(languageComboBox)
+
+        self._changingLanguage = False
+        self._warnedRestartNeeded = False
+
+        self._hideMinimizedWindow = gtk.CheckButton(xstr("prefs_hideMinimizedWindow"))
+        self._hideMinimizedWindow.set_use_underline(True)
+        self._hideMinimizedWindow.set_tooltip_text(xstr("prefs_hideMinimizedWindow_tooltip"))
+        guiBox.pack_start(self._hideMinimizedWindow, False, False, 4)
+
+        self._quitOnClose = gtk.CheckButton(xstr("prefs_quitOnClose"))
+        self._quitOnClose.set_use_underline(True)
+        self._quitOnClose.set_tooltip_text(xstr("prefs_quitOnClose_tooltip"))
+        guiBox.pack_start(self._quitOnClose, False, False, 4)
+
+        onlineBox = self._createFrame(mainBox, xstr("prefs_frame_online"))
+
+        self._onlineGateSystem = gtk.CheckButton(xstr("prefs_onlineGateSystem"))
+        self._onlineGateSystem.set_use_underline(True)
+        self._onlineGateSystem.set_tooltip_text(xstr("prefs_onlineGateSystem_tooltip"))
+        onlineBox.pack_start(self._onlineGateSystem, False, False, 4)
+
+        self._onlineACARS = gtk.CheckButton(xstr("prefs_onlineACARS"))
+        self._onlineACARS.set_use_underline(True)
+        self._onlineACARS.set_tooltip_text(xstr("prefs_onlineACARS_tooltip"))
+        onlineBox.pack_start(self._onlineACARS, False, False, 4)
+
+        simulatorBox = self._createFrame(mainBox, xstr("prefs_frame_simulator"))
+
+        self._flareTimeFromFS = gtk.CheckButton(xstr("prefs_flaretimeFromFS"))
+        self._flareTimeFromFS.set_use_underline(True)
+        self._flareTimeFromFS.set_tooltip_text(xstr("prefs_flaretimeFromFS_tooltip"))
+        simulatorBox.pack_start(self._flareTimeFromFS, False, False, 4)
+
+        self._syncFSTime = gtk.CheckButton(xstr("prefs_syncFSTime"))
+        self._syncFSTime.set_use_underline(True)
+        self._syncFSTime.set_tooltip_text(xstr("prefs_syncFSTime_tooltip"))
+        simulatorBox.pack_start(self._syncFSTime, False, False, 4)
+
+        self._usingFS2Crew = gtk.CheckButton(xstr("prefs_usingFS2Crew"))
+        self._usingFS2Crew.set_use_underline(True)
+        self._usingFS2Crew.set_tooltip_text(xstr("prefs_usingFS2Crew_tooltip"))
+        simulatorBox.pack_start(self._usingFS2Crew, False, False, 4)
+
+        (iasSmoothingBox, self._iasSmoothingEnabled,
+         self._iasSmoothingLength) = \
+           self._createSmoothingBox(xstr("prefs_iasSmoothingEnabled"),
+                                    xstr("prefs_iasSmoothingEnabledTooltip"))
+        simulatorBox.pack_start(iasSmoothingBox, False, False, 4)
+
+        (vsSmoothingBox, self._vsSmoothingEnabled,
+         self._vsSmoothingLength) = \
+           self._createSmoothingBox(xstr("prefs_vsSmoothingEnabled"),
+                                    xstr("prefs_vsSmoothingEnabledTooltip"))
+        simulatorBox.pack_start(vsSmoothingBox, False, False, 4)
+
+        self._useSimBrief = gtk.CheckButton(xstr("prefs_useSimBrief"))
+        self._useSimBrief.set_use_underline(True)
+        self._useSimBrief.set_tooltip_text(xstr("prefs_useSimBrief_tooltip"))
+        mainBox.pack_start(self._useSimBrief, False, False, 0)
+
+        pirepBox = gtk.HBox()
+        mainBox.pack_start(pirepBox, False, False, 8)
+
+        label = gtk.Label(xstr("prefs_pirepDirectory"))
+        label.set_use_underline(True)
+        pirepBox.pack_start(label, False, False, 4)
+
+        self._pirepDirectory = gtk.Entry()
+        self._pirepDirectory.set_tooltip_text(xstr("prefs_pirepDirectory_tooltip"))
+        self._pirepDirectory.connect("changed", self._pirepDirectoryChanged)
+        label.set_mnemonic_widget(self._pirepDirectory)
+        pirepBox.pack_start(self._pirepDirectory, True, True, 4)
+
+        self._pirepDirectoryButton = gtk.Button(xstr("button_browse"))
+        self._pirepDirectoryButton.connect("clicked",
+                                           self._pirepDirectoryButtonClicked)
+        pirepBox.pack_start(self._pirepDirectoryButton, False, False, 4)
+
+        self._pirepAutoSave = gtk.CheckButton(xstr("prefs_pirepAutoSave"))
+        self._pirepAutoSave.set_use_underline(True)
+        self._pirepAutoSave.set_tooltip_text(xstr("prefs_pirepAutoSave_tooltip"))
+        mainBox.pack_start(self._pirepAutoSave, False, False, 0)
+
+        return mainAlignment
+
+    def _createFrame(self, mainBox, label):
+        """Create a frame with an inner alignment and VBox.
+
+        Return the vbox."""
+        frame = gtk.Frame(label = label)
+        mainBox.pack_start(frame, False, False, 4)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 1.0, yscale = 0.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 0,
+                              padding_left = 0, padding_right = 0)
+        frame.add(alignment)
+        vbox = gtk.VBox()
+        alignment.add(vbox)
+
+        return vbox
+
+    def _createSmoothingBox(self, checkBoxLabel, checkBoxTooltip,
+                            maxSeconds = 10):
+        """Create a HBox that contains entry fields for smoothing some value."""
+        smoothingBox = gtk.HBox()
+
+        smoothingEnabled = gtk.CheckButton(checkBoxLabel)
+        smoothingEnabled.set_use_underline(True)
+        smoothingEnabled.set_tooltip_text(checkBoxTooltip)
+
+        smoothingBox.pack_start(smoothingEnabled, False, False, 0)
+
+        smoothingLength = gtk.SpinButton()
+        smoothingLength.set_numeric(True)
+        smoothingLength.set_range(2, maxSeconds)
+        smoothingLength.set_increments(1, 1)
+        smoothingLength.set_alignment(1.0)
+        smoothingLength.set_width_chars(2)
+
+        smoothingBox.pack_start(smoothingLength, False, False, 0)
+
+        smoothingBox.pack_start(gtk.Label(xstr("prefs_smoothing_seconds")),
+                                False, False, 4)
+
+        smoothingEnabled.connect("toggled", self._smoothingToggled,
+                                 smoothingLength)
+        smoothingLength.set_sensitive(False)
+
+        return (smoothingBox, smoothingEnabled, smoothingLength)
+
+    def _setLanguage(self, language):
+        """Set the language to the given one."""
+        iter = self._languageList.get_iter_first()
+        while iter is not None:
+            (lang,) = self._languageList.get(iter, 1)
+            if (not language and lang=="$system") or \
+               lang==language:
+                self._changingLanguage = True
+                self._languageComboBox.set_active_iter(iter)
+                self._changingLanguage = False
+                break
+            else:
+                iter = self._languageList.iter_next(iter)
+
+    def _getLanguage(self):
+        """Get the language selected by the user."""
+        iter = self._languageComboBox.get_active_iter()
+        (lang,) = self._languageList.get(iter, 1)
+        return "" if lang=="$system" else lang
+
+    def _languageChanged(self, comboBox):
+        """Called when the language has changed."""
+        if not self._changingLanguage and not self._warnedRestartNeeded:
+            dialog = gtk.MessageDialog(parent = self,
+                                       type = MESSAGETYPE_INFO,
+                                       message_format = xstr("prefs_restart"))
+            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+            dialog.set_title(self.get_title())
+            dialog.format_secondary_markup(xstr("prefs_language_restart_sec"))
+            dialog.run()
+            dialog.hide()
+            self._warnedRestartNeeded = True
+
+    def _smoothingToggled(self, smoothingEnabled, smoothingLength):
+        """Called when a smoothing enabled check box is toggled."""
+        sensitive = smoothingEnabled.get_active()
+        smoothingLength.set_sensitive(sensitive)
+        if sensitive:
+            smoothingLength.grab_focus()
+
+    def _setSmoothing(self, smoothingEnabled, smoothingLength, smoothing):
+        """Set the smoothing controls from the given value.
+
+        If the value is less than 2, smoothing is disabled. The smoothing
+        length is the absolute value of the value."""
+        smoothingEnabled.set_active(smoothing>=2)
+        smoothingLength.set_value(abs(smoothing))
+
+    def _getSmoothing(self, smoothingEnabled, smoothingLength):
+        """Get the smoothing value from the given controls.
+
+        The returned value is the value of smoothingLength multiplied by -1, if
+        smoothing is disabled."""
+        value = smoothingLength.get_value_as_int()
+        if not smoothingEnabled.get_active():
+            value *= -1
+        return value
+
+    def _pirepDirectoryButtonClicked(self, button):
+        """Called when the PIREP directory button is clicked."""
+        dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
+                                       xstr("prefs_pirepDirectory_browser_title"),
+                                       action = FILE_CHOOSER_ACTION_SELECT_FOLDER,
+                                       buttons = (gtk.STOCK_CANCEL, RESPONSETYPE_CANCEL,
+                                                  gtk.STOCK_OK, RESPONSETYPE_OK),
+                                       parent = self)
+        dialog.set_modal(True)
+        dialog.set_transient_for(self)
+
+        directory = self._pirepDirectory.get_text()
+        if directory:
+            dialog.select_filename(directory)
+
+        result = dialog.run()
+        dialog.hide()
+
+        if result==RESPONSETYPE_OK:
+            self._pirepDirectory.set_text(text2unicode(dialog.get_filename()))
+
+    def _pirepDirectoryChanged(self, entry):
+        """Called when the PIREP directory is changed."""
+        if self._pirepDirectory.get_text():
+            self._pirepAutoSave.set_sensitive(True)
+        else:
+            self._pirepAutoSave.set_active(False)
+            self._pirepAutoSave.set_sensitive(False)
+
+    def _buildMessages(self):
+        """Build the page for the message settings."""
+
+        mainAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                      xscale = 0.0, yscale = 0.0)
+        mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
+                                  padding_left = 4, padding_right = 4)
+        mainBox = gtk.VBox()
+        mainAlignment.add(mainBox)
+
+        table = gtk.Table(len(const.messageTypes) + 1, 3)
+        table.set_row_spacings(8)
+        table.set_col_spacings(32)
+        table.set_homogeneous(False)
+        mainBox.pack_start(table, False, False, 4)
+
+        label = gtk.Label(xstr("prefs_msgs_fs"))
+        label.set_justify(JUSTIFY_CENTER)
+        label.set_alignment(0.5, 1.0)
+        table.attach(label, 1, 2, 0, 1)
+
+        label = gtk.Label(xstr("prefs_msgs_sound"))
+        label.set_justify(JUSTIFY_CENTER)
+        label.set_alignment(0.5, 1.0)
+        table.attach(label, 2, 3, 0, 1)
+
+        self._msgFSCheckButtons = {}
+        self._msgSoundCheckButtons = {}
+        row = 1
+        for messageType in const.messageTypes:
+            messageTypeStr = const.messageType2string(messageType)
+            label = gtk.Label(xstr("prefs_msgs_type_" + messageTypeStr))
+            label.set_justify(JUSTIFY_CENTER)
+            label.set_use_underline(True)
+            label.set_alignment(0.5, 0.5)
+            table.attach(label, 0, 1, row, row+1)
+
+            fsCheckButton = gtk.CheckButton()
+            alignment = gtk.Alignment(xscale = 0.0, yscale = 0.0,
+                                      xalign = 0.5, yalign = 0.5)
+            alignment.add(fsCheckButton)
+            table.attach(alignment, 1, 2, row, row+1)
+            self._msgFSCheckButtons[messageType] = fsCheckButton
+
+            soundCheckButton = gtk.CheckButton()
+            alignment = gtk.Alignment(xscale = 0.0, yscale = 0.0,
+                                      xalign = 0.5, yalign = 0.5)
+            alignment.add(soundCheckButton)
+            table.attach(alignment, 2, 3, row, row+1)
+            self._msgSoundCheckButtons[messageType] = soundCheckButton
+
+            mnemonicWidget = gtk.Label("")
+            table.attach(mnemonicWidget, 3, 4, row, row+1)
+            label.set_mnemonic_widget(mnemonicWidget)
+            mnemonicWidget.connect("mnemonic-activate",
+                                   self._msgLabelActivated,
+                                   messageType)
+
+            row += 1
+
+        return mainAlignment
+
+    def _msgLabelActivated(self, button, cycle_group, messageType):
+        """Called when the mnemonic of a label is activated.
+
+        It cycles the corresponding options."""
+        fsCheckButton = self._msgFSCheckButtons[messageType]
+        soundCheckButton = self._msgSoundCheckButtons[messageType]
+
+        num = 1 if fsCheckButton.get_active() else 0
+        num += 2 if soundCheckButton.get_active() else 0
+        num += 1
+
+        fsCheckButton.set_active((num&0x01)==0x01)
+        soundCheckButton.set_active((num&0x02)==0x02)
+
+        return True
+
+    def _buildSounds(self):
+        """Build the page for the sounds."""
+        mainAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                      xscale = 1.0, yscale = 1.0)
+        mainAlignment.set_padding(padding_top = 8, padding_bottom = 8,
+                                  padding_left = 4, padding_right = 4)
+
+        mainBox = gtk.VBox()
+        mainAlignment.add(mainBox)
+
+        backgroundFrame = gtk.Frame(label = xstr("prefs_sounds_frame_bg"))
+        mainBox.pack_start(backgroundFrame, False, False, 4)
+
+        backgroundAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                            xscale = 1.0, yscale = 0.0)
+        backgroundAlignment.set_padding(padding_top = 4, padding_bottom = 4,
+                                        padding_left = 4, padding_right = 4)
+        backgroundFrame.add(backgroundAlignment)
+
+        backgroundBox = gtk.VBox()
+        backgroundAlignment.add(backgroundBox)
+
+        self._enableSounds = gtk.CheckButton(xstr("prefs_sounds_enable"))
+        self._enableSounds.set_use_underline(True)
+        self._enableSounds.set_tooltip_text(xstr("prefs_sounds_enable_tooltip"))
+        self._enableSounds.connect("toggled", self._enableSoundsToggled)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 1.0, yscale = 0.0)
+        alignment.add(self._enableSounds)
+        backgroundBox.pack_start(alignment, False, False, 4)
+
+        self._pilotControlsSounds = gtk.CheckButton(xstr("prefs_sounds_pilotControls"))
+        self._pilotControlsSounds.set_use_underline(True)
+        self._pilotControlsSounds.set_tooltip_text(xstr("prefs_sounds_pilotControls_tooltip"))
+        self._pilotControlsSounds.connect("toggled", self._pilotControlsSoundsToggled)
+        backgroundBox.pack_start(self._pilotControlsSounds, False, False, 4)
+
+        self._pilotHotkey = Hotkey(xstr("prefs_sounds_pilotHotkey"),
+                                   [xstr("prefs_sounds_pilotHotkey_tooltip"),
+                                    xstr("prefs_sounds_pilotHotkeyCtrl_tooltip"),
+                                    xstr("prefs_sounds_pilotHotkeyShift_tooltip")])
+
+        backgroundBox.pack_start(self._pilotHotkey, False, False, 4)
+
+        self._enableApproachCallouts = gtk.CheckButton(xstr("prefs_sounds_approachCallouts"))
+        self._enableApproachCallouts.set_use_underline(True)
+        self._enableApproachCallouts.set_tooltip_text(xstr("prefs_sounds_approachCallouts_tooltip"))
+        backgroundBox.pack_start(self._enableApproachCallouts, False, False, 4)
+
+        self._speedbrakeAtTD = gtk.CheckButton(xstr("prefs_sounds_speedbrakeAtTD"))
+        self._speedbrakeAtTD.set_use_underline(True)
+        self._speedbrakeAtTD.set_tooltip_text(xstr("prefs_sounds_speedbrakeAtTD_tooltip"))
+        backgroundBox.pack_start(self._speedbrakeAtTD, False, False, 4)
+
+        checklistFrame = gtk.Frame(label = xstr("prefs_sounds_frame_checklists"))
+        mainBox.pack_start(checklistFrame, False, False, 4)
+
+        checklistAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                           xscale = 1.0, yscale = 0.0)
+        checklistAlignment.set_padding(padding_top = 4, padding_bottom = 4,
+                                       padding_left = 4, padding_right = 4)
+        checklistFrame.add(checklistAlignment)
+
+        checklistBox = gtk.VBox()
+        checklistAlignment.add(checklistBox)
+
+        self._enableChecklists = gtk.CheckButton(xstr("prefs_sounds_enableChecklists"))
+        self._enableChecklists.set_use_underline(True)
+        self._enableChecklists.set_tooltip_text(xstr("prefs_sounds_enableChecklists_tooltip"))
+        self._enableChecklists.connect("toggled", self._enableChecklistsToggled)
+        checklistBox.pack_start(self._enableChecklists, False, False, 4)
+
+        self._checklistHotkey = Hotkey(xstr("prefs_sounds_checklistHotkey"),
+                                       [xstr("prefs_sounds_checklistHotkey_tooltip"),
+                                        xstr("prefs_sounds_checklistHotkeyCtrl_tooltip"),
+                                        xstr("prefs_sounds_checklistHotkeyShift_tooltip")])
+
+        checklistBox.pack_start(self._checklistHotkey, False, False, 4)
+
+        self._enableSoundsToggled(self._enableSounds)
+        self._enableChecklistsToggled(self._enableChecklists)
+
+        self._pilotHotkey.connect("hotkey-changed", self._reconcileHotkeys,
+                                  self._checklistHotkey)
+        self._checklistHotkey.connect("hotkey-changed", self._reconcileHotkeys,
+                                      self._pilotHotkey)
+
+        return mainAlignment
+
+    def _enableSoundsToggled(self, button):
+        """Called when the enable sounds button is toggled."""
+        active = button.get_active()
+        self._pilotControlsSounds.set_sensitive(active)
+        self._pilotControlsSoundsToggled(self._pilotControlsSounds)
+        self._enableApproachCallouts.set_sensitive(active)
+        self._speedbrakeAtTD.set_sensitive(active)
+
+    def _pilotControlsSoundsToggled(self, button):
+        """Called when the enable sounds button is toggled."""
+        active = button.get_active() and self._enableSounds.get_active()
+        self._pilotHotkey.set_sensitive(active)
+        if active and self._checklistHotkey.get_sensitive():
+            self._reconcileHotkeys(self._checklistHotkey, Hotkey.CHANGED_SHIFT,
+                                   self._pilotHotkey)
+
+    def _enableChecklistsToggled(self, button):
+        """Called when the enable checklists button is toggled."""
+        active = button.get_active()
+        self._checklistHotkey.set_sensitive(active)
+        if active and self._pilotHotkey.get_sensitive():
+            self._reconcileHotkeys(self._pilotHotkey, Hotkey.CHANGED_SHIFT,
+                                   self._checklistHotkey)
+
+    def _reconcileHotkeys(self, changedHotkey, what, otherHotkey):
+        """Reconcile the given hotkeys so that they are different.
+
+        changedHotkey is the hotkey that has changed. what is one of the
+        Hotkey.CHANGED_XXX constants denoting what has changed. otherHotkey is
+        the other hotkey that must be reconciled.
+
+        If the other hotkey is not sensitive or is not equal to the changed
+        one, nothing happens.
+
+        Otherwise, if the status of the Ctrl modifier has changed, the status
+        of the Ctrl modifier on the other hotkey will be negated. Similarly, if
+        the Shift modifier has changed. If the key has changed, the Shift
+        modifier is negated in the other hotkey."""
+        if otherHotkey.get_sensitive() and changedHotkey==otherHotkey:
+            if what==Hotkey.CHANGED_CTRL:
+                otherHotkey.ctrl = not changedHotkey.ctrl
+            elif what==Hotkey.CHANGED_SHIFT or what==Hotkey.CHANGED_KEY:
+                otherHotkey.shift = not changedHotkey.shift
+
+    def _buildAdvanced(self):
+        """Build the page for the advanced settings."""
+
+        mainAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                      xscale = 1.0, yscale = 0.0)
+        mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
+                                  padding_left = 4, padding_right = 4)
+        mainBox = gtk.VBox()
+        mainAlignment.add(mainBox)
+
+        self._autoUpdate = gtk.CheckButton(xstr("prefs_update_auto"))
+        mainBox.pack_start(self._autoUpdate, False, False, 4)
+
+        self._autoUpdate.set_use_underline(True)
+        self._autoUpdate.connect("toggled", self._autoUpdateToggled)
+        self._autoUpdate.set_tooltip_text(xstr("prefs_update_auto_tooltip"))
+        self._warnedAutoUpdate = False
+
+        updateURLBox = gtk.HBox()
+        mainBox.pack_start(updateURLBox, False, False, 4)
+        label = gtk.Label(xstr("prefs_update_url"))
+        label.set_use_underline(True)
+        updateURLBox.pack_start(label, False, False, 4)
+
+        self._updateURL = gtk.Entry()
+        label.set_mnemonic_widget(self._updateURL)
+        self._updateURL.set_width_chars(40)
+        self._updateURL.set_tooltip_text(xstr("prefs_update_url_tooltip"))
+        self._updateURL.connect("changed", self._updateURLChanged)
+        updateURLBox.pack_start(self._updateURL, True, True, 4)
+
+        self._useRPC = gtk.CheckButton(xstr("prefs_use_rpc"))
+        mainBox.pack_start(self._useRPC, False, False, 16)
+
+        self._useRPC.set_use_underline(True)
+        self._useRPC.set_tooltip_text(xstr("prefs_use_rpc_tooltip"))
+
+        return mainAlignment
+
+    def _setOKButtonSensitivity(self):
+        """Set the sensitive state of the OK button."""
+        sensitive = False
+        try:
+            result = urlparse.urlparse(self._updateURL.get_text())
+            sensitive = result.scheme!="" and (result.netloc + result.path)!=""
+        except:
+            pass
+
+        okButton = self.get_widget_for_response(RESPONSETYPE_ACCEPT)
+        okButton.set_sensitive(sensitive)
+
+    def _autoUpdateToggled(self, button):
+        """Called when the auto update check button is toggled."""
+        if not self._settingFromConfig and not self._warnedAutoUpdate and \
+           not self._autoUpdate.get_active():
+            dialog = gtk.MessageDialog(parent = self,
+                                       type = MESSAGETYPE_INFO,
+                                       message_format = xstr("prefs_update_auto_warning"))
+            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+            dialog.set_title(self.get_title())
+            dialog.run()
+            dialog.hide()
+            self._warnedAutoUpdate = True
+
+    def _updateURLChanged(self, entry):
+        """Called when the update URL is changed."""
+        self._setOKButtonSensitivity()
+
Index: src/mlx/gui/prefs.py
===================================================================
--- src/mlx/gui/prefs.py	(revision 738)
+++ 	(revision )
@@ -1,855 +1,0 @@
-
-from common import *
-
-from mlx.i18n import xstr
-import mlx.const as const
-import mlx.config as config
-
-import urlparse
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.prefs
-#
-# The preferences dialog.
-#
-# This module implements the preferences dialog, allowing the editing of the
-# configuration of the program. The preferences are grouped into tabs, each
-# containing the necessary controls to set the various options.
-
-#------------------------------------------------------------------------------
-
-class Hotkey(gtk.HBox):
-    """A widget to handle a hotkey."""
-
-    # Constant to denote that the status of the Ctrl modifier is changed
-    CHANGED_CTRL = 1
-
-    # Constant to denote that the status of the Shift modifier is changed
-    CHANGED_SHIFT = 2
-
-    # Constant to denote that the value of the key is changed
-    CHANGED_KEY = 3
-
-    def __init__(self, labelText, tooltips):
-        """Construct the hotkey widget.
-
-        labelText is the text for the label before the hotkey.
-
-        The tooltips parameter is an array of the tooltips for:
-        - the hotkey combo box,
-        - the control check box, and
-        - the shift check box."""
-        super(Hotkey, self).__init__()
-
-        label = gtk.Label(labelText)
-        label.set_use_underline(True)
-        labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                       xscale = 0.0, yscale = 0.0)
-        labelAlignment.set_padding(padding_top = 0, padding_bottom = 0,
-                                   padding_left = 0, padding_right = 4)
-        labelAlignment.add(label)
-        self.pack_start(labelAlignment, False, False, 0)
-
-        self._ctrl = gtk.CheckButton("Ctrl")
-        self._ctrl.set_tooltip_text(tooltips[1])
-        self._ctrl.connect("toggled", self._ctrlToggled)
-        self.pack_start(self._ctrl, False, False, 4)
-
-        self._shift = gtk.CheckButton("Shift")
-        self._shift.set_tooltip_text(tooltips[2])
-        self._shift.connect("toggled", self._shiftToggled)
-        self.pack_start(self._shift, False, False, 4)
-
-        self._hotkeyModel = gtk.ListStore(str)
-        for keyCode in range(ord("0"), ord("9")+1) + range(ord("A"), ord("Z")+1):
-            self._hotkeyModel.append([chr(keyCode)])
-
-        self._hotkey = gtk.ComboBox(model = self._hotkeyModel)
-        cell = gtk.CellRendererText()
-        self._hotkey.pack_start(cell, True)
-        self._hotkey.add_attribute(cell, 'text', 0)
-        self._hotkey.set_tooltip_text(tooltips[0])
-        self._hotkey.connect("changed", self._keyChanged)
-        self.pack_start(self._hotkey, False, False, 4)
-
-        label.set_mnemonic_widget(self._hotkey)
-
-        self._setting = False
-
-    @property
-    def ctrl(self):
-        """Get whether the Ctrl modifier is selected."""
-        return self._ctrl.get_active()
-
-    @ctrl.setter
-    def ctrl(self, ctrl):
-        """Get whether the Ctrl modifier is selected."""
-        self._setting = True
-        self._ctrl.set_active(ctrl)
-        self._setting = False
-
-    @property
-    def shift(self):
-        """Get whether the Shift modifier is selected."""
-        return self._shift.get_active()
-
-    @shift.setter
-    def shift(self, shift):
-        """Get whether the Shift modifier is selected."""
-        self._setting = True
-        self._shift.set_active(shift)
-        self._setting = False
-
-    @property
-    def key(self):
-        """Get the value of the key."""
-        return self._hotkeyModel.get_value(self._hotkey.get_active_iter(), 0)
-
-    @key.setter
-    def key(self, key):
-        """Set the value of the key."""
-        self._setting = True
-
-        hotkeyModel = self._hotkeyModel
-        iter = hotkeyModel.get_iter_first()
-        while iter is not None and \
-              hotkeyModel.get_value(iter, 0)!=key:
-            iter = hotkeyModel.iter_next(iter)
-
-        if iter is None:
-            iter = hotkeyModel.get_iter_first()
-
-        self._hotkey.set_active_iter(iter)
-
-        self._setting = False
-
-    def set(self, hotkey):
-        """Set the hotkey widget from the given hotkey."""
-        self.ctrl = hotkey.ctrl
-        self.shift = hotkey.shift
-        self.key = hotkey.key
-
-    def get(self):
-        """Get a hotkey corresponding to the settings in the widghet."""
-
-        key = self._hotkeyModel.get_value(self._hotkey.get_active_iter(), 0)
-
-        return config.Hotkey(ctrl = self.ctrl, shift = self.shift,
-                             key = self.key)
-
-    def _ctrlToggled(self, checkButton):
-        """Called when the status of the Ctrl modifier has changed."""
-        if not self._setting:
-            self.emit("hotkey-changed", Hotkey.CHANGED_CTRL)
-
-    def _shiftToggled(self, checkButton):
-        """Called when the status of the Shift modifier has changed."""
-        if not self._setting:
-            self.emit("hotkey-changed", Hotkey.CHANGED_SHIFT)
-
-    def _keyChanged(self, comboBox):
-        """Called when the value of the key has changed."""
-        if not self._setting:
-            self.emit("hotkey-changed", Hotkey.CHANGED_KEY)
-
-    def __eq__(self, other):
-        """Determine if the two hotkeys are equal."""
-        return self.ctrl==other.ctrl and self.shift==other.shift and \
-               self.key==other.key
-
-#------------------------------------------------------------------------------
-
-gobject.signal_new("hotkey-changed", Hotkey, gobject.SIGNAL_RUN_FIRST,
-                   None, (int,))
-
-#------------------------------------------------------------------------------
-
-class Preferences(gtk.Dialog):
-    """The preferences dialog."""
-    def __init__(self, gui):
-        """Construct the dialog."""
-        super(Preferences, self).__init__(WINDOW_TITLE_BASE + " " +
-                                          xstr("prefs_title"),
-                                          gui.mainWindow,
-                                          DIALOG_MODAL)
-
-        self.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
-        self.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
-        self.set_resizable(False)
-
-        self._gui = gui
-        self._settingFromConfig = False
-
-        contentArea = self.get_content_area()
-
-        notebook = gtk.Notebook()
-        contentArea.pack_start(notebook, True, True, 4)
-
-        general = self._buildGeneral()
-        label = gtk.Label(xstr("prefs_tab_general"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("prefs_tab_general_tooltip"))
-        notebook.append_page(general, label)
-
-        messages = self._buildMessages()
-        label = gtk.Label(xstr("prefs_tab_messages"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("prefs_tab_message_tooltip"))
-        notebook.append_page(messages, label)
-
-        sounds = self._buildSounds()
-        label = gtk.Label(xstr("prefs_tab_sounds"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("prefs_tab_sounds_tooltip"))
-        notebook.append_page(sounds, label)
-
-        advanced = self._buildAdvanced()
-        label = gtk.Label(xstr("prefs_tab_advanced"))
-        label.set_use_underline(True)
-        label.set_tooltip_text(xstr("prefs_tab_advanced_tooltip"))
-        notebook.append_page(advanced, label)
-
-    def run(self, config):
-        """Run the preferences dialog.
-
-        The dialog will be set up from data in the given configuration. If the
-        changes are accepted by the user, the configuration is updated and saved."""
-        self._fromConfig(config)
-
-        self.show_all()
-        response = super(Preferences, self).run()
-        self.hide()
-
-        if response==RESPONSETYPE_ACCEPT:
-            self._toConfig(config)
-            config.save()
-
-    def _fromConfig(self, config):
-        """Setup the dialog from the given configuration."""
-        self._settingFromConfig = True
-
-        self._setLanguage(config.language)
-        self._hideMinimizedWindow.set_active(config.hideMinimizedWindow)
-        self._quitOnClose.set_active(config.quitOnClose)
-        self._onlineGateSystem.set_active(config.onlineGateSystem)
-        self._onlineACARS.set_active(config.onlineACARS)
-        self._flareTimeFromFS.set_active(config.flareTimeFromFS)
-        self._syncFSTime.set_active(config.syncFSTime)
-        self._usingFS2Crew.set_active(config.usingFS2Crew)
-
-        self._setSmoothing(self._iasSmoothingEnabled, self._iasSmoothingLength,
-                           config.iasSmoothingLength)
-        self._setSmoothing(self._vsSmoothingEnabled, self._vsSmoothingLength,
-                           config.vsSmoothingLength)
-
-        self._useSimBrief.set_active(config.useSimBrief)
-
-        pirepDirectory = config.pirepDirectory
-        self._pirepDirectory.set_text("" if pirepDirectory is None
-                                      else pirepDirectory)
-        self._pirepAutoSave.set_active(config.pirepAutoSave)
-        if not pirepDirectory:
-            self._pirepAutoSave.set_sensitive(False)
-
-        for messageType in const.messageTypes:
-            level = config.getMessageTypeLevel(messageType)
-            button = self._msgFSCheckButtons[messageType]
-            button.set_active(level == const.MESSAGELEVEL_FS or
-                              level == const.MESSAGELEVEL_BOTH)
-            button = self._msgSoundCheckButtons[messageType]
-            button.set_active(level == const.MESSAGELEVEL_SOUND or
-                              level == const.MESSAGELEVEL_BOTH)
-
-        self._enableSounds.set_active(config.enableSounds)
-        self._pilotControlsSounds.set_active(config.pilotControlsSounds)
-        self._pilotHotkey.set(config.pilotHotkey)
-        self._enableApproachCallouts.set_active(config.enableApproachCallouts)
-        self._speedbrakeAtTD.set_active(config.speedbrakeAtTD)
-
-        self._enableChecklists.set_active(config.enableChecklists)
-        self._checklistHotkey.set(config.checklistHotkey)
-
-        self._autoUpdate.set_active(config.autoUpdate)
-        if not config.autoUpdate:
-            self._warnedAutoUpdate = True
-
-        self._updateURL.set_text(config.updateURL)
-
-        self._useRPC.set_active(config.useRPC)
-
-        self._settingFromConfig = False
-
-    def _toConfig(self, config):
-        """Setup the given config from the settings in the dialog."""
-        config.language = self._getLanguage()
-        config.hideMinimizedWindow = self._hideMinimizedWindow.get_active()
-        config.quitOnClose = self._quitOnClose.get_active()
-        config.onlineGateSystem = self._onlineGateSystem.get_active()
-        config.onlineACARS = self._onlineACARS.get_active()
-        config.flareTimeFromFS = self._flareTimeFromFS.get_active()
-        config.syncFSTime = self._syncFSTime.get_active()
-        config.usingFS2Crew = self._usingFS2Crew.get_active()
-        config.iasSmoothingLength = self._getSmoothing(self._iasSmoothingEnabled,
-                                                       self._iasSmoothingLength)
-        config.vsSmoothingLength = self._getSmoothing(self._vsSmoothingEnabled,
-                                                       self._vsSmoothingLength)
-        config.useSimBrief = self._useSimBrief.get_active()
-        config.pirepDirectory = text2unicode(self._pirepDirectory.get_text())
-        config.pirepAutoSave = self._pirepAutoSave.get_active()
-
-        for messageType in const.messageTypes:
-            fsButtonActive = self._msgFSCheckButtons[messageType].get_active()
-            soundButtonActive = self._msgSoundCheckButtons[messageType].get_active()
-            if fsButtonActive:
-                level = const.MESSAGELEVEL_BOTH if soundButtonActive \
-                        else const.MESSAGELEVEL_FS
-            elif soundButtonActive:
-                level = const.MESSAGELEVEL_SOUND
-            else:
-                level = const.MESSAGELEVEL_NONE
-            config.setMessageTypeLevel(messageType, level)
-
-        config.enableSounds = self._enableSounds.get_active()
-        config.pilotControlsSounds = self._pilotControlsSounds.get_active()
-        config.pilotHotkey = self._pilotHotkey.get()
-        config.enableApproachCallouts = self._enableApproachCallouts.get_active()
-        config.speedbrakeAtTD = self._speedbrakeAtTD.get_active()
-
-        config.enableChecklists = self._enableChecklists.get_active()
-        config.checklistHotkey = self._checklistHotkey.get()
-
-        config.autoUpdate = self._autoUpdate.get_active()
-        config.updateURL = self._updateURL.get_text()
-        config.useRPC = self._useRPC.get_active()
-
-    def _buildGeneral(self):
-        """Build the page for the general settings."""
-        mainAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                      xscale = 1.0, yscale = 0.0)
-        mainAlignment.set_padding(padding_top = 0, padding_bottom = 8,
-                                  padding_left = 4, padding_right = 4)
-        mainBox = gtk.VBox()
-        mainAlignment.add(mainBox)
-
-        guiBox = self._createFrame(mainBox, xstr("prefs_frame_gui"))
-
-        languageBox = gtk.HBox()
-        guiBox.pack_start(languageBox, False, False, 4)
-
-        label = gtk.Label(xstr("prefs_language"))
-        label.set_use_underline(True)
-
-        languageBox.pack_start(label, False, False, 4)
-
-        self._languageList = gtk.ListStore(str, str)
-        for language in const.languages:
-            self._languageList.append([xstr("prefs_lang_" + language),
-                                       language])
-
-        self._languageComboBox = languageComboBox = \
-            gtk.ComboBox(model = self._languageList)
-        cell = gtk.CellRendererText()
-        languageComboBox.pack_start(cell, True)
-        languageComboBox.add_attribute(cell, 'text', 0)
-        languageComboBox.set_tooltip_text(xstr("prefs_language_tooltip"))
-        languageComboBox.connect("changed", self._languageChanged)
-        languageBox.pack_start(languageComboBox, False, False, 4)
-
-        label.set_mnemonic_widget(languageComboBox)
-
-        self._changingLanguage = False
-        self._warnedRestartNeeded = False
-
-        self._hideMinimizedWindow = gtk.CheckButton(xstr("prefs_hideMinimizedWindow"))
-        self._hideMinimizedWindow.set_use_underline(True)
-        self._hideMinimizedWindow.set_tooltip_text(xstr("prefs_hideMinimizedWindow_tooltip"))
-        guiBox.pack_start(self._hideMinimizedWindow, False, False, 4)
-
-        self._quitOnClose = gtk.CheckButton(xstr("prefs_quitOnClose"))
-        self._quitOnClose.set_use_underline(True)
-        self._quitOnClose.set_tooltip_text(xstr("prefs_quitOnClose_tooltip"))
-        guiBox.pack_start(self._quitOnClose, False, False, 4)
-
-        onlineBox = self._createFrame(mainBox, xstr("prefs_frame_online"))
-
-        self._onlineGateSystem = gtk.CheckButton(xstr("prefs_onlineGateSystem"))
-        self._onlineGateSystem.set_use_underline(True)
-        self._onlineGateSystem.set_tooltip_text(xstr("prefs_onlineGateSystem_tooltip"))
-        onlineBox.pack_start(self._onlineGateSystem, False, False, 4)
-
-        self._onlineACARS = gtk.CheckButton(xstr("prefs_onlineACARS"))
-        self._onlineACARS.set_use_underline(True)
-        self._onlineACARS.set_tooltip_text(xstr("prefs_onlineACARS_tooltip"))
-        onlineBox.pack_start(self._onlineACARS, False, False, 4)
-
-        simulatorBox = self._createFrame(mainBox, xstr("prefs_frame_simulator"))
-
-        self._flareTimeFromFS = gtk.CheckButton(xstr("prefs_flaretimeFromFS"))
-        self._flareTimeFromFS.set_use_underline(True)
-        self._flareTimeFromFS.set_tooltip_text(xstr("prefs_flaretimeFromFS_tooltip"))
-        simulatorBox.pack_start(self._flareTimeFromFS, False, False, 4)
-
-        self._syncFSTime = gtk.CheckButton(xstr("prefs_syncFSTime"))
-        self._syncFSTime.set_use_underline(True)
-        self._syncFSTime.set_tooltip_text(xstr("prefs_syncFSTime_tooltip"))
-        simulatorBox.pack_start(self._syncFSTime, False, False, 4)
-
-        self._usingFS2Crew = gtk.CheckButton(xstr("prefs_usingFS2Crew"))
-        self._usingFS2Crew.set_use_underline(True)
-        self._usingFS2Crew.set_tooltip_text(xstr("prefs_usingFS2Crew_tooltip"))
-        simulatorBox.pack_start(self._usingFS2Crew, False, False, 4)
-
-        (iasSmoothingBox, self._iasSmoothingEnabled,
-         self._iasSmoothingLength) = \
-           self._createSmoothingBox(xstr("prefs_iasSmoothingEnabled"),
-                                    xstr("prefs_iasSmoothingEnabledTooltip"))
-        simulatorBox.pack_start(iasSmoothingBox, False, False, 4)
-
-        (vsSmoothingBox, self._vsSmoothingEnabled,
-         self._vsSmoothingLength) = \
-           self._createSmoothingBox(xstr("prefs_vsSmoothingEnabled"),
-                                    xstr("prefs_vsSmoothingEnabledTooltip"))
-        simulatorBox.pack_start(vsSmoothingBox, False, False, 4)
-
-        self._useSimBrief = gtk.CheckButton(xstr("prefs_useSimBrief"))
-        self._useSimBrief.set_use_underline(True)
-        self._useSimBrief.set_tooltip_text(xstr("prefs_useSimBrief_tooltip"))
-        mainBox.pack_start(self._useSimBrief, False, False, 0)
-
-        pirepBox = gtk.HBox()
-        mainBox.pack_start(pirepBox, False, False, 8)
-
-        label = gtk.Label(xstr("prefs_pirepDirectory"))
-        label.set_use_underline(True)
-        pirepBox.pack_start(label, False, False, 4)
-
-        self._pirepDirectory = gtk.Entry()
-        self._pirepDirectory.set_tooltip_text(xstr("prefs_pirepDirectory_tooltip"))
-        self._pirepDirectory.connect("changed", self._pirepDirectoryChanged)
-        label.set_mnemonic_widget(self._pirepDirectory)
-        pirepBox.pack_start(self._pirepDirectory, True, True, 4)
-
-        self._pirepDirectoryButton = gtk.Button(xstr("button_browse"))
-        self._pirepDirectoryButton.connect("clicked",
-                                           self._pirepDirectoryButtonClicked)
-        pirepBox.pack_start(self._pirepDirectoryButton, False, False, 4)
-
-        self._pirepAutoSave = gtk.CheckButton(xstr("prefs_pirepAutoSave"))
-        self._pirepAutoSave.set_use_underline(True)
-        self._pirepAutoSave.set_tooltip_text(xstr("prefs_pirepAutoSave_tooltip"))
-        mainBox.pack_start(self._pirepAutoSave, False, False, 0)
-
-        return mainAlignment
-
-    def _createFrame(self, mainBox, label):
-        """Create a frame with an inner alignment and VBox.
-
-        Return the vbox."""
-        frame = gtk.Frame(label = label)
-        mainBox.pack_start(frame, False, False, 4)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                  xscale = 1.0, yscale = 0.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 0,
-                              padding_left = 0, padding_right = 0)
-        frame.add(alignment)
-        vbox = gtk.VBox()
-        alignment.add(vbox)
-
-        return vbox
-
-    def _createSmoothingBox(self, checkBoxLabel, checkBoxTooltip,
-                            maxSeconds = 10):
-        """Create a HBox that contains entry fields for smoothing some value."""
-        smoothingBox = gtk.HBox()
-
-        smoothingEnabled = gtk.CheckButton(checkBoxLabel)
-        smoothingEnabled.set_use_underline(True)
-        smoothingEnabled.set_tooltip_text(checkBoxTooltip)
-
-        smoothingBox.pack_start(smoothingEnabled, False, False, 0)
-
-        smoothingLength = gtk.SpinButton()
-        smoothingLength.set_numeric(True)
-        smoothingLength.set_range(2, maxSeconds)
-        smoothingLength.set_increments(1, 1)
-        smoothingLength.set_alignment(1.0)
-        smoothingLength.set_width_chars(2)
-
-        smoothingBox.pack_start(smoothingLength, False, False, 0)
-
-        smoothingBox.pack_start(gtk.Label(xstr("prefs_smoothing_seconds")),
-                                False, False, 4)
-
-        smoothingEnabled.connect("toggled", self._smoothingToggled,
-                                 smoothingLength)
-        smoothingLength.set_sensitive(False)
-
-        return (smoothingBox, smoothingEnabled, smoothingLength)
-
-    def _setLanguage(self, language):
-        """Set the language to the given one."""
-        iter = self._languageList.get_iter_first()
-        while iter is not None:
-            (lang,) = self._languageList.get(iter, 1)
-            if (not language and lang=="$system") or \
-               lang==language:
-                self._changingLanguage = True
-                self._languageComboBox.set_active_iter(iter)
-                self._changingLanguage = False
-                break
-            else:
-                iter = self._languageList.iter_next(iter)
-
-    def _getLanguage(self):
-        """Get the language selected by the user."""
-        iter = self._languageComboBox.get_active_iter()
-        (lang,) = self._languageList.get(iter, 1)
-        return "" if lang=="$system" else lang
-
-    def _languageChanged(self, comboBox):
-        """Called when the language has changed."""
-        if not self._changingLanguage and not self._warnedRestartNeeded:
-            dialog = gtk.MessageDialog(parent = self,
-                                       type = MESSAGETYPE_INFO,
-                                       message_format = xstr("prefs_restart"))
-            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-            dialog.set_title(self.get_title())
-            dialog.format_secondary_markup(xstr("prefs_language_restart_sec"))
-            dialog.run()
-            dialog.hide()
-            self._warnedRestartNeeded = True
-
-    def _smoothingToggled(self, smoothingEnabled, smoothingLength):
-        """Called when a smoothing enabled check box is toggled."""
-        sensitive = smoothingEnabled.get_active()
-        smoothingLength.set_sensitive(sensitive)
-        if sensitive:
-            smoothingLength.grab_focus()
-
-    def _setSmoothing(self, smoothingEnabled, smoothingLength, smoothing):
-        """Set the smoothing controls from the given value.
-
-        If the value is less than 2, smoothing is disabled. The smoothing
-        length is the absolute value of the value."""
-        smoothingEnabled.set_active(smoothing>=2)
-        smoothingLength.set_value(abs(smoothing))
-
-    def _getSmoothing(self, smoothingEnabled, smoothingLength):
-        """Get the smoothing value from the given controls.
-
-        The returned value is the value of smoothingLength multiplied by -1, if
-        smoothing is disabled."""
-        value = smoothingLength.get_value_as_int()
-        if not smoothingEnabled.get_active():
-            value *= -1
-        return value
-
-    def _pirepDirectoryButtonClicked(self, button):
-        """Called when the PIREP directory button is clicked."""
-        dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
-                                       xstr("prefs_pirepDirectory_browser_title"),
-                                       action = FILE_CHOOSER_ACTION_SELECT_FOLDER,
-                                       buttons = (gtk.STOCK_CANCEL, RESPONSETYPE_CANCEL,
-                                                  gtk.STOCK_OK, RESPONSETYPE_OK),
-                                       parent = self)
-        dialog.set_modal(True)
-        dialog.set_transient_for(self)
-
-        directory = self._pirepDirectory.get_text()
-        if directory:
-            dialog.select_filename(directory)
-
-        result = dialog.run()
-        dialog.hide()
-
-        if result==RESPONSETYPE_OK:
-            self._pirepDirectory.set_text(text2unicode(dialog.get_filename()))
-
-    def _pirepDirectoryChanged(self, entry):
-        """Called when the PIREP directory is changed."""
-        if self._pirepDirectory.get_text():
-            self._pirepAutoSave.set_sensitive(True)
-        else:
-            self._pirepAutoSave.set_active(False)
-            self._pirepAutoSave.set_sensitive(False)
-
-    def _buildMessages(self):
-        """Build the page for the message settings."""
-
-        mainAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                      xscale = 0.0, yscale = 0.0)
-        mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
-                                  padding_left = 4, padding_right = 4)
-        mainBox = gtk.VBox()
-        mainAlignment.add(mainBox)
-
-        table = gtk.Table(len(const.messageTypes) + 1, 3)
-        table.set_row_spacings(8)
-        table.set_col_spacings(32)
-        table.set_homogeneous(False)
-        mainBox.pack_start(table, False, False, 4)
-
-        label = gtk.Label(xstr("prefs_msgs_fs"))
-        label.set_justify(JUSTIFY_CENTER)
-        label.set_alignment(0.5, 1.0)
-        table.attach(label, 1, 2, 0, 1)
-
-        label = gtk.Label(xstr("prefs_msgs_sound"))
-        label.set_justify(JUSTIFY_CENTER)
-        label.set_alignment(0.5, 1.0)
-        table.attach(label, 2, 3, 0, 1)
-
-        self._msgFSCheckButtons = {}
-        self._msgSoundCheckButtons = {}
-        row = 1
-        for messageType in const.messageTypes:
-            messageTypeStr = const.messageType2string(messageType)
-            label = gtk.Label(xstr("prefs_msgs_type_" + messageTypeStr))
-            label.set_justify(JUSTIFY_CENTER)
-            label.set_use_underline(True)
-            label.set_alignment(0.5, 0.5)
-            table.attach(label, 0, 1, row, row+1)
-
-            fsCheckButton = gtk.CheckButton()
-            alignment = gtk.Alignment(xscale = 0.0, yscale = 0.0,
-                                      xalign = 0.5, yalign = 0.5)
-            alignment.add(fsCheckButton)
-            table.attach(alignment, 1, 2, row, row+1)
-            self._msgFSCheckButtons[messageType] = fsCheckButton
-
-            soundCheckButton = gtk.CheckButton()
-            alignment = gtk.Alignment(xscale = 0.0, yscale = 0.0,
-                                      xalign = 0.5, yalign = 0.5)
-            alignment.add(soundCheckButton)
-            table.attach(alignment, 2, 3, row, row+1)
-            self._msgSoundCheckButtons[messageType] = soundCheckButton
-
-            mnemonicWidget = gtk.Label("")
-            table.attach(mnemonicWidget, 3, 4, row, row+1)
-            label.set_mnemonic_widget(mnemonicWidget)
-            mnemonicWidget.connect("mnemonic-activate",
-                                   self._msgLabelActivated,
-                                   messageType)
-
-            row += 1
-
-        return mainAlignment
-
-    def _msgLabelActivated(self, button, cycle_group, messageType):
-        """Called when the mnemonic of a label is activated.
-
-        It cycles the corresponding options."""
-        fsCheckButton = self._msgFSCheckButtons[messageType]
-        soundCheckButton = self._msgSoundCheckButtons[messageType]
-
-        num = 1 if fsCheckButton.get_active() else 0
-        num += 2 if soundCheckButton.get_active() else 0
-        num += 1
-
-        fsCheckButton.set_active((num&0x01)==0x01)
-        soundCheckButton.set_active((num&0x02)==0x02)
-
-        return True
-
-    def _buildSounds(self):
-        """Build the page for the sounds."""
-        mainAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                      xscale = 1.0, yscale = 1.0)
-        mainAlignment.set_padding(padding_top = 8, padding_bottom = 8,
-                                  padding_left = 4, padding_right = 4)
-
-        mainBox = gtk.VBox()
-        mainAlignment.add(mainBox)
-
-        backgroundFrame = gtk.Frame(label = xstr("prefs_sounds_frame_bg"))
-        mainBox.pack_start(backgroundFrame, False, False, 4)
-
-        backgroundAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                            xscale = 1.0, yscale = 0.0)
-        backgroundAlignment.set_padding(padding_top = 4, padding_bottom = 4,
-                                        padding_left = 4, padding_right = 4)
-        backgroundFrame.add(backgroundAlignment)
-
-        backgroundBox = gtk.VBox()
-        backgroundAlignment.add(backgroundBox)
-
-        self._enableSounds = gtk.CheckButton(xstr("prefs_sounds_enable"))
-        self._enableSounds.set_use_underline(True)
-        self._enableSounds.set_tooltip_text(xstr("prefs_sounds_enable_tooltip"))
-        self._enableSounds.connect("toggled", self._enableSoundsToggled)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 1.0, yscale = 0.0)
-        alignment.add(self._enableSounds)
-        backgroundBox.pack_start(alignment, False, False, 4)
-
-        self._pilotControlsSounds = gtk.CheckButton(xstr("prefs_sounds_pilotControls"))
-        self._pilotControlsSounds.set_use_underline(True)
-        self._pilotControlsSounds.set_tooltip_text(xstr("prefs_sounds_pilotControls_tooltip"))
-        self._pilotControlsSounds.connect("toggled", self._pilotControlsSoundsToggled)
-        backgroundBox.pack_start(self._pilotControlsSounds, False, False, 4)
-
-        self._pilotHotkey = Hotkey(xstr("prefs_sounds_pilotHotkey"),
-                                   [xstr("prefs_sounds_pilotHotkey_tooltip"),
-                                    xstr("prefs_sounds_pilotHotkeyCtrl_tooltip"),
-                                    xstr("prefs_sounds_pilotHotkeyShift_tooltip")])
-
-        backgroundBox.pack_start(self._pilotHotkey, False, False, 4)
-
-        self._enableApproachCallouts = gtk.CheckButton(xstr("prefs_sounds_approachCallouts"))
-        self._enableApproachCallouts.set_use_underline(True)
-        self._enableApproachCallouts.set_tooltip_text(xstr("prefs_sounds_approachCallouts_tooltip"))
-        backgroundBox.pack_start(self._enableApproachCallouts, False, False, 4)
-
-        self._speedbrakeAtTD = gtk.CheckButton(xstr("prefs_sounds_speedbrakeAtTD"))
-        self._speedbrakeAtTD.set_use_underline(True)
-        self._speedbrakeAtTD.set_tooltip_text(xstr("prefs_sounds_speedbrakeAtTD_tooltip"))
-        backgroundBox.pack_start(self._speedbrakeAtTD, False, False, 4)
-
-        checklistFrame = gtk.Frame(label = xstr("prefs_sounds_frame_checklists"))
-        mainBox.pack_start(checklistFrame, False, False, 4)
-
-        checklistAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                           xscale = 1.0, yscale = 0.0)
-        checklistAlignment.set_padding(padding_top = 4, padding_bottom = 4,
-                                       padding_left = 4, padding_right = 4)
-        checklistFrame.add(checklistAlignment)
-
-        checklistBox = gtk.VBox()
-        checklistAlignment.add(checklistBox)
-
-        self._enableChecklists = gtk.CheckButton(xstr("prefs_sounds_enableChecklists"))
-        self._enableChecklists.set_use_underline(True)
-        self._enableChecklists.set_tooltip_text(xstr("prefs_sounds_enableChecklists_tooltip"))
-        self._enableChecklists.connect("toggled", self._enableChecklistsToggled)
-        checklistBox.pack_start(self._enableChecklists, False, False, 4)
-
-        self._checklistHotkey = Hotkey(xstr("prefs_sounds_checklistHotkey"),
-                                       [xstr("prefs_sounds_checklistHotkey_tooltip"),
-                                        xstr("prefs_sounds_checklistHotkeyCtrl_tooltip"),
-                                        xstr("prefs_sounds_checklistHotkeyShift_tooltip")])
-
-        checklistBox.pack_start(self._checklistHotkey, False, False, 4)
-
-        self._enableSoundsToggled(self._enableSounds)
-        self._enableChecklistsToggled(self._enableChecklists)
-
-        self._pilotHotkey.connect("hotkey-changed", self._reconcileHotkeys,
-                                  self._checklistHotkey)
-        self._checklistHotkey.connect("hotkey-changed", self._reconcileHotkeys,
-                                      self._pilotHotkey)
-
-        return mainAlignment
-
-    def _enableSoundsToggled(self, button):
-        """Called when the enable sounds button is toggled."""
-        active = button.get_active()
-        self._pilotControlsSounds.set_sensitive(active)
-        self._pilotControlsSoundsToggled(self._pilotControlsSounds)
-        self._enableApproachCallouts.set_sensitive(active)
-        self._speedbrakeAtTD.set_sensitive(active)
-
-    def _pilotControlsSoundsToggled(self, button):
-        """Called when the enable sounds button is toggled."""
-        active = button.get_active() and self._enableSounds.get_active()
-        self._pilotHotkey.set_sensitive(active)
-        if active and self._checklistHotkey.get_sensitive():
-            self._reconcileHotkeys(self._checklistHotkey, Hotkey.CHANGED_SHIFT,
-                                   self._pilotHotkey)
-
-    def _enableChecklistsToggled(self, button):
-        """Called when the enable checklists button is toggled."""
-        active = button.get_active()
-        self._checklistHotkey.set_sensitive(active)
-        if active and self._pilotHotkey.get_sensitive():
-            self._reconcileHotkeys(self._pilotHotkey, Hotkey.CHANGED_SHIFT,
-                                   self._checklistHotkey)
-
-    def _reconcileHotkeys(self, changedHotkey, what, otherHotkey):
-        """Reconcile the given hotkeys so that they are different.
-
-        changedHotkey is the hotkey that has changed. what is one of the
-        Hotkey.CHANGED_XXX constants denoting what has changed. otherHotkey is
-        the other hotkey that must be reconciled.
-
-        If the other hotkey is not sensitive or is not equal to the changed
-        one, nothing happens.
-
-        Otherwise, if the status of the Ctrl modifier has changed, the status
-        of the Ctrl modifier on the other hotkey will be negated. Similarly, if
-        the Shift modifier has changed. If the key has changed, the Shift
-        modifier is negated in the other hotkey."""
-        if otherHotkey.get_sensitive() and changedHotkey==otherHotkey:
-            if what==Hotkey.CHANGED_CTRL:
-                otherHotkey.ctrl = not changedHotkey.ctrl
-            elif what==Hotkey.CHANGED_SHIFT or what==Hotkey.CHANGED_KEY:
-                otherHotkey.shift = not changedHotkey.shift
-
-    def _buildAdvanced(self):
-        """Build the page for the advanced settings."""
-
-        mainAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
-                                      xscale = 1.0, yscale = 0.0)
-        mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
-                                  padding_left = 4, padding_right = 4)
-        mainBox = gtk.VBox()
-        mainAlignment.add(mainBox)
-
-        self._autoUpdate = gtk.CheckButton(xstr("prefs_update_auto"))
-        mainBox.pack_start(self._autoUpdate, False, False, 4)
-
-        self._autoUpdate.set_use_underline(True)
-        self._autoUpdate.connect("toggled", self._autoUpdateToggled)
-        self._autoUpdate.set_tooltip_text(xstr("prefs_update_auto_tooltip"))
-        self._warnedAutoUpdate = False
-
-        updateURLBox = gtk.HBox()
-        mainBox.pack_start(updateURLBox, False, False, 4)
-        label = gtk.Label(xstr("prefs_update_url"))
-        label.set_use_underline(True)
-        updateURLBox.pack_start(label, False, False, 4)
-
-        self._updateURL = gtk.Entry()
-        label.set_mnemonic_widget(self._updateURL)
-        self._updateURL.set_width_chars(40)
-        self._updateURL.set_tooltip_text(xstr("prefs_update_url_tooltip"))
-        self._updateURL.connect("changed", self._updateURLChanged)
-        updateURLBox.pack_start(self._updateURL, True, True, 4)
-
-        self._useRPC = gtk.CheckButton(xstr("prefs_use_rpc"))
-        mainBox.pack_start(self._useRPC, False, False, 16)
-
-        self._useRPC.set_use_underline(True)
-        self._useRPC.set_tooltip_text(xstr("prefs_use_rpc_tooltip"))
-
-        return mainAlignment
-
-    def _setOKButtonSensitivity(self):
-        """Set the sensitive state of the OK button."""
-        sensitive = False
-        try:
-            result = urlparse.urlparse(self._updateURL.get_text())
-            sensitive = result.scheme!="" and (result.netloc + result.path)!=""
-        except:
-            pass
-
-        okButton = self.get_widget_for_response(RESPONSETYPE_ACCEPT)
-        okButton.set_sensitive(sensitive)
-
-    def _autoUpdateToggled(self, button):
-        """Called when the auto update check button is toggled."""
-        if not self._settingFromConfig and not self._warnedAutoUpdate and \
-           not self._autoUpdate.get_active():
-            dialog = gtk.MessageDialog(parent = self,
-                                       type = MESSAGETYPE_INFO,
-                                       message_format = xstr("prefs_update_auto_warning"))
-            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-            dialog.set_title(self.get_title())
-            dialog.run()
-            dialog.hide()
-            self._warnedAutoUpdate = True
-
-    def _updateURLChanged(self, entry):
-        """Called when the update URL is changed."""
-        self._setOKButtonSensitivity()
-
Index: src/mlx/gui/statusbar.py
===================================================================
--- src/mlx/gui/statusbar.py	(revision 679)
+++ src/mlx/gui/statusbar.py	(revision 679)
@@ -0,0 +1,152 @@
+
+from common import *
+
+import mlx.const as const
+from mlx.i18n import xstr
+
+import math
+import time
+
+#-------------------------------------------------------------------------------
+
+## @package mlx.gui.statusbar
+#
+# The status bar.
+#
+# This module implements the status bar seen at the lower part of the main
+# window. It contains the icon depicting the status of the connection to the
+# simulator, the current flight stage, rating and the simulator time.
+
+#-------------------------------------------------------------------------------
+
+class Statusbar(gtk.Frame, FlightStatusHandler):
+    """A status bar for the logger."""
+    def __init__(self, iconDirectory):
+        """Construct the status bar."""
+        gtk.Frame.__init__(self)
+        FlightStatusHandler.__init__(self)
+
+        self._connecting = False
+        self._connected = False
+        
+        self.set_shadow_type(gtk.ShadowType.NONE if pygobject
+                             else gtk.SHADOW_NONE)
+
+        frameAlignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
+
+        frameAlignment.set_padding(padding_top = 2, padding_bottom = 2,
+                                   padding_left = 16, padding_right = 16)
+        self.add(frameAlignment)
+
+        statusBox = gtk.HBox()
+        frameAlignment.add(statusBox)
+
+        iconPath = os.path.join(iconDirectory, "conn_grey.png")
+        self._connGreyIcon = pixbuf_new_from_file(iconPath)
+
+        iconPath = os.path.join(iconDirectory, "conn_red.png")
+        self._connRedIcon = pixbuf_new_from_file(iconPath)
+
+        iconPath = os.path.join(iconDirectory, "conn_green.png")
+        self._connGreenIcon = pixbuf_new_from_file(iconPath)
+
+        self._connStateArea = gtk.DrawingArea()
+        self._connStateArea.set_size_request(18, 18)
+        self._connStateArea.set_tooltip_markup(xstr("statusbar_conn_tooltip"))
+
+        if pygobject:
+            self._connStateArea.connect("draw", self._drawConnState)
+        else:
+            self._connStateArea.connect("expose_event", self._drawConnState)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5)
+        alignment.add(self._connStateArea)        
+
+        statusBox.pack_start(alignment, False, False, 8)
+
+        statusBox.pack_start(gtk.VSeparator(), False, False, 8)
+
+        self._stageLabel = gtk.Label()
+        longestStage = xstr("flight_stage_" +
+                            const.stage2string(const.STAGE_PUSHANDTAXI))
+        self._stageLabel.set_width_chars(len(longestStage) + 3)
+        self._stageLabel.set_tooltip_text(xstr("statusbar_stage_tooltip"))
+        self._stageLabel.set_alignment(0.0, 0.5)
+        
+        statusBox.pack_start(self._stageLabel, False, False, 8)
+
+        statusBox.pack_start(gtk.VSeparator(), False, False, 8)
+
+        self._timeLabel = gtk.Label("--:--:--")
+        self._timeLabel.set_width_chars(8)
+        self._timeLabel.set_tooltip_text(xstr("statusbar_time_tooltip"))
+        self._timeLabel.set_alignment(1.0, 0.5)
+        
+        statusBox.pack_start(self._timeLabel, False, False, 8)
+
+        statusBox.pack_start(gtk.VSeparator(), False, False, 8)
+
+        self._ratingLabel = gtk.Label()
+        self._ratingLabel.set_width_chars(20)
+        self._ratingLabel.set_tooltip_text(xstr("statusbar_rating_tooltip"))
+        self._ratingLabel.set_alignment(0.0, 0.5)
+        
+        statusBox.pack_start(self._ratingLabel, False, False, 8)
+
+        self._busyLabel = gtk.Label()
+        self._busyLabel.set_width_chars(30)
+        self._busyLabel.set_tooltip_text(xstr("statusbar_busy_tooltip"))
+        self._busyLabel.set_alignment(1.0, 0.5)
+        statusBox.pack_start(self._busyLabel, True, True, 8)
+        
+        self._updateFlightStatus()
+        self.updateTime()
+
+    def updateConnection(self, connecting, connected):
+        """Update the connection status."""
+        self._connecting = connecting
+        self._connected = connected
+        self._connStateArea.queue_draw()
+
+    def updateBusyState(self, message):
+        """Update the busy state."""
+        self._busyLabel.set_text("" if message is None else message)
+
+    def updateTime(self, t = None):
+        """Update the time"""
+        timeStr = "--:--:--" if t is None \
+                  else time.strftime("%H:%M:%S", time.gmtime(t))
+        
+        self._timeLabel.set_text(timeStr)
+
+    def _drawConnState(self, connStateArea, eventOrContext):
+        """Draw the connection state."""
+        if self._connecting:
+            if self._connected:
+                icon = self._connGreenIcon
+            else:
+                icon = self._connRedIcon
+        else:
+            icon = self._connGreyIcon
+
+        if pygobject:
+            gdk.cairo_set_source_pixbuf(eventOrContext, icon, 0, 0)
+            eventOrContext.paint()
+        else:
+            gc = connStateArea.get_style().fg_gc[gtk.STATE_NORMAL]
+            drawable = connStateArea.get_window()
+            drawable.draw_pixbuf(gc, icon, 0, 0, 0, 0)
+
+    def _updateFlightStatus(self):
+        """Update the flight status information."""
+        if self._stage is None:
+            text = "-"
+        else:
+            text = xstr("flight_stage_" + const.stage2string(self._stage)).upper()
+        self._stageLabel.set_text(text)
+    
+        if self._noGoReason is None:
+            rating = "%.1f%%" % (self._rating,)
+        else:
+            rating = '<span foreground="red">' + self._noGoReason + '</span>'
+        self._ratingLabel.set_markup(rating)
Index: src/mlx/gui/statusbar.py
===================================================================
--- src/mlx/gui/statusbar.py	(revision 679)
+++ 	(revision )
@@ -1,152 +1,0 @@
-
-from common import *
-
-import mlx.const as const
-from mlx.i18n import xstr
-
-import math
-import time
-
-#-------------------------------------------------------------------------------
-
-## @package mlx.gui.statusbar
-#
-# The status bar.
-#
-# This module implements the status bar seen at the lower part of the main
-# window. It contains the icon depicting the status of the connection to the
-# simulator, the current flight stage, rating and the simulator time.
-
-#-------------------------------------------------------------------------------
-
-class Statusbar(gtk.Frame, FlightStatusHandler):
-    """A status bar for the logger."""
-    def __init__(self, iconDirectory):
-        """Construct the status bar."""
-        gtk.Frame.__init__(self)
-        FlightStatusHandler.__init__(self)
-
-        self._connecting = False
-        self._connected = False
-        
-        self.set_shadow_type(gtk.ShadowType.NONE if pygobject
-                             else gtk.SHADOW_NONE)
-
-        frameAlignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
-
-        frameAlignment.set_padding(padding_top = 2, padding_bottom = 2,
-                                   padding_left = 16, padding_right = 16)
-        self.add(frameAlignment)
-
-        statusBox = gtk.HBox()
-        frameAlignment.add(statusBox)
-
-        iconPath = os.path.join(iconDirectory, "conn_grey.png")
-        self._connGreyIcon = pixbuf_new_from_file(iconPath)
-
-        iconPath = os.path.join(iconDirectory, "conn_red.png")
-        self._connRedIcon = pixbuf_new_from_file(iconPath)
-
-        iconPath = os.path.join(iconDirectory, "conn_green.png")
-        self._connGreenIcon = pixbuf_new_from_file(iconPath)
-
-        self._connStateArea = gtk.DrawingArea()
-        self._connStateArea.set_size_request(18, 18)
-        self._connStateArea.set_tooltip_markup(xstr("statusbar_conn_tooltip"))
-
-        if pygobject:
-            self._connStateArea.connect("draw", self._drawConnState)
-        else:
-            self._connStateArea.connect("expose_event", self._drawConnState)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5)
-        alignment.add(self._connStateArea)        
-
-        statusBox.pack_start(alignment, False, False, 8)
-
-        statusBox.pack_start(gtk.VSeparator(), False, False, 8)
-
-        self._stageLabel = gtk.Label()
-        longestStage = xstr("flight_stage_" +
-                            const.stage2string(const.STAGE_PUSHANDTAXI))
-        self._stageLabel.set_width_chars(len(longestStage) + 3)
-        self._stageLabel.set_tooltip_text(xstr("statusbar_stage_tooltip"))
-        self._stageLabel.set_alignment(0.0, 0.5)
-        
-        statusBox.pack_start(self._stageLabel, False, False, 8)
-
-        statusBox.pack_start(gtk.VSeparator(), False, False, 8)
-
-        self._timeLabel = gtk.Label("--:--:--")
-        self._timeLabel.set_width_chars(8)
-        self._timeLabel.set_tooltip_text(xstr("statusbar_time_tooltip"))
-        self._timeLabel.set_alignment(1.0, 0.5)
-        
-        statusBox.pack_start(self._timeLabel, False, False, 8)
-
-        statusBox.pack_start(gtk.VSeparator(), False, False, 8)
-
-        self._ratingLabel = gtk.Label()
-        self._ratingLabel.set_width_chars(20)
-        self._ratingLabel.set_tooltip_text(xstr("statusbar_rating_tooltip"))
-        self._ratingLabel.set_alignment(0.0, 0.5)
-        
-        statusBox.pack_start(self._ratingLabel, False, False, 8)
-
-        self._busyLabel = gtk.Label()
-        self._busyLabel.set_width_chars(30)
-        self._busyLabel.set_tooltip_text(xstr("statusbar_busy_tooltip"))
-        self._busyLabel.set_alignment(1.0, 0.5)
-        statusBox.pack_start(self._busyLabel, True, True, 8)
-        
-        self._updateFlightStatus()
-        self.updateTime()
-
-    def updateConnection(self, connecting, connected):
-        """Update the connection status."""
-        self._connecting = connecting
-        self._connected = connected
-        self._connStateArea.queue_draw()
-
-    def updateBusyState(self, message):
-        """Update the busy state."""
-        self._busyLabel.set_text("" if message is None else message)
-
-    def updateTime(self, t = None):
-        """Update the time"""
-        timeStr = "--:--:--" if t is None \
-                  else time.strftime("%H:%M:%S", time.gmtime(t))
-        
-        self._timeLabel.set_text(timeStr)
-
-    def _drawConnState(self, connStateArea, eventOrContext):
-        """Draw the connection state."""
-        if self._connecting:
-            if self._connected:
-                icon = self._connGreenIcon
-            else:
-                icon = self._connRedIcon
-        else:
-            icon = self._connGreyIcon
-
-        if pygobject:
-            gdk.cairo_set_source_pixbuf(eventOrContext, icon, 0, 0)
-            eventOrContext.paint()
-        else:
-            gc = connStateArea.get_style().fg_gc[gtk.STATE_NORMAL]
-            drawable = connStateArea.get_window()
-            drawable.draw_pixbuf(gc, icon, 0, 0, 0, 0)
-
-    def _updateFlightStatus(self):
-        """Update the flight status information."""
-        if self._stage is None:
-            text = "-"
-        else:
-            text = xstr("flight_stage_" + const.stage2string(self._stage)).upper()
-        self._stageLabel.set_text(text)
-    
-        if self._noGoReason is None:
-            rating = "%.1f%%" % (self._rating,)
-        else:
-            rating = '<span foreground="red">' + self._noGoReason + '</span>'
-        self._ratingLabel.set_markup(rating)
Index: src/mlx/gui/statusicon.py
===================================================================
--- src/mlx/gui/statusicon.py	(revision 544)
+++ src/mlx/gui/statusicon.py	(revision 544)
@@ -0,0 +1,177 @@
+
+from common import *
+
+import mlx.const as const
+from mlx.i18n import xstr
+
+#-------------------------------------------------------------------------------
+
+## @package mlx.gui.statusicon
+#
+# The status icon.
+#
+# This module implements that status icon displayed on the Windows or the GNOME
+# taskbar, or whatever the place for status icons is called in the used
+# environment. It works with both the more modern appindicator interface
+# (mainly found on Ubuntu), if that is available, or with the older status icon
+# support in Gtk (which is used on Windows as well). In this latter case, the
+# icon has a tooltip with the flight stage and rating information, while these
+# data are placed into the menu in case of appindicator.
+
+#-------------------------------------------------------------------------------
+
+class StatusIcon(FlightStatusHandler):
+    """The class handling the status icon."""
+    def __init__(self, iconDirectory, gui):
+        """Construct the status icon."""
+        super(StatusIcon, self).__init__()
+
+        self._gui = gui
+        self._selfToggling = False
+
+        menu = gtk.Menu()
+
+        if appIndicator:
+            self._stageMenuItem = gtk.MenuItem("-")
+            self._stageMenuItem.show()
+            menu.append(self._stageMenuItem)
+
+            self._ratingMenuItem = gtk.MenuItem("-")
+            self._ratingMenuItem.show()
+            menu.append(self._ratingMenuItem)
+
+            separator = gtk.SeparatorMenuItem()
+            separator.show()
+            menu.append(separator)
+
+        self._showHideMenuItem = gtk.CheckMenuItem()
+        self._showHideMenuItem.set_label(xstr("statusicon_showmain"))
+        self._showHideMenuItem.set_active(True)
+        self._showHideMenuItem.connect("toggled", self._showHideToggled)
+        self._showHideMenuItem.show()
+        menu.append(self._showHideMenuItem)
+
+        self._showMonitorMenuItem = gtk.CheckMenuItem()
+        self._showMonitorMenuItem.set_label(xstr("statusicon_showmonitor"))
+        self._showMonitorMenuItem.set_active(False)
+        self._showMonitorMenuItem.connect("toggled", self._showMonitorToggled)
+        self._showMonitorMenuItem.show()
+        menu.append(self._showMonitorMenuItem)
+
+        separator = gtk.SeparatorMenuItem()
+        separator.show()
+        menu.append(separator)
+
+        self._quitMenuItem = gtk.MenuItem()
+        self._quitMenuItem.set_label(xstr("statusicon_quit"))
+        self._quitMenuItem.show()
+        self._quitMenuItem.connect("activate", self._gui._quit)
+        menu.append(self._quitMenuItem)
+
+        menu.show()
+
+        iconFile = os.path.join(iconDirectory, "logo.ico")
+
+        if appIndicator:
+            if pygobject:
+                indicator = appindicator.Indicator.new ("mava-logger-x", iconFile,
+                                                        appindicator.IndicatorCategory.APPLICATION_STATUS)
+                indicator.set_status (appindicator.IndicatorStatus.ACTIVE)
+            else:
+                indicator = appindicator.Indicator ("mava-logger-x", iconFile,
+                                                    appindicator.CATEGORY_APPLICATION_STATUS)
+                indicator.set_status (appindicator.STATUS_ACTIVE)
+
+            indicator.set_menu(menu)
+            self._indicator = indicator
+        else:
+            def popup_menu(status, button, time):
+                menu.popup(None, None, gtk.status_icon_position_menu,
+                           button, time, status)
+
+            statusIcon = gtk.StatusIcon()
+            statusIcon.set_from_file(iconFile)
+            statusIcon.set_visible(True)
+            statusIcon.connect('popup-menu', popup_menu)
+            statusIcon.connect('activate',
+                               lambda status: self._gui.toggleMainWindow())
+            self._statusIcon = statusIcon
+
+        self._updateFlightStatus()
+
+    def mainWindowHidden(self):
+        """Called when the main window is hidden."""
+        if self._showHideMenuItem.get_active():
+            self._selfToggling = True
+            self._showHideMenuItem.set_active(False)
+
+    def mainWindowShown(self):
+        """Called when the main window is shown."""
+        if not self._showHideMenuItem.get_active():
+            self._selfToggling = True
+            self._showHideMenuItem.set_active(True)
+
+    def monitorWindowHidden(self):
+        """Called when the monitor window is hidden."""
+        if self._showMonitorMenuItem.get_active():
+            self._selfToggling = True
+            self._showMonitorMenuItem.set_active(False)
+
+    def monitorWindowShown(self):
+        """Called when the monitor window is shown."""
+        if not self._showMonitorMenuItem.get_active():
+            self._selfToggling = True
+            self._showMonitorMenuItem.set_active(True)
+
+    def destroy(self):
+        """Hide and destroy the status icon."""
+        if appIndicator:
+            if pygobject:
+                self._indicator.set_status(appindicator.IndicatorStatus.PASSIVE)
+            else:
+                self._indicator.set_status(appindicator.STATUS_PASSIVE)
+        else:
+            self._statusIcon.set_visible(False)
+
+    def _showHideToggled(self, menuitem):
+        """Called when the show/hide menu item is toggled."""
+        if self._selfToggling:
+            self._selfToggling = False
+        elif self._showHideMenuItem.get_active():
+            self._gui.showMainWindow()
+        else:
+            self._gui.hideMainWindow()
+
+    def _showMonitorToggled(self, menuitem):
+        """Called when the show/hide monitor window menu item is toggled."""
+        if self._selfToggling:
+            self._selfToggling = False
+        elif self._showMonitorMenuItem.get_active():
+            self._gui.showMonitorWindow()
+        else:
+            self._gui.hideMonitorWindow()
+
+    def _updateFlightStatus(self):
+        """Update the flight status."""
+        stage = u"-" if self._stage is None \
+                else xstr("flight_stage_" + const.stage2string(self._stage))
+
+        if self._noGoReason is None:
+            rating = "%.1f%%" % (self._rating,)
+        else:
+            rating = self._noGoReason
+
+        if appIndicator:
+            self._stageMenuItem.set_label("%s: %s" % \
+                                          (xstr("statusicon_stage"),
+                                           stage))
+            self._ratingMenuItem.set_label("%s: %s" % \
+                                           (xstr("statusicon_rating"),
+                                            rating))
+        else:
+            if self._noGoReason is not None:
+                rating = '<span foreground="red">' + rating + '</span>'
+            markup = u"MAVA Logger X %s\n\n%s: %s\n%s: %s" %\
+                     (const.VERSION, xstr("statusicon_stage"), stage,
+                      xstr("statusicon_rating"), rating)
+            self._statusIcon.set_tooltip_markup(markup)
Index: src/mlx/gui/statusicon.py
===================================================================
--- src/mlx/gui/statusicon.py	(revision 544)
+++ 	(revision )
@@ -1,177 +1,0 @@
-
-from common import *
-
-import mlx.const as const
-from mlx.i18n import xstr
-
-#-------------------------------------------------------------------------------
-
-## @package mlx.gui.statusicon
-#
-# The status icon.
-#
-# This module implements that status icon displayed on the Windows or the GNOME
-# taskbar, or whatever the place for status icons is called in the used
-# environment. It works with both the more modern appindicator interface
-# (mainly found on Ubuntu), if that is available, or with the older status icon
-# support in Gtk (which is used on Windows as well). In this latter case, the
-# icon has a tooltip with the flight stage and rating information, while these
-# data are placed into the menu in case of appindicator.
-
-#-------------------------------------------------------------------------------
-
-class StatusIcon(FlightStatusHandler):
-    """The class handling the status icon."""
-    def __init__(self, iconDirectory, gui):
-        """Construct the status icon."""
-        super(StatusIcon, self).__init__()
-
-        self._gui = gui
-        self._selfToggling = False
-
-        menu = gtk.Menu()
-
-        if appIndicator:
-            self._stageMenuItem = gtk.MenuItem("-")
-            self._stageMenuItem.show()
-            menu.append(self._stageMenuItem)
-
-            self._ratingMenuItem = gtk.MenuItem("-")
-            self._ratingMenuItem.show()
-            menu.append(self._ratingMenuItem)
-
-            separator = gtk.SeparatorMenuItem()
-            separator.show()
-            menu.append(separator)
-
-        self._showHideMenuItem = gtk.CheckMenuItem()
-        self._showHideMenuItem.set_label(xstr("statusicon_showmain"))
-        self._showHideMenuItem.set_active(True)
-        self._showHideMenuItem.connect("toggled", self._showHideToggled)
-        self._showHideMenuItem.show()
-        menu.append(self._showHideMenuItem)
-
-        self._showMonitorMenuItem = gtk.CheckMenuItem()
-        self._showMonitorMenuItem.set_label(xstr("statusicon_showmonitor"))
-        self._showMonitorMenuItem.set_active(False)
-        self._showMonitorMenuItem.connect("toggled", self._showMonitorToggled)
-        self._showMonitorMenuItem.show()
-        menu.append(self._showMonitorMenuItem)
-
-        separator = gtk.SeparatorMenuItem()
-        separator.show()
-        menu.append(separator)
-
-        self._quitMenuItem = gtk.MenuItem()
-        self._quitMenuItem.set_label(xstr("statusicon_quit"))
-        self._quitMenuItem.show()
-        self._quitMenuItem.connect("activate", self._gui._quit)
-        menu.append(self._quitMenuItem)
-
-        menu.show()
-
-        iconFile = os.path.join(iconDirectory, "logo.ico")
-
-        if appIndicator:
-            if pygobject:
-                indicator = appindicator.Indicator.new ("mava-logger-x", iconFile,
-                                                        appindicator.IndicatorCategory.APPLICATION_STATUS)
-                indicator.set_status (appindicator.IndicatorStatus.ACTIVE)
-            else:
-                indicator = appindicator.Indicator ("mava-logger-x", iconFile,
-                                                    appindicator.CATEGORY_APPLICATION_STATUS)
-                indicator.set_status (appindicator.STATUS_ACTIVE)
-
-            indicator.set_menu(menu)
-            self._indicator = indicator
-        else:
-            def popup_menu(status, button, time):
-                menu.popup(None, None, gtk.status_icon_position_menu,
-                           button, time, status)
-
-            statusIcon = gtk.StatusIcon()
-            statusIcon.set_from_file(iconFile)
-            statusIcon.set_visible(True)
-            statusIcon.connect('popup-menu', popup_menu)
-            statusIcon.connect('activate',
-                               lambda status: self._gui.toggleMainWindow())
-            self._statusIcon = statusIcon
-
-        self._updateFlightStatus()
-
-    def mainWindowHidden(self):
-        """Called when the main window is hidden."""
-        if self._showHideMenuItem.get_active():
-            self._selfToggling = True
-            self._showHideMenuItem.set_active(False)
-
-    def mainWindowShown(self):
-        """Called when the main window is shown."""
-        if not self._showHideMenuItem.get_active():
-            self._selfToggling = True
-            self._showHideMenuItem.set_active(True)
-
-    def monitorWindowHidden(self):
-        """Called when the monitor window is hidden."""
-        if self._showMonitorMenuItem.get_active():
-            self._selfToggling = True
-            self._showMonitorMenuItem.set_active(False)
-
-    def monitorWindowShown(self):
-        """Called when the monitor window is shown."""
-        if not self._showMonitorMenuItem.get_active():
-            self._selfToggling = True
-            self._showMonitorMenuItem.set_active(True)
-
-    def destroy(self):
-        """Hide and destroy the status icon."""
-        if appIndicator:
-            if pygobject:
-                self._indicator.set_status(appindicator.IndicatorStatus.PASSIVE)
-            else:
-                self._indicator.set_status(appindicator.STATUS_PASSIVE)
-        else:
-            self._statusIcon.set_visible(False)
-
-    def _showHideToggled(self, menuitem):
-        """Called when the show/hide menu item is toggled."""
-        if self._selfToggling:
-            self._selfToggling = False
-        elif self._showHideMenuItem.get_active():
-            self._gui.showMainWindow()
-        else:
-            self._gui.hideMainWindow()
-
-    def _showMonitorToggled(self, menuitem):
-        """Called when the show/hide monitor window menu item is toggled."""
-        if self._selfToggling:
-            self._selfToggling = False
-        elif self._showMonitorMenuItem.get_active():
-            self._gui.showMonitorWindow()
-        else:
-            self._gui.hideMonitorWindow()
-
-    def _updateFlightStatus(self):
-        """Update the flight status."""
-        stage = u"-" if self._stage is None \
-                else xstr("flight_stage_" + const.stage2string(self._stage))
-
-        if self._noGoReason is None:
-            rating = "%.1f%%" % (self._rating,)
-        else:
-            rating = self._noGoReason
-
-        if appIndicator:
-            self._stageMenuItem.set_label("%s: %s" % \
-                                          (xstr("statusicon_stage"),
-                                           stage))
-            self._ratingMenuItem.set_label("%s: %s" % \
-                                           (xstr("statusicon_rating"),
-                                            rating))
-        else:
-            if self._noGoReason is not None:
-                rating = '<span foreground="red">' + rating + '</span>'
-            markup = u"MAVA Logger X %s\n\n%s: %s\n%s: %s" %\
-                     (const.VERSION, xstr("statusicon_stage"), stage,
-                      xstr("statusicon_rating"), rating)
-            self._statusIcon.set_tooltip_markup(markup)
Index: src/mlx/gui/timetable.py
===================================================================
--- src/mlx/gui/timetable.py	(revision 859)
+++ src/mlx/gui/timetable.py	(revision 859)
@@ -0,0 +1,804 @@
+# Module for handling the time table and booking flights
+
+#-----------------------------------------------------------------------------
+
+from mlx.gui.common import *
+from flightlist import ColumnDescriptor
+from mlx.rpc import ScheduledFlight
+
+import mlx.const as const
+
+import datetime
+import random
+
+#-----------------------------------------------------------------------------
+
+class Timetable(gtk.Alignment):
+    """The widget for the time table."""
+    def _getVIPRenderer():
+        """Get the renderer for the VIP column."""
+        renderer = gtk.CellRendererToggle()
+        renderer.set_activatable(True)
+        return renderer
+
+    defaultColumnDescriptors = [
+        ColumnDescriptor("callsign", xstr("timetable_no"),
+                         sortable = True, defaultSortable = True),
+        ColumnDescriptor("aircraftType", xstr("timetable_type"),
+                         sortable = True,
+                         convertFn = lambda aircraftType, flight:
+                             aircraftNames[aircraftType]),
+        ColumnDescriptor("departureICAO", xstr("timetable_from"),
+                         sortable = True),
+        ColumnDescriptor("arrivalICAO", xstr("timetable_to"), sortable = True),
+        ColumnDescriptor("departureTime", xstr("timetable_dep"),
+                         sortable = True),
+        ColumnDescriptor("arrivalTime", xstr("timetable_arr"), sortable = True),
+        ColumnDescriptor("duration", xstr("timetable_duration"),
+                         sortable = True,
+                         convertFn = lambda duration, flight:
+                         "%02d:%02d" % (duration/3600,
+                                        (duration%3600)/60)),
+        ColumnDescriptor("type", xstr("timetable_vip"), type = bool,
+                         renderer = _getVIPRenderer(),
+                         sortable = True,
+                         convertFn = lambda type, flight:
+                             type==ScheduledFlight.TYPE_VIP)
+    ]
+
+    columnOrdering = ["callsign", "aircraftType",
+                      "date", "departureTime", "arrivalTime",
+                      "departureICAO", "arrivalICAO", "duration", "type"]
+
+    @staticmethod
+    def isFlightSelected(flight, regularEnabled, vipEnabled, aircraftTypes):
+        """Determine if the given flight is selected by the given
+        filtering conditions."""
+        return ((regularEnabled and flight.type==ScheduledFlight.TYPE_NORMAL) or \
+                (vipEnabled and flight.type==ScheduledFlight.TYPE_VIP)) and \
+               flight.aircraftType in aircraftTypes
+
+    def __init__(self, columnDescriptors = defaultColumnDescriptors,
+                 popupMenuProducer = None):
+        """Construct the time table."""
+        # FIXME: this is very similar to flightlist.FlightList
+        self._columnDescriptors = columnDescriptors
+        self._popupMenuProducer = popupMenuProducer
+        self._popupMenu = None
+
+        types = [int]
+        defaultSortableIndex = None
+        for columnDescriptor in self._columnDescriptors:
+            if columnDescriptor.defaultSortable:
+                defaultSortableIndex = len(types)
+            columnDescriptor.appendType(types)
+
+        self._model = gtk.ListStore(*types)
+        if defaultSortableIndex is not None:
+            sortOrder = SORT_DESCENDING \
+              if self._columnDescriptors[defaultSortableIndex-1]._defaultDescending \
+              else SORT_ASCENDING
+            self._model.set_sort_column_id(defaultSortableIndex, sortOrder)
+        self._view = gtk.TreeView(self._model)
+
+        self._tooltips = gtk.Tooltips()
+        self._tooltips.disable()
+        self._view.connect("motion-notify-event", self._updateTooltip)
+
+        flightPairIndexColumn = gtk.TreeViewColumn()
+        flightPairIndexColumn.set_visible(False)
+        self._view.append_column(flightPairIndexColumn)
+
+        index = 1
+        for columnDescriptor in self._columnDescriptors:
+            column = columnDescriptor.getViewColumn(index)
+            self._view.append_column(column)
+            self._model.set_sort_func(index, self._compareFlights,
+                                      columnDescriptor.attribute)
+            index += 1
+
+        self._view.connect("row-activated", self._rowActivated)
+        self._view.connect("button-press-event", self._buttonPressEvent)
+
+        selection = self._view.get_selection()
+        selection.connect("changed", self._selectionChanged)
+
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.add(self._view)
+        scrolledWindow.set_size_request(800, -1)
+
+        # FIXME: these should be constants in common.py
+        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC,
+                                  gtk.PolicyType.AUTOMATIC if pygobject
+                                  else gtk.POLICY_AUTOMATIC)
+        scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
+                                       else gtk.SHADOW_IN)
+
+        super(Timetable, self).__init__(xalign = 0.5, yalign = 0.0,
+                                        xscale = 0.0, yscale = 1.0)
+        self.add(scrolledWindow)
+
+        self._flightPairs = []
+
+    @property
+    def selectedIndexes(self):
+        """Get the indexes of the selected entries, if any.
+
+        The indexes are sorted."""
+        selection = self._view.get_selection()
+        (model, rows) = selection.get_selected_rows()
+
+        indexes = [self._getIndexForPath(path) for path in rows]
+        indexes.sort()
+        return indexes
+
+    @property
+    def hasFlightPairs(self):
+        """Determine if the timetable contains any flights."""
+        return len(self._flightPairs)>0
+
+    def clear(self):
+        """Clear the flight pairs."""
+        self._model.clear()
+        self._flightPairs = []
+
+    def setFlightPairs(self, flightPairs):
+        """Setup the table contents from the given list of
+        rpc.ScheduledFlightPair objects."""
+        self.clear()
+
+        self._flightPairs = flightPairs
+
+    def getFlightPair(self, index):
+        """Get the flight pair with the given index."""
+        return self._flightPairs[index]
+
+    def updateList(self, regularEnabled, vipEnabled, types):
+        """Update the actual list according to the given filter values."""
+        index = 0
+        self._model.clear()
+        for flightPair in self._flightPairs:
+            flight = flightPair.flight0
+            if Timetable.isFlightSelected(flight, regularEnabled, vipEnabled,
+                                          types):
+                values = [index]
+                for columnDescriptor in self._columnDescriptors:
+                    values.append(columnDescriptor.getValueFrom(flight))
+                self._model.append(values)
+            index += 1
+
+    def _getIndexForPath(self, path):
+        """Get the index for the given path."""
+        iter = self._model.get_iter(path)
+        return self._model.get_value(iter, 0)
+
+    def _rowActivated(self, flightList, path, column):
+        """Called when a row is selected."""
+        self.emit("row-activated", self._getIndexForPath(path))
+
+    def _buttonPressEvent(self, widget, event):
+        """Called when a mouse button is pressed or released."""
+        if event.type!=EVENT_BUTTON_PRESS or event.button!=3 or \
+           self._popupMenuProducer is None:
+            return
+
+        (path, _, _, _) = self._view.get_path_at_pos(int(event.x),
+                                                     int(event.y))
+        selection = self._view.get_selection()
+        selection.unselect_all()
+        selection.select_path(path)
+
+        if self._popupMenu is None:
+            self._popupMenu = self._popupMenuProducer()
+        menu = self._popupMenu
+        if pygobject:
+            menu.popup(None, None, None, None, event.button, event.time)
+        else:
+            menu.popup(None, None, None, event.button, event.time)
+
+    def _selectionChanged(self, selection):
+        """Called when the selection has changed."""
+        self.emit("selection-changed", self.selectedIndexes)
+
+    def _compareFlights(self, model, iter1, iter2, mainColumn):
+        """Compare the flights at the given iterators according to the given
+        main column."""
+        index1 = self._model.get_value(iter1, 0)
+        index2 = self._model.get_value(iter2, 0)
+
+        flightPair1 = self._flightPairs[index1]
+        flightPair2 = self._flightPairs[index2]
+
+        result = flightPair1.compareBy(flightPair2, mainColumn)
+        if result==0:
+            for column in Timetable.columnOrdering:
+                if column!=mainColumn:
+                    result = flightPair1.compareBy(flightPair2, column)
+                    if result!=0:
+                        break
+        return result
+
+    def _updateTooltip(self, widget, event):
+        """Update the tooltip for the position of the given event."""
+        try:
+            (path, col, x, y) = widget.get_path_at_pos( int(event.x), int(event.y))
+            index = self._getIndexForPath(path)
+
+            flight = self._flightPairs[index].flight0
+            comment = flight.comment
+            date = flight.date
+
+            if comment or date!=const.defaultDate:
+                text = ""
+                if comment:
+                    text = comment
+                if date!=const.defaultDate:
+                    if text:
+                        text += "; "
+                    text += date.strftime("%Y-%m-%d")
+
+                self._tooltips.set_tip(widget, text)
+                self._tooltips.enable()
+            else:
+                self._tooltips.set_tip(widget, "")
+                self._tooltips.disable()
+        except Exception, e:
+            print e
+            self._tooltips.set_tip(widget, "")
+            self._tooltips.disable()
+
+#-----------------------------------------------------------------------------
+
+class CalendarWindow(gtk.Window):
+    """A window for a calendar."""
+    def __init__(self):
+        """Construct the window."""
+        super(CalendarWindow, self).__init__()
+
+        self.set_decorated(False)
+        self.set_modal(True)
+        self.connect("key-press-event", self._keyPressed)
+
+        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                      xscale = 1.0, yscale = 1.0)
+        #mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
+        #                              padding_left = 8, padding_right = 8)
+
+        self._calendar = gtk.Calendar()
+        self._calendar.connect("day-selected-double-click", self._daySelectedDoubleClick)
+        mainAlignment.add(self._calendar)
+
+        self.add(mainAlignment)
+
+    def setDate(self, date):
+        """Set the current date to the given one."""
+        self._calendar.select_month(date.month-1, date.year)
+        self._calendar.select_day(date.day)
+
+    def getDate(self):
+        """Get the currently selected date."""
+        (year, monthM1, day) = self._calendar.get_date()
+        return datetime.date(year, monthM1+1, day)
+
+    def _daySelectedDoubleClick(self, calendar):
+        """Called when a date is double clicked."""
+        self.emit("date-selected")
+
+    def _keyPressed(self, window, event):
+        """Called when a key is pressed in the window.
+
+        If the Escape key is pressed, 'delete-event' is emitted to close the
+        window."""
+        keyName = gdk.keyval_name(event.keyval)
+        if keyName =="Escape":
+            self.emit("delete-event", None)
+            return True
+        elif keyName =="Return":
+            self.emit("date-selected")
+            return True
+
+gobject.signal_new("date-selected", CalendarWindow, gobject.SIGNAL_RUN_FIRST,
+                   None, ())
+
+#-----------------------------------------------------------------------------
+
+class BookDialog(gtk.Dialog):
+    """The dialog box to select additional data for a booking."""
+    def __init__(self, timetableWindow, flightPair, planes):
+        """Construct the dialog box."""
+        super(BookDialog, self).__init__(title = WINDOW_TITLE_BASE +
+                                         " - " +
+                                         xstr("timetable_book_title"),
+                                         parent = timetableWindow)
+        contentArea = self.get_content_area()
+
+        frame = gtk.Frame(xstr("timetable_book_frame_title"))
+        frame.set_size_request(600, -1)
+
+        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                      xscale = 0.0, yscale = 0.0)
+        mainAlignment.set_padding(padding_top = 16, padding_bottom = 12,
+                                  padding_left = 8, padding_right = 8)
+
+        table = gtk.Table(6, 2)
+        table.set_row_spacings(8)
+        table.set_col_spacings(16)
+
+        row = 0
+        label = gtk.Label()
+        label.set_markup(xstr("timetable_book_callsign"))
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row + 1)
+
+        text = flightPair.flight0.callsign
+        if flightPair.flight1 is not None:
+            text += " / " + flightPair.flight1.callsign
+        label = gtk.Label(text)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row + 1)
+
+        row += 1
+
+        label = gtk.Label()
+        label.set_markup(xstr("timetable_book_from_to"))
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row + 1)
+
+        text = flightPair.flight0.departureICAO + " - " + \
+               flightPair.flight0.arrivalICAO
+        if flightPair.flight1 is not None:
+            text += " - " + flightPair.flight1.arrivalICAO
+        label = gtk.Label(text)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row + 1)
+
+        row += 1
+
+        if flightPair.flight0.type==ScheduledFlight.TYPE_VIP and \
+           flightPair.flight0.date!=const.defaultDate:
+            label = gtk.Label()
+            label.set_markup(xstr("timetable_book_flightDate"))
+            label.set_use_underline(True)
+            label.set_alignment(0.0, 0.5)
+            table.attach(label, 0, 1, row, row + 1)
+
+            self._flightDate = gtk.Button()
+            self._flightDate.connect("clicked", self._flightDateClicked)
+            self._flightDate.set_tooltip_text(xstr("timetable_book_flightDate_tooltip"))
+            label.set_mnemonic_widget(self._flightDate)
+
+            table.attach(self._flightDate, 1, 2, row, row + 1)
+
+            self._calendarWindow = calendarWindow = CalendarWindow()
+            calendarWindow.set_transient_for(self)
+            calendarWindow.connect("delete-event", self._calendarWindowDeleted)
+            calendarWindow.connect("date-selected", self._calendarWindowDateSelected)
+
+            self._setDate(flightPair.flight0.date)
+
+            row += 1
+        else:
+            self._flightDate = None
+            self._calendarWindow = None
+
+        label = gtk.Label()
+        label.set_markup(xstr("timetable_book_dep_arr"))
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row + 1)
+
+        text = str(flightPair.flight0.departureTime) + " - " + \
+               str(flightPair.flight0.arrivalTime)
+        if flightPair.flight1 is not None:
+            text += " / " + str(flightPair.flight1.departureTime) + " - " + \
+                    str(flightPair.flight1.arrivalTime)
+        label = gtk.Label(text)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row + 1)
+
+        row += 1
+
+        label = gtk.Label()
+        label.set_markup(xstr("timetable_book_duration"))
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row + 1)
+
+
+        duration = flightPair.flight0.duration
+        text = "%02d:%02d" % (duration/3600, (duration%3600)/60)
+        if flightPair.flight1 is not None:
+            duration = flightPair.flight0.duration
+            text += " / %02d:%02d" % (duration/3600, (duration%3600)/60)
+        label = gtk.Label(text)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, row, row + 1)
+
+        row += 2
+
+        label = gtk.Label()
+        label.set_markup(xstr("timetable_book_tailNumber"))
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, row, row + 1)
+
+        self._planes = planes
+        tailNumbersModel = gtk.ListStore(str)
+        for plane in planes:
+            tailNumbersModel.append((plane.tailNumber,))
+
+        self._tailNumber = gtk.ComboBox(model = tailNumbersModel)
+        renderer = gtk.CellRendererText()
+        self._tailNumber.pack_start(renderer, True)
+        self._tailNumber.add_attribute(renderer, "text", 0)
+        self._tailNumber.set_tooltip_text(xstr("timetable_book_tailNumber_tooltip"))
+        self._tailNumber.set_active(random.randint(0, len(planes)-1))
+
+        table.attach(self._tailNumber, 1, 2, row, row + 1)
+
+        mainAlignment.add(table)
+
+        frame.add(mainAlignment)
+        contentArea.pack_start(frame, True, True, 4)
+
+        self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
+
+        self._okButton = self.add_button(xstr("button_book"), RESPONSETYPE_OK)
+        self._okButton.set_use_underline(True)
+        self._okButton.set_can_default(True)
+
+    @property
+    def plane(self):
+        """Get the currently selected plane."""
+        return self._planes[self._tailNumber.get_active()]
+
+    @property
+    def date(self):
+        """Get the flight date, if selected."""
+        return None if self._calendarWindow is None \
+          else self._calendarWindow.getDate()
+
+    def _setDate(self, date):
+        """Set the date to the given one."""
+        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
+        self._calendarWindow.setDate(date)
+
+    def _flightDateClicked(self, button):
+        """Called when the flight date button is clicked."""
+        self._calendarWindow.set_position(gtk.WIN_POS_MOUSE)
+        self.set_focus(self._calendarWindow)
+        self._calendarWindow.show_all()
+
+    def _calendarWindowDeleted(self, window, event):
+        """Called when the flight date window is deleted."""
+        self._calendarWindow.hide()
+
+    def _calendarWindowDateSelected(self, window):
+        """Called when the flight date window is deleted."""
+        self._calendarWindow.hide()
+        date = window.getDate()
+        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
+
+#-----------------------------------------------------------------------------
+
+class TimetableWindow(gtk.Window):
+    """The window to display the timetable."""
+    typeFamilies = [
+        const.AIRCRAFT_FAMILY_B737NG,
+        const.AIRCRAFT_FAMILY_DH8D,
+        const.AIRCRAFT_FAMILY_B767,
+
+        const.AIRCRAFT_FAMILY_B737CL,
+        const.AIRCRAFT_FAMILY_CRJ2,
+        const.AIRCRAFT_FAMILY_F70,
+
+        const.AIRCRAFT_FAMILY_T134,
+        const.AIRCRAFT_FAMILY_T154
+        ]
+
+    def __init__(self, gui):
+        super(TimetableWindow, self).__init__()
+
+        self._gui = gui
+        self.set_title(WINDOW_TITLE_BASE + " - " + xstr("timetable_title"))
+        self.set_size_request(-1, 600)
+        self.set_transient_for(gui.mainWindow)
+        self.set_modal(True)
+        self.connect("key-press-event", self._keyPressed)
+
+        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                      xscale = 1.0, yscale = 1.0)
+        mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
+                                  padding_left = 8, padding_right = 8)
+
+        vbox = gtk.VBox()
+
+        filterAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                        xscale = 1.0, yscale = 1.0)
+
+        filterFrame = gtk.Frame(xstr("timetable_filter"))
+
+        filterVBox = gtk.VBox()
+
+        topAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                     xscale = 0.0, yscale = 0.0)
+        topHBox = gtk.HBox()
+
+        label = gtk.Label(xstr("timetable_flightdate"))
+        label.set_use_underline(True)
+        topHBox.pack_start(label, False, False, 4)
+
+        self._flightDate = gtk.Button()
+        self._flightDate.connect("clicked", self._flightDateClicked)
+        self._flightDate.connect("clicked", self._flightDateClicked)
+        self._flightDate.set_tooltip_text(xstr("timetable_flightdate_tooltip"))
+        label.set_mnemonic_widget(self._flightDate)
+        topHBox.pack_start(self._flightDate, False, False, 4)
+
+        filler = gtk.Alignment()
+        filler.set_size_request(48, 2)
+        topHBox.pack_start(filler, False, True, 0)
+
+        self._regularFlights = gtk.CheckButton(xstr("timetable_show_regular"))
+        self._regularFlights.set_use_underline(True)
+        self._regularFlights.set_tooltip_text(xstr("timetable_show_regular_tooltip"))
+        self._regularFlights.set_active(True)
+        self._regularFlights.connect("toggled", self._filterChanged)
+        topHBox.pack_start(self._regularFlights, False, False, 8)
+
+        self._vipFlights = gtk.CheckButton(xstr("timetable_show_vip"))
+        self._vipFlights.set_use_underline(True)
+        self._vipFlights.set_tooltip_text(xstr("timetable_show_vip_tooltip"))
+        self._vipFlights.set_active(True)
+        self._vipFlights.connect("toggled", self._filterChanged)
+        topHBox.pack_start(self._vipFlights, False, False, 8)
+
+        topAlignment.add(topHBox)
+
+        filterVBox.pack_start(topAlignment, False, False, 4)
+
+        separator = gtk.HSeparator()
+        filterVBox.pack_start(separator, False, False, 4)
+
+        typeAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                      xscale = 0.0, yscale = 0.0)
+
+        numColumns = 4
+        numRows = (len(TimetableWindow.typeFamilies)+numColumns-1)/numColumns
+
+        typeTable = gtk.Table(numRows, numColumns)
+        typeTable.set_col_spacings(8)
+        row = 0
+        column = 0
+        self._typeFamilyButtons = {}
+        for typeFamily in TimetableWindow.typeFamilies:
+            checkButton = gtk.CheckButton(aircraftFamilyNames[typeFamily])
+            checkButton.set_active(True)
+            checkButton.connect("toggled", self._filterChanged)
+            self._typeFamilyButtons[typeFamily] = checkButton
+
+            typeTable.attach(checkButton, column, column + 1, row, row+1)
+
+            column += 1
+            if column>=numColumns:
+                row += 1
+                column = 0
+
+        typeAlignment.add(typeTable)
+        filterVBox.pack_start(typeAlignment, False, False, 4)
+
+        filterFrame.add(filterVBox)
+
+        filterAlignment.add(filterFrame)
+        vbox.pack_start(filterAlignment, False, False, 2)
+
+        self._timetable = Timetable(popupMenuProducer =
+                                        self._createTimetablePopupMenu)
+        self._timetable.connect("row-activated", self._rowActivated)
+        self._timetable.connect("selection-changed", self._selectionChanged)
+        vbox.pack_start(self._timetable, True, True, 2)
+
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        buttonBox = gtk.HBox()
+
+        self._bookButton = gtk.Button(xstr("button_book"))
+        self._bookButton.set_use_underline(True)
+        self._bookButton.set_can_default(True)
+        self._bookButton.connect("clicked", self._bookClicked)
+        self._bookButton.set_sensitive(False)
+        buttonBox.pack_start(self._bookButton, False, False, 4);
+
+        self._closeButton = gtk.Button(xstr("button_close"))
+        self._closeButton.set_use_underline(True)
+        self._closeButton.connect("clicked", self._closeClicked)
+        buttonBox.pack_start(self._closeButton, False, False, 4);
+
+        alignment.add(buttonBox)
+        vbox.pack_start(alignment, False, False, 2)
+
+        mainAlignment.add(vbox)
+
+        self._calendarWindow = calendarWindow = CalendarWindow()
+        calendarWindow.set_transient_for(self)
+        calendarWindow.connect("delete-event", self._calendarWindowDeleted)
+        calendarWindow.connect("date-selected", self._calendarWindowDateSelected)
+
+        self.add(mainAlignment)
+
+        self.setDate(datetime.date.today())
+
+        self._flightPairToBook = None
+
+    @property
+    def hasFlightPairs(self):
+        """Determine if there any flight pairs displayed in the window."""
+        return self._timetable.hasFlightPairs
+
+    @property
+    def isRegularEnabled(self):
+        """Determine if regular flights are enabled."""
+        return self._regularFlights.get_active()!=0
+
+    @property
+    def isVIPEnabled(self):
+        """Determine if VIP flights are enabled."""
+        return self._vipFlights.get_active()!=0
+
+    def setTypes(self, aircraftTypes):
+        """Enable/disable the type family checkboxes according to the given
+        list of types."""
+        typeFamilies = set()
+        for aircraftType in aircraftTypes:
+            typeFamilies.add(const.aircraftType2Family(aircraftType))
+
+        for (typeFamily, checkButton) in self._typeFamilyButtons.iteritems():
+            checkButton.set_sensitive(typeFamily in typeFamilies)
+
+    def clear(self):
+        """Clear all flight pairs."""
+        self._timetable.clear()
+
+    def setFlightPairs(self, flightPairs):
+        """Set the flight pairs."""
+        self._timetable.setFlightPairs(flightPairs)
+        self._updateList()
+
+    def setDate(self, date):
+        """Set the date to the given one."""
+        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
+        self._calendarWindow.setDate(date)
+
+    def _closeClicked(self, button):
+        """Called when the Close button is clicked.
+
+        A 'delete-event' is emitted to close the window."""
+        self.emit("delete-event", None)
+
+    def _flightDateClicked(self, button):
+        """Called when the flight date button is clicked."""
+        self._calendarWindow.set_position(gtk.WIN_POS_MOUSE)
+        self.set_focus(self._calendarWindow)
+        self._calendarWindow.show_all()
+
+    def _calendarWindowDeleted(self, window, event):
+        """Called when the flight date window is deleted."""
+        self._calendarWindow.hide()
+
+    def _calendarWindowDateSelected(self, window):
+        """Called when the flight date window is deleted."""
+        self._calendarWindow.hide()
+        date = window.getDate()
+        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
+        self._gui.updateTimeTable(date)
+
+    def _filterChanged(self, checkButton):
+        """Called when the filter conditions have changed."""
+        self._updateList()
+
+    def _keyPressed(self, window, event):
+        """Called when a key is pressed in the window.
+
+        If the Escape key is pressed, 'delete-event' is emitted to close the
+        window."""
+        if gdk.keyval_name(event.keyval) == "Escape":
+            self.emit("delete-event", None)
+            return True
+
+    def _updateList(self):
+        """Update the timetable list."""
+        aircraftTypes = []
+        for (aircraftFamily, button) in self._typeFamilyButtons.iteritems():
+            if button.get_active():
+                aircraftTypes += const.aircraftFamily2Types[aircraftFamily]
+
+        self._timetable.updateList(self.isRegularEnabled,
+                                   self.isVIPEnabled,
+                                   aircraftTypes)
+
+    def _bookClicked(self, button):
+        """Called when the book button has been clicked."""
+        self._book(self._timetable.getFlightPair(self._timetable.selectedIndexes[0]))
+
+    def _rowActivated(self, timetable, index):
+        """Called when a row has been activated (e.g. double-clicked) in the
+        timetable."""
+        self._book(self._timetable.getFlightPair(index))
+
+    def _selectionChanged(self, timetable, indexes):
+        """Called when the selection has changed.
+
+        It sets the sensitivity of the book button based on whether a row is
+        selected or not."""
+        self._bookButton.set_sensitive(len(indexes)>0)
+
+    def _book(self, flightPair):
+        """Try to book the given flight pair."""
+        self._flightPairToBook = flightPair
+        self._gui.getFleet(callback = self._continueBook,
+                           busyCallback = self._busyCallback)
+
+    def _busyCallback(self, busy):
+        """Called when the busy state has changed."""
+        self.set_sensitive(not busy)
+
+    def _continueBook(self, fleet):
+        """Continue booking, once the fleet is available."""
+        flightPair = self._flightPairToBook
+        aircraftType = flightPair.flight0.aircraftType
+        planes = [plane for plane in fleet
+                  if plane.aircraftType == aircraftType]
+        planes.sort(cmp = lambda p1, p2: cmp(p1.tailNumber, p2.tailNumber))
+
+        dialog = BookDialog(self, flightPair, planes)
+        dialog.show_all()
+        result = dialog.run()
+        dialog.hide()
+        if result==RESPONSETYPE_OK:
+            flightIDs = [flightPair.flight0.id]
+            if flightPair.flight1 is not None:
+                flightIDs.append(flightPair.flight1.id)
+
+            date = dialog.date
+            if date is None:
+                date = self._calendarWindow.getDate()
+
+            self._gui.bookFlights(self._bookFlightsCallback,
+                                  flightIDs, date, dialog.plane.tailNumber,
+                                  busyCallback = self._busyCallback)
+
+    def _bookFlightsCallback(self, returned, result):
+        """Called when the booking has finished."""
+        if returned:
+            dialog = gtk.MessageDialog(parent = self,
+                                       type = MESSAGETYPE_INFO,
+                                       message_format = xstr("bookflights_successful"))
+            dialog.format_secondary_markup(xstr("bookflights_successful_secondary"))
+        else:
+            dialog = gtk.MessageDialog(parent = self,
+                                       type = MESSAGETYPE_ERROR,
+                                       message_format = xstr("bookflights_failed"))
+            dialog.format_secondary_markup(xstr("bookflights_failed_secondary"))
+
+        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
+        dialog.set_title(WINDOW_TITLE_BASE)
+
+        dialog.run()
+        dialog.hide()
+
+    def _createTimetablePopupMenu(self):
+        """Get the popuop menu for the timetable."""
+        menu = gtk.Menu()
+
+        menuItem = gtk.MenuItem()
+        menuItem.set_label(xstr("timetable_popup_book"))
+        menuItem.set_use_underline(True)
+        menuItem.connect("activate", self._popupBook)
+        menuItem.show()
+
+        menu.append(menuItem)
+
+        return menu
+
+    def _popupBook(self, menuItem):
+        """Try to book the given flight pair."""
+        index = self._timetable.selectedIndexes[0]
+        self._book(self._timetable.getFlightPair(index))
Index: src/mlx/gui/timetable.py
===================================================================
--- src/mlx/gui/timetable.py	(revision 859)
+++ 	(revision )
@@ -1,804 +1,0 @@
-# Module for handling the time table and booking flights
-
-#-----------------------------------------------------------------------------
-
-from mlx.gui.common import *
-from flightlist import ColumnDescriptor
-from mlx.rpc import ScheduledFlight
-
-import mlx.const as const
-
-import datetime
-import random
-
-#-----------------------------------------------------------------------------
-
-class Timetable(gtk.Alignment):
-    """The widget for the time table."""
-    def _getVIPRenderer():
-        """Get the renderer for the VIP column."""
-        renderer = gtk.CellRendererToggle()
-        renderer.set_activatable(True)
-        return renderer
-
-    defaultColumnDescriptors = [
-        ColumnDescriptor("callsign", xstr("timetable_no"),
-                         sortable = True, defaultSortable = True),
-        ColumnDescriptor("aircraftType", xstr("timetable_type"),
-                         sortable = True,
-                         convertFn = lambda aircraftType, flight:
-                             aircraftNames[aircraftType]),
-        ColumnDescriptor("departureICAO", xstr("timetable_from"),
-                         sortable = True),
-        ColumnDescriptor("arrivalICAO", xstr("timetable_to"), sortable = True),
-        ColumnDescriptor("departureTime", xstr("timetable_dep"),
-                         sortable = True),
-        ColumnDescriptor("arrivalTime", xstr("timetable_arr"), sortable = True),
-        ColumnDescriptor("duration", xstr("timetable_duration"),
-                         sortable = True,
-                         convertFn = lambda duration, flight:
-                         "%02d:%02d" % (duration/3600,
-                                        (duration%3600)/60)),
-        ColumnDescriptor("type", xstr("timetable_vip"), type = bool,
-                         renderer = _getVIPRenderer(),
-                         sortable = True,
-                         convertFn = lambda type, flight:
-                             type==ScheduledFlight.TYPE_VIP)
-    ]
-
-    columnOrdering = ["callsign", "aircraftType",
-                      "date", "departureTime", "arrivalTime",
-                      "departureICAO", "arrivalICAO", "duration", "type"]
-
-    @staticmethod
-    def isFlightSelected(flight, regularEnabled, vipEnabled, aircraftTypes):
-        """Determine if the given flight is selected by the given
-        filtering conditions."""
-        return ((regularEnabled and flight.type==ScheduledFlight.TYPE_NORMAL) or \
-                (vipEnabled and flight.type==ScheduledFlight.TYPE_VIP)) and \
-               flight.aircraftType in aircraftTypes
-
-    def __init__(self, columnDescriptors = defaultColumnDescriptors,
-                 popupMenuProducer = None):
-        """Construct the time table."""
-        # FIXME: this is very similar to flightlist.FlightList
-        self._columnDescriptors = columnDescriptors
-        self._popupMenuProducer = popupMenuProducer
-        self._popupMenu = None
-
-        types = [int]
-        defaultSortableIndex = None
-        for columnDescriptor in self._columnDescriptors:
-            if columnDescriptor.defaultSortable:
-                defaultSortableIndex = len(types)
-            columnDescriptor.appendType(types)
-
-        self._model = gtk.ListStore(*types)
-        if defaultSortableIndex is not None:
-            sortOrder = SORT_DESCENDING \
-              if self._columnDescriptors[defaultSortableIndex-1]._defaultDescending \
-              else SORT_ASCENDING
-            self._model.set_sort_column_id(defaultSortableIndex, sortOrder)
-        self._view = gtk.TreeView(self._model)
-
-        self._tooltips = gtk.Tooltips()
-        self._tooltips.disable()
-        self._view.connect("motion-notify-event", self._updateTooltip)
-
-        flightPairIndexColumn = gtk.TreeViewColumn()
-        flightPairIndexColumn.set_visible(False)
-        self._view.append_column(flightPairIndexColumn)
-
-        index = 1
-        for columnDescriptor in self._columnDescriptors:
-            column = columnDescriptor.getViewColumn(index)
-            self._view.append_column(column)
-            self._model.set_sort_func(index, self._compareFlights,
-                                      columnDescriptor.attribute)
-            index += 1
-
-        self._view.connect("row-activated", self._rowActivated)
-        self._view.connect("button-press-event", self._buttonPressEvent)
-
-        selection = self._view.get_selection()
-        selection.connect("changed", self._selectionChanged)
-
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.add(self._view)
-        scrolledWindow.set_size_request(800, -1)
-
-        # FIXME: these should be constants in common.py
-        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC,
-                                  gtk.PolicyType.AUTOMATIC if pygobject
-                                  else gtk.POLICY_AUTOMATIC)
-        scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
-                                       else gtk.SHADOW_IN)
-
-        super(Timetable, self).__init__(xalign = 0.5, yalign = 0.0,
-                                        xscale = 0.0, yscale = 1.0)
-        self.add(scrolledWindow)
-
-        self._flightPairs = []
-
-    @property
-    def selectedIndexes(self):
-        """Get the indexes of the selected entries, if any.
-
-        The indexes are sorted."""
-        selection = self._view.get_selection()
-        (model, rows) = selection.get_selected_rows()
-
-        indexes = [self._getIndexForPath(path) for path in rows]
-        indexes.sort()
-        return indexes
-
-    @property
-    def hasFlightPairs(self):
-        """Determine if the timetable contains any flights."""
-        return len(self._flightPairs)>0
-
-    def clear(self):
-        """Clear the flight pairs."""
-        self._model.clear()
-        self._flightPairs = []
-
-    def setFlightPairs(self, flightPairs):
-        """Setup the table contents from the given list of
-        rpc.ScheduledFlightPair objects."""
-        self.clear()
-
-        self._flightPairs = flightPairs
-
-    def getFlightPair(self, index):
-        """Get the flight pair with the given index."""
-        return self._flightPairs[index]
-
-    def updateList(self, regularEnabled, vipEnabled, types):
-        """Update the actual list according to the given filter values."""
-        index = 0
-        self._model.clear()
-        for flightPair in self._flightPairs:
-            flight = flightPair.flight0
-            if Timetable.isFlightSelected(flight, regularEnabled, vipEnabled,
-                                          types):
-                values = [index]
-                for columnDescriptor in self._columnDescriptors:
-                    values.append(columnDescriptor.getValueFrom(flight))
-                self._model.append(values)
-            index += 1
-
-    def _getIndexForPath(self, path):
-        """Get the index for the given path."""
-        iter = self._model.get_iter(path)
-        return self._model.get_value(iter, 0)
-
-    def _rowActivated(self, flightList, path, column):
-        """Called when a row is selected."""
-        self.emit("row-activated", self._getIndexForPath(path))
-
-    def _buttonPressEvent(self, widget, event):
-        """Called when a mouse button is pressed or released."""
-        if event.type!=EVENT_BUTTON_PRESS or event.button!=3 or \
-           self._popupMenuProducer is None:
-            return
-
-        (path, _, _, _) = self._view.get_path_at_pos(int(event.x),
-                                                     int(event.y))
-        selection = self._view.get_selection()
-        selection.unselect_all()
-        selection.select_path(path)
-
-        if self._popupMenu is None:
-            self._popupMenu = self._popupMenuProducer()
-        menu = self._popupMenu
-        if pygobject:
-            menu.popup(None, None, None, None, event.button, event.time)
-        else:
-            menu.popup(None, None, None, event.button, event.time)
-
-    def _selectionChanged(self, selection):
-        """Called when the selection has changed."""
-        self.emit("selection-changed", self.selectedIndexes)
-
-    def _compareFlights(self, model, iter1, iter2, mainColumn):
-        """Compare the flights at the given iterators according to the given
-        main column."""
-        index1 = self._model.get_value(iter1, 0)
-        index2 = self._model.get_value(iter2, 0)
-
-        flightPair1 = self._flightPairs[index1]
-        flightPair2 = self._flightPairs[index2]
-
-        result = flightPair1.compareBy(flightPair2, mainColumn)
-        if result==0:
-            for column in Timetable.columnOrdering:
-                if column!=mainColumn:
-                    result = flightPair1.compareBy(flightPair2, column)
-                    if result!=0:
-                        break
-        return result
-
-    def _updateTooltip(self, widget, event):
-        """Update the tooltip for the position of the given event."""
-        try:
-            (path, col, x, y) = widget.get_path_at_pos( int(event.x), int(event.y))
-            index = self._getIndexForPath(path)
-
-            flight = self._flightPairs[index].flight0
-            comment = flight.comment
-            date = flight.date
-
-            if comment or date!=const.defaultDate:
-                text = ""
-                if comment:
-                    text = comment
-                if date!=const.defaultDate:
-                    if text:
-                        text += "; "
-                    text += date.strftime("%Y-%m-%d")
-
-                self._tooltips.set_tip(widget, text)
-                self._tooltips.enable()
-            else:
-                self._tooltips.set_tip(widget, "")
-                self._tooltips.disable()
-        except Exception, e:
-            print e
-            self._tooltips.set_tip(widget, "")
-            self._tooltips.disable()
-
-#-----------------------------------------------------------------------------
-
-class CalendarWindow(gtk.Window):
-    """A window for a calendar."""
-    def __init__(self):
-        """Construct the window."""
-        super(CalendarWindow, self).__init__()
-
-        self.set_decorated(False)
-        self.set_modal(True)
-        self.connect("key-press-event", self._keyPressed)
-
-        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                      xscale = 1.0, yscale = 1.0)
-        #mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
-        #                              padding_left = 8, padding_right = 8)
-
-        self._calendar = gtk.Calendar()
-        self._calendar.connect("day-selected-double-click", self._daySelectedDoubleClick)
-        mainAlignment.add(self._calendar)
-
-        self.add(mainAlignment)
-
-    def setDate(self, date):
-        """Set the current date to the given one."""
-        self._calendar.select_month(date.month-1, date.year)
-        self._calendar.select_day(date.day)
-
-    def getDate(self):
-        """Get the currently selected date."""
-        (year, monthM1, day) = self._calendar.get_date()
-        return datetime.date(year, monthM1+1, day)
-
-    def _daySelectedDoubleClick(self, calendar):
-        """Called when a date is double clicked."""
-        self.emit("date-selected")
-
-    def _keyPressed(self, window, event):
-        """Called when a key is pressed in the window.
-
-        If the Escape key is pressed, 'delete-event' is emitted to close the
-        window."""
-        keyName = gdk.keyval_name(event.keyval)
-        if keyName =="Escape":
-            self.emit("delete-event", None)
-            return True
-        elif keyName =="Return":
-            self.emit("date-selected")
-            return True
-
-gobject.signal_new("date-selected", CalendarWindow, gobject.SIGNAL_RUN_FIRST,
-                   None, ())
-
-#-----------------------------------------------------------------------------
-
-class BookDialog(gtk.Dialog):
-    """The dialog box to select additional data for a booking."""
-    def __init__(self, timetableWindow, flightPair, planes):
-        """Construct the dialog box."""
-        super(BookDialog, self).__init__(title = WINDOW_TITLE_BASE +
-                                         " - " +
-                                         xstr("timetable_book_title"),
-                                         parent = timetableWindow)
-        contentArea = self.get_content_area()
-
-        frame = gtk.Frame(xstr("timetable_book_frame_title"))
-        frame.set_size_request(600, -1)
-
-        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                      xscale = 0.0, yscale = 0.0)
-        mainAlignment.set_padding(padding_top = 16, padding_bottom = 12,
-                                  padding_left = 8, padding_right = 8)
-
-        table = gtk.Table(6, 2)
-        table.set_row_spacings(8)
-        table.set_col_spacings(16)
-
-        row = 0
-        label = gtk.Label()
-        label.set_markup(xstr("timetable_book_callsign"))
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row + 1)
-
-        text = flightPair.flight0.callsign
-        if flightPair.flight1 is not None:
-            text += " / " + flightPair.flight1.callsign
-        label = gtk.Label(text)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row + 1)
-
-        row += 1
-
-        label = gtk.Label()
-        label.set_markup(xstr("timetable_book_from_to"))
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row + 1)
-
-        text = flightPair.flight0.departureICAO + " - " + \
-               flightPair.flight0.arrivalICAO
-        if flightPair.flight1 is not None:
-            text += " - " + flightPair.flight1.arrivalICAO
-        label = gtk.Label(text)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row + 1)
-
-        row += 1
-
-        if flightPair.flight0.type==ScheduledFlight.TYPE_VIP and \
-           flightPair.flight0.date!=const.defaultDate:
-            label = gtk.Label()
-            label.set_markup(xstr("timetable_book_flightDate"))
-            label.set_use_underline(True)
-            label.set_alignment(0.0, 0.5)
-            table.attach(label, 0, 1, row, row + 1)
-
-            self._flightDate = gtk.Button()
-            self._flightDate.connect("clicked", self._flightDateClicked)
-            self._flightDate.set_tooltip_text(xstr("timetable_book_flightDate_tooltip"))
-            label.set_mnemonic_widget(self._flightDate)
-
-            table.attach(self._flightDate, 1, 2, row, row + 1)
-
-            self._calendarWindow = calendarWindow = CalendarWindow()
-            calendarWindow.set_transient_for(self)
-            calendarWindow.connect("delete-event", self._calendarWindowDeleted)
-            calendarWindow.connect("date-selected", self._calendarWindowDateSelected)
-
-            self._setDate(flightPair.flight0.date)
-
-            row += 1
-        else:
-            self._flightDate = None
-            self._calendarWindow = None
-
-        label = gtk.Label()
-        label.set_markup(xstr("timetable_book_dep_arr"))
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row + 1)
-
-        text = str(flightPair.flight0.departureTime) + " - " + \
-               str(flightPair.flight0.arrivalTime)
-        if flightPair.flight1 is not None:
-            text += " / " + str(flightPair.flight1.departureTime) + " - " + \
-                    str(flightPair.flight1.arrivalTime)
-        label = gtk.Label(text)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row + 1)
-
-        row += 1
-
-        label = gtk.Label()
-        label.set_markup(xstr("timetable_book_duration"))
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row + 1)
-
-
-        duration = flightPair.flight0.duration
-        text = "%02d:%02d" % (duration/3600, (duration%3600)/60)
-        if flightPair.flight1 is not None:
-            duration = flightPair.flight0.duration
-            text += " / %02d:%02d" % (duration/3600, (duration%3600)/60)
-        label = gtk.Label(text)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, row, row + 1)
-
-        row += 2
-
-        label = gtk.Label()
-        label.set_markup(xstr("timetable_book_tailNumber"))
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, row, row + 1)
-
-        self._planes = planes
-        tailNumbersModel = gtk.ListStore(str)
-        for plane in planes:
-            tailNumbersModel.append((plane.tailNumber,))
-
-        self._tailNumber = gtk.ComboBox(model = tailNumbersModel)
-        renderer = gtk.CellRendererText()
-        self._tailNumber.pack_start(renderer, True)
-        self._tailNumber.add_attribute(renderer, "text", 0)
-        self._tailNumber.set_tooltip_text(xstr("timetable_book_tailNumber_tooltip"))
-        self._tailNumber.set_active(random.randint(0, len(planes)-1))
-
-        table.attach(self._tailNumber, 1, 2, row, row + 1)
-
-        mainAlignment.add(table)
-
-        frame.add(mainAlignment)
-        contentArea.pack_start(frame, True, True, 4)
-
-        self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
-
-        self._okButton = self.add_button(xstr("button_book"), RESPONSETYPE_OK)
-        self._okButton.set_use_underline(True)
-        self._okButton.set_can_default(True)
-
-    @property
-    def plane(self):
-        """Get the currently selected plane."""
-        return self._planes[self._tailNumber.get_active()]
-
-    @property
-    def date(self):
-        """Get the flight date, if selected."""
-        return None if self._calendarWindow is None \
-          else self._calendarWindow.getDate()
-
-    def _setDate(self, date):
-        """Set the date to the given one."""
-        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
-        self._calendarWindow.setDate(date)
-
-    def _flightDateClicked(self, button):
-        """Called when the flight date button is clicked."""
-        self._calendarWindow.set_position(gtk.WIN_POS_MOUSE)
-        self.set_focus(self._calendarWindow)
-        self._calendarWindow.show_all()
-
-    def _calendarWindowDeleted(self, window, event):
-        """Called when the flight date window is deleted."""
-        self._calendarWindow.hide()
-
-    def _calendarWindowDateSelected(self, window):
-        """Called when the flight date window is deleted."""
-        self._calendarWindow.hide()
-        date = window.getDate()
-        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
-
-#-----------------------------------------------------------------------------
-
-class TimetableWindow(gtk.Window):
-    """The window to display the timetable."""
-    typeFamilies = [
-        const.AIRCRAFT_FAMILY_B737NG,
-        const.AIRCRAFT_FAMILY_DH8D,
-        const.AIRCRAFT_FAMILY_B767,
-
-        const.AIRCRAFT_FAMILY_B737CL,
-        const.AIRCRAFT_FAMILY_CRJ2,
-        const.AIRCRAFT_FAMILY_F70,
-
-        const.AIRCRAFT_FAMILY_T134,
-        const.AIRCRAFT_FAMILY_T154
-        ]
-
-    def __init__(self, gui):
-        super(TimetableWindow, self).__init__()
-
-        self._gui = gui
-        self.set_title(WINDOW_TITLE_BASE + " - " + xstr("timetable_title"))
-        self.set_size_request(-1, 600)
-        self.set_transient_for(gui.mainWindow)
-        self.set_modal(True)
-        self.connect("key-press-event", self._keyPressed)
-
-        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                      xscale = 1.0, yscale = 1.0)
-        mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
-                                  padding_left = 8, padding_right = 8)
-
-        vbox = gtk.VBox()
-
-        filterAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                        xscale = 1.0, yscale = 1.0)
-
-        filterFrame = gtk.Frame(xstr("timetable_filter"))
-
-        filterVBox = gtk.VBox()
-
-        topAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                     xscale = 0.0, yscale = 0.0)
-        topHBox = gtk.HBox()
-
-        label = gtk.Label(xstr("timetable_flightdate"))
-        label.set_use_underline(True)
-        topHBox.pack_start(label, False, False, 4)
-
-        self._flightDate = gtk.Button()
-        self._flightDate.connect("clicked", self._flightDateClicked)
-        self._flightDate.connect("clicked", self._flightDateClicked)
-        self._flightDate.set_tooltip_text(xstr("timetable_flightdate_tooltip"))
-        label.set_mnemonic_widget(self._flightDate)
-        topHBox.pack_start(self._flightDate, False, False, 4)
-
-        filler = gtk.Alignment()
-        filler.set_size_request(48, 2)
-        topHBox.pack_start(filler, False, True, 0)
-
-        self._regularFlights = gtk.CheckButton(xstr("timetable_show_regular"))
-        self._regularFlights.set_use_underline(True)
-        self._regularFlights.set_tooltip_text(xstr("timetable_show_regular_tooltip"))
-        self._regularFlights.set_active(True)
-        self._regularFlights.connect("toggled", self._filterChanged)
-        topHBox.pack_start(self._regularFlights, False, False, 8)
-
-        self._vipFlights = gtk.CheckButton(xstr("timetable_show_vip"))
-        self._vipFlights.set_use_underline(True)
-        self._vipFlights.set_tooltip_text(xstr("timetable_show_vip_tooltip"))
-        self._vipFlights.set_active(True)
-        self._vipFlights.connect("toggled", self._filterChanged)
-        topHBox.pack_start(self._vipFlights, False, False, 8)
-
-        topAlignment.add(topHBox)
-
-        filterVBox.pack_start(topAlignment, False, False, 4)
-
-        separator = gtk.HSeparator()
-        filterVBox.pack_start(separator, False, False, 4)
-
-        typeAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                      xscale = 0.0, yscale = 0.0)
-
-        numColumns = 4
-        numRows = (len(TimetableWindow.typeFamilies)+numColumns-1)/numColumns
-
-        typeTable = gtk.Table(numRows, numColumns)
-        typeTable.set_col_spacings(8)
-        row = 0
-        column = 0
-        self._typeFamilyButtons = {}
-        for typeFamily in TimetableWindow.typeFamilies:
-            checkButton = gtk.CheckButton(aircraftFamilyNames[typeFamily])
-            checkButton.set_active(True)
-            checkButton.connect("toggled", self._filterChanged)
-            self._typeFamilyButtons[typeFamily] = checkButton
-
-            typeTable.attach(checkButton, column, column + 1, row, row+1)
-
-            column += 1
-            if column>=numColumns:
-                row += 1
-                column = 0
-
-        typeAlignment.add(typeTable)
-        filterVBox.pack_start(typeAlignment, False, False, 4)
-
-        filterFrame.add(filterVBox)
-
-        filterAlignment.add(filterFrame)
-        vbox.pack_start(filterAlignment, False, False, 2)
-
-        self._timetable = Timetable(popupMenuProducer =
-                                        self._createTimetablePopupMenu)
-        self._timetable.connect("row-activated", self._rowActivated)
-        self._timetable.connect("selection-changed", self._selectionChanged)
-        vbox.pack_start(self._timetable, True, True, 2)
-
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        buttonBox = gtk.HBox()
-
-        self._bookButton = gtk.Button(xstr("button_book"))
-        self._bookButton.set_use_underline(True)
-        self._bookButton.set_can_default(True)
-        self._bookButton.connect("clicked", self._bookClicked)
-        self._bookButton.set_sensitive(False)
-        buttonBox.pack_start(self._bookButton, False, False, 4);
-
-        self._closeButton = gtk.Button(xstr("button_close"))
-        self._closeButton.set_use_underline(True)
-        self._closeButton.connect("clicked", self._closeClicked)
-        buttonBox.pack_start(self._closeButton, False, False, 4);
-
-        alignment.add(buttonBox)
-        vbox.pack_start(alignment, False, False, 2)
-
-        mainAlignment.add(vbox)
-
-        self._calendarWindow = calendarWindow = CalendarWindow()
-        calendarWindow.set_transient_for(self)
-        calendarWindow.connect("delete-event", self._calendarWindowDeleted)
-        calendarWindow.connect("date-selected", self._calendarWindowDateSelected)
-
-        self.add(mainAlignment)
-
-        self.setDate(datetime.date.today())
-
-        self._flightPairToBook = None
-
-    @property
-    def hasFlightPairs(self):
-        """Determine if there any flight pairs displayed in the window."""
-        return self._timetable.hasFlightPairs
-
-    @property
-    def isRegularEnabled(self):
-        """Determine if regular flights are enabled."""
-        return self._regularFlights.get_active()!=0
-
-    @property
-    def isVIPEnabled(self):
-        """Determine if VIP flights are enabled."""
-        return self._vipFlights.get_active()!=0
-
-    def setTypes(self, aircraftTypes):
-        """Enable/disable the type family checkboxes according to the given
-        list of types."""
-        typeFamilies = set()
-        for aircraftType in aircraftTypes:
-            typeFamilies.add(const.aircraftType2Family(aircraftType))
-
-        for (typeFamily, checkButton) in self._typeFamilyButtons.iteritems():
-            checkButton.set_sensitive(typeFamily in typeFamilies)
-
-    def clear(self):
-        """Clear all flight pairs."""
-        self._timetable.clear()
-
-    def setFlightPairs(self, flightPairs):
-        """Set the flight pairs."""
-        self._timetable.setFlightPairs(flightPairs)
-        self._updateList()
-
-    def setDate(self, date):
-        """Set the date to the given one."""
-        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
-        self._calendarWindow.setDate(date)
-
-    def _closeClicked(self, button):
-        """Called when the Close button is clicked.
-
-        A 'delete-event' is emitted to close the window."""
-        self.emit("delete-event", None)
-
-    def _flightDateClicked(self, button):
-        """Called when the flight date button is clicked."""
-        self._calendarWindow.set_position(gtk.WIN_POS_MOUSE)
-        self.set_focus(self._calendarWindow)
-        self._calendarWindow.show_all()
-
-    def _calendarWindowDeleted(self, window, event):
-        """Called when the flight date window is deleted."""
-        self._calendarWindow.hide()
-
-    def _calendarWindowDateSelected(self, window):
-        """Called when the flight date window is deleted."""
-        self._calendarWindow.hide()
-        date = window.getDate()
-        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
-        self._gui.updateTimeTable(date)
-
-    def _filterChanged(self, checkButton):
-        """Called when the filter conditions have changed."""
-        self._updateList()
-
-    def _keyPressed(self, window, event):
-        """Called when a key is pressed in the window.
-
-        If the Escape key is pressed, 'delete-event' is emitted to close the
-        window."""
-        if gdk.keyval_name(event.keyval) == "Escape":
-            self.emit("delete-event", None)
-            return True
-
-    def _updateList(self):
-        """Update the timetable list."""
-        aircraftTypes = []
-        for (aircraftFamily, button) in self._typeFamilyButtons.iteritems():
-            if button.get_active():
-                aircraftTypes += const.aircraftFamily2Types[aircraftFamily]
-
-        self._timetable.updateList(self.isRegularEnabled,
-                                   self.isVIPEnabled,
-                                   aircraftTypes)
-
-    def _bookClicked(self, button):
-        """Called when the book button has been clicked."""
-        self._book(self._timetable.getFlightPair(self._timetable.selectedIndexes[0]))
-
-    def _rowActivated(self, timetable, index):
-        """Called when a row has been activated (e.g. double-clicked) in the
-        timetable."""
-        self._book(self._timetable.getFlightPair(index))
-
-    def _selectionChanged(self, timetable, indexes):
-        """Called when the selection has changed.
-
-        It sets the sensitivity of the book button based on whether a row is
-        selected or not."""
-        self._bookButton.set_sensitive(len(indexes)>0)
-
-    def _book(self, flightPair):
-        """Try to book the given flight pair."""
-        self._flightPairToBook = flightPair
-        self._gui.getFleet(callback = self._continueBook,
-                           busyCallback = self._busyCallback)
-
-    def _busyCallback(self, busy):
-        """Called when the busy state has changed."""
-        self.set_sensitive(not busy)
-
-    def _continueBook(self, fleet):
-        """Continue booking, once the fleet is available."""
-        flightPair = self._flightPairToBook
-        aircraftType = flightPair.flight0.aircraftType
-        planes = [plane for plane in fleet
-                  if plane.aircraftType == aircraftType]
-        planes.sort(cmp = lambda p1, p2: cmp(p1.tailNumber, p2.tailNumber))
-
-        dialog = BookDialog(self, flightPair, planes)
-        dialog.show_all()
-        result = dialog.run()
-        dialog.hide()
-        if result==RESPONSETYPE_OK:
-            flightIDs = [flightPair.flight0.id]
-            if flightPair.flight1 is not None:
-                flightIDs.append(flightPair.flight1.id)
-
-            date = dialog.date
-            if date is None:
-                date = self._calendarWindow.getDate()
-
-            self._gui.bookFlights(self._bookFlightsCallback,
-                                  flightIDs, date, dialog.plane.tailNumber,
-                                  busyCallback = self._busyCallback)
-
-    def _bookFlightsCallback(self, returned, result):
-        """Called when the booking has finished."""
-        if returned:
-            dialog = gtk.MessageDialog(parent = self,
-                                       type = MESSAGETYPE_INFO,
-                                       message_format = xstr("bookflights_successful"))
-            dialog.format_secondary_markup(xstr("bookflights_successful_secondary"))
-        else:
-            dialog = gtk.MessageDialog(parent = self,
-                                       type = MESSAGETYPE_ERROR,
-                                       message_format = xstr("bookflights_failed"))
-            dialog.format_secondary_markup(xstr("bookflights_failed_secondary"))
-
-        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
-        dialog.set_title(WINDOW_TITLE_BASE)
-
-        dialog.run()
-        dialog.hide()
-
-    def _createTimetablePopupMenu(self):
-        """Get the popuop menu for the timetable."""
-        menu = gtk.Menu()
-
-        menuItem = gtk.MenuItem()
-        menuItem.set_label(xstr("timetable_popup_book"))
-        menuItem.set_use_underline(True)
-        menuItem.connect("activate", self._popupBook)
-        menuItem.show()
-
-        menu.append(menuItem)
-
-        return menu
-
-    def _popupBook(self, menuItem):
-        """Try to book the given flight pair."""
-        index = self._timetable.selectedIndexes[0]
-        self._book(self._timetable.getFlightPair(index))
Index: src/mlx/gui/update.py
===================================================================
--- src/mlx/gui/update.py	(revision 755)
+++ src/mlx/gui/update.py	(revision 755)
@@ -0,0 +1,292 @@
+
+from mlx.gui.common import *
+
+from mlx.update import update
+from mlx.i18n import xstr
+
+import mlx.const as const
+
+import threading
+import os
+import sys
+
+#-------------------------------------------------------------------------------
+
+## @package mlx.gui.update
+#
+# The GUI part of the update.
+#
+# This module implements the GUI portion of the \ref mlx.update
+# "automatic update" function. The update runs in a separate thread, which puts
+# the relevant events into the Gtk event loop.
+
+#-------------------------------------------------------------------------------
+
+class Updater(threading.Thread):
+    """The updater thread."""
+
+    # The removal or renaming of one file equals to the downloading of this
+    # many bytes in time (for the progress calculation)
+    REMOVE2BYTES = 100
+
+    _progressWindow = None
+
+    _progressLabel = None
+    
+    _progressBar = None
+    
+    _progressOKButton = None
+
+    _sudoDialog = None
+
+    @staticmethod
+    def _createGUI(parentWindow):
+        """Create the GUI elements, if needed."""
+        if Updater._progressWindow is not None:
+            return
+
+        Updater._progressWindow = window = gtk.Window()
+        window.set_title(WINDOW_TITLE_BASE + " " + xstr("update_title"))
+        window.set_transient_for(parentWindow)
+        #win.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
+        window.set_size_request(400, -1)
+        window.set_resizable(False)
+        window.set_modal(True)
+        window.connect("delete-event", lambda a, b: True)
+        window.set_deletable(False)
+        window.set_position(gtk.WindowPosition.CENTER_ON_PARENT if pygobject
+                            else gtk.WIN_POS_CENTER_ON_PARENT)
+
+        mainAlignment = gtk.Alignment(xscale = 1.0)
+        mainAlignment.set_padding(padding_top = 4, padding_bottom = 10,
+                                  padding_left = 16, padding_right = 16)
+        window.add(mainAlignment)
+
+        mainVBox = gtk.VBox()
+        mainAlignment.add(mainVBox)
+
+        labelAlignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
+        Updater._progressLabel = progressLabel = gtk.Label()
+        labelAlignment.add(progressLabel)
+        mainVBox.pack_start(labelAlignment, True, True, 4)
+        
+        Updater._progressBar = progressBar = gtk.ProgressBar()
+        mainVBox.pack_start(progressBar, True, True, 4)
+
+        buttonAlignment = gtk.Alignment(xalign = 0.5, xscale = 0.1)
+        Updater._progressOKButton = progressOKButton = gtk.Button("OK")
+        buttonAlignment.add(progressOKButton)        
+        mainVBox.pack_start(buttonAlignment, True, True, 4)
+
+        Updater._sudoDialog = sudoDialog = \
+            gtk.Dialog(WINDOW_TITLE_BASE + " " + xstr("update_title"),
+                       parentWindow,
+                       gtk.DialogFlags.MODAL if pygobject else gtk.DIALOG_MODAL)
+        sudoDialog.add_button(xstr("button_cancel"), 0)
+        sudoDialog.add_button(xstr("button_ok"), 1)
+                       
+        infoLabelAlignment = gtk.Alignment(xalign = 0.5, xscale = 0.1)
+        infoLabelAlignment.set_padding(padding_top = 4, padding_bottom = 10,
+                                       padding_left = 16, padding_right = 16)
+
+        infoLabel = gtk.Label(xstr("update_needsudo"))
+        infoLabel.set_justify(gtk.Justification.CENTER if pygobject
+                              else gtk.JUSTIFY_CENTER)
+        infoLabelAlignment.add(infoLabel)
+        sudoDialog.vbox.pack_start(infoLabelAlignment, True, True, 4)
+
+        sudoDialog.set_position(gtk.WindowPosition.CENTER_ON_PARENT if pygobject
+                                else gtk.WIN_POS_CENTER_ON_PARENT)
+
+    def __init__(self, gui, programDirectory, updateURL, parentWindow):
+        """Construct the updater. If not created yet, the windows used by the
+        updater are also created."""
+        super(Updater, self).__init__()
+
+        self._gui = gui
+        
+        self._programDirectory = programDirectory
+        self._updateURL = updateURL
+
+        self._totalProgress = 0
+        self._waitAfterFinish = False
+        self._restart = False
+
+        self._sudoReply = None
+        self._sudoCondition = threading.Condition()
+
+        Updater._createGUI(parentWindow)
+
+    def run(self):
+        """Execute the thread's operation."""
+        gobject.idle_add(self._startUpdate)
+        update(self._programDirectory, self._updateURL, self, fromGUI = True)
+        if not self._waitAfterFinish:
+            gobject.idle_add(self._progressWindow.hide)
+            self._gui.updateDone()
+
+    def downloadingManifest(self):
+        """Called when the downloading of the manifest has started."""
+        gobject.idle_add(self._downloadingManifest)
+
+    def _downloadingManifest(self):
+        """Called when the downloading of the manifest has started."""
+        self._progressLabel.set_text(xstr("update_manifest_progress"))
+        self._progressBar.set_fraction(0)
+
+    def downloadedManifest(self):
+        """Called when the downloading of the manifest has finished."""
+        gobject.idle_add(self._downloadedManifest)
+
+    def _downloadedManifest(self):
+        """Called when the downloading of the manifest has finished."""
+        self._progressLabel.set_text(xstr("update_manifest_done"))
+        self._progressBar.set_fraction(0.05)
+
+    def needSudo(self):
+        """Called when the program is interested in whether we want to run a
+        program with administrator rights to do the update."""
+        gobject.idle_add(self._needSudo)
+        with self._sudoCondition:
+            while self._sudoReply is None:
+                self._sudoCondition.wait(1)
+            return self._sudoReply
+
+    def _needSudo(self):
+        """Called when the program is interested in whether we want to run a
+        program with administrator rights to do the update."""
+        self._sudoDialog.show_all()
+        result = self._sudoDialog.run()
+        self._sudoDialog.hide()
+        with self._sudoCondition:
+            self._sudoReply = result!=0
+            self._sudoCondition.notify()
+        
+    def setTotalSize(self, numToModifyAndNew, totalSize, numToRemove,
+                     numToRemoveLocal):
+        """Called when the downloading of the files has started."""
+        self._numToModifyAndNew = numToModifyAndNew
+        self._numModifiedOrNew = 0
+        self._totalSize = totalSize
+        self._downloaded = 0
+        self._numToRemove = numToRemove
+        self._numToRemoveLocal = numToRemoveLocal
+        self._numRemoved = 0
+
+        self._totalProgress = self._totalSize + \
+                              (self._numToModifyAndNew + \
+                               self._numToRemove + self._numToRemoveLocal) * \
+                              Updater.REMOVE2BYTES
+        self._waitAfterFinish = self._totalSize > 0 or self._numToRemove > 0
+
+    def _startDownload(self):
+        """Called when the download has started."""
+        self._progressLabel.set_text(xstr("update_files_progress"))
+
+    def setDownloaded(self, downloaded):
+        """Called when a certain number of bytes are downloaded."""
+        self._downloaded = downloaded
+        gobject.idle_add(self._setDownloaded, downloaded)
+
+    def _setDownloaded(self, downloaded):
+        """Called when a certain number of bytes are downloaded."""
+        self._progressLabel.set_text(xstr("update_files_bytes") % \
+                                     (downloaded, self._totalSize))
+        self._setProgress()
+
+    def startRenaming(self):
+        """Called when the renaming of files has started."""
+        gobject.idle_add(self._startRenaming)
+
+    def _startRenaming(self):
+        """Called when the renaming of files has started."""
+        self._progressLabel.set_text(xstr("update_renaming"))
+
+    def renamed(self, path, count):
+        """Called when a file has been renamed."""
+        self._numModifiedOrNew = count
+        gobject.idle_add(self._renamed, path, count)
+
+    def _renamed(self, path, count):
+        """Called when a file has been renamed."""
+        self._progressLabel.set_text(xstr("update_renamed") % (path,))
+        self._setProgress()
+
+    def startRemoving(self):
+        """Called when the removing of files has started."""
+        gobject.idle_add(self._startRemoving)
+
+    def _startRemoving(self):
+        """Called when the removing of files has started."""
+        self._progressLabel.set_text(xstr("update_removing"))
+
+    def removed(self, path, count):
+        """Called when a file has been removed."""
+        self._numRemoved = count
+        gobject.idle_add(self._removed, path, count)
+
+    def _removed(self, path, count):
+        """Called when a file has been removed."""
+        self._progressLabel.set_text(xstr("update_removed") % (path,))
+        self._setProgress()
+
+    def writingManifest(self):
+        """Called when the writing of the new manifest file has started."""
+        gobject.idle_add(self._writingManifest)
+
+    def _writingManifest(self):
+        """Called when the writing of the new manifest file has started."""
+        self._progressLabel.set_text(xstr("update_writing_manifest"))
+        
+    def done(self):
+        """Called when the update has been done."""
+        gobject.idle_add(self._done)
+        self._restart = self._waitAfterFinish
+
+    def _done(self):
+        """Called when the writing of the new manifest file has started."""
+        self._progressBar.set_fraction(1)
+        if self._totalProgress>0:
+            self._progressLabel.set_text(xstr("update_finished"))
+            self._progressOKButton.set_sensitive(True)
+        else:
+            self._progressLabel.set_text(xstr("update_nothing"))
+        
+    def _setProgress(self):
+        """Set the progress bar based on the current stage."""
+        if self._totalProgress>0:
+            progress = self._downloaded + \
+                       (self._numModifiedOrNew + self._numRemoved) * \
+                       Updater.REMOVE2BYTES
+            self._progressBar.set_fraction(0.05 + 0.94 * progress / self._totalProgress)
+
+    def failed(self, what):
+        """Called when the downloading has failed."""
+        self._waitAfterFinish = True
+        gobject.idle_add(self._failed, what)
+
+    def _failed(self, what):
+        """Called when the downloading has failed."""        
+        self._progressLabel.set_text(xstr("update_failed"))
+        self._progressBar.set_fraction(1)
+        self._progressOKButton.set_sensitive(True)
+
+    def _startUpdate(self):
+        """Start the update.
+
+        It resets the GUI elements."""
+        self._progressLabel.set_text("")
+        self._progressBar.set_fraction(0)
+        self._progressWindow.show_all()
+        self._progressOKButton.set_sensitive(False)
+        self._progressOKButton.connect("clicked", self._progressOKClicked)
+
+    def _progressOKClicked(self, button):
+        """Called when the OK button on the progress window is clicked."""
+        self._progressWindow.hide()
+        if self._restart:
+            self._gui.restart()
+        else:
+            self._gui.updateDone()
+
+#-------------------------------------------------------------------------------
Index: src/mlx/gui/update.py
===================================================================
--- src/mlx/gui/update.py	(revision 755)
+++ 	(revision )
@@ -1,292 +1,0 @@
-
-from mlx.gui.common import *
-
-from mlx.update import update
-from mlx.i18n import xstr
-
-import mlx.const as const
-
-import threading
-import os
-import sys
-
-#-------------------------------------------------------------------------------
-
-## @package mlx.gui.update
-#
-# The GUI part of the update.
-#
-# This module implements the GUI portion of the \ref mlx.update
-# "automatic update" function. The update runs in a separate thread, which puts
-# the relevant events into the Gtk event loop.
-
-#-------------------------------------------------------------------------------
-
-class Updater(threading.Thread):
-    """The updater thread."""
-
-    # The removal or renaming of one file equals to the downloading of this
-    # many bytes in time (for the progress calculation)
-    REMOVE2BYTES = 100
-
-    _progressWindow = None
-
-    _progressLabel = None
-    
-    _progressBar = None
-    
-    _progressOKButton = None
-
-    _sudoDialog = None
-
-    @staticmethod
-    def _createGUI(parentWindow):
-        """Create the GUI elements, if needed."""
-        if Updater._progressWindow is not None:
-            return
-
-        Updater._progressWindow = window = gtk.Window()
-        window.set_title(WINDOW_TITLE_BASE + " " + xstr("update_title"))
-        window.set_transient_for(parentWindow)
-        #win.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
-        window.set_size_request(400, -1)
-        window.set_resizable(False)
-        window.set_modal(True)
-        window.connect("delete-event", lambda a, b: True)
-        window.set_deletable(False)
-        window.set_position(gtk.WindowPosition.CENTER_ON_PARENT if pygobject
-                            else gtk.WIN_POS_CENTER_ON_PARENT)
-
-        mainAlignment = gtk.Alignment(xscale = 1.0)
-        mainAlignment.set_padding(padding_top = 4, padding_bottom = 10,
-                                  padding_left = 16, padding_right = 16)
-        window.add(mainAlignment)
-
-        mainVBox = gtk.VBox()
-        mainAlignment.add(mainVBox)
-
-        labelAlignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
-        Updater._progressLabel = progressLabel = gtk.Label()
-        labelAlignment.add(progressLabel)
-        mainVBox.pack_start(labelAlignment, True, True, 4)
-        
-        Updater._progressBar = progressBar = gtk.ProgressBar()
-        mainVBox.pack_start(progressBar, True, True, 4)
-
-        buttonAlignment = gtk.Alignment(xalign = 0.5, xscale = 0.1)
-        Updater._progressOKButton = progressOKButton = gtk.Button("OK")
-        buttonAlignment.add(progressOKButton)        
-        mainVBox.pack_start(buttonAlignment, True, True, 4)
-
-        Updater._sudoDialog = sudoDialog = \
-            gtk.Dialog(WINDOW_TITLE_BASE + " " + xstr("update_title"),
-                       parentWindow,
-                       gtk.DialogFlags.MODAL if pygobject else gtk.DIALOG_MODAL)
-        sudoDialog.add_button(xstr("button_cancel"), 0)
-        sudoDialog.add_button(xstr("button_ok"), 1)
-                       
-        infoLabelAlignment = gtk.Alignment(xalign = 0.5, xscale = 0.1)
-        infoLabelAlignment.set_padding(padding_top = 4, padding_bottom = 10,
-                                       padding_left = 16, padding_right = 16)
-
-        infoLabel = gtk.Label(xstr("update_needsudo"))
-        infoLabel.set_justify(gtk.Justification.CENTER if pygobject
-                              else gtk.JUSTIFY_CENTER)
-        infoLabelAlignment.add(infoLabel)
-        sudoDialog.vbox.pack_start(infoLabelAlignment, True, True, 4)
-
-        sudoDialog.set_position(gtk.WindowPosition.CENTER_ON_PARENT if pygobject
-                                else gtk.WIN_POS_CENTER_ON_PARENT)
-
-    def __init__(self, gui, programDirectory, updateURL, parentWindow):
-        """Construct the updater. If not created yet, the windows used by the
-        updater are also created."""
-        super(Updater, self).__init__()
-
-        self._gui = gui
-        
-        self._programDirectory = programDirectory
-        self._updateURL = updateURL
-
-        self._totalProgress = 0
-        self._waitAfterFinish = False
-        self._restart = False
-
-        self._sudoReply = None
-        self._sudoCondition = threading.Condition()
-
-        Updater._createGUI(parentWindow)
-
-    def run(self):
-        """Execute the thread's operation."""
-        gobject.idle_add(self._startUpdate)
-        update(self._programDirectory, self._updateURL, self, fromGUI = True)
-        if not self._waitAfterFinish:
-            gobject.idle_add(self._progressWindow.hide)
-            self._gui.updateDone()
-
-    def downloadingManifest(self):
-        """Called when the downloading of the manifest has started."""
-        gobject.idle_add(self._downloadingManifest)
-
-    def _downloadingManifest(self):
-        """Called when the downloading of the manifest has started."""
-        self._progressLabel.set_text(xstr("update_manifest_progress"))
-        self._progressBar.set_fraction(0)
-
-    def downloadedManifest(self):
-        """Called when the downloading of the manifest has finished."""
-        gobject.idle_add(self._downloadedManifest)
-
-    def _downloadedManifest(self):
-        """Called when the downloading of the manifest has finished."""
-        self._progressLabel.set_text(xstr("update_manifest_done"))
-        self._progressBar.set_fraction(0.05)
-
-    def needSudo(self):
-        """Called when the program is interested in whether we want to run a
-        program with administrator rights to do the update."""
-        gobject.idle_add(self._needSudo)
-        with self._sudoCondition:
-            while self._sudoReply is None:
-                self._sudoCondition.wait(1)
-            return self._sudoReply
-
-    def _needSudo(self):
-        """Called when the program is interested in whether we want to run a
-        program with administrator rights to do the update."""
-        self._sudoDialog.show_all()
-        result = self._sudoDialog.run()
-        self._sudoDialog.hide()
-        with self._sudoCondition:
-            self._sudoReply = result!=0
-            self._sudoCondition.notify()
-        
-    def setTotalSize(self, numToModifyAndNew, totalSize, numToRemove,
-                     numToRemoveLocal):
-        """Called when the downloading of the files has started."""
-        self._numToModifyAndNew = numToModifyAndNew
-        self._numModifiedOrNew = 0
-        self._totalSize = totalSize
-        self._downloaded = 0
-        self._numToRemove = numToRemove
-        self._numToRemoveLocal = numToRemoveLocal
-        self._numRemoved = 0
-
-        self._totalProgress = self._totalSize + \
-                              (self._numToModifyAndNew + \
-                               self._numToRemove + self._numToRemoveLocal) * \
-                              Updater.REMOVE2BYTES
-        self._waitAfterFinish = self._totalSize > 0 or self._numToRemove > 0
-
-    def _startDownload(self):
-        """Called when the download has started."""
-        self._progressLabel.set_text(xstr("update_files_progress"))
-
-    def setDownloaded(self, downloaded):
-        """Called when a certain number of bytes are downloaded."""
-        self._downloaded = downloaded
-        gobject.idle_add(self._setDownloaded, downloaded)
-
-    def _setDownloaded(self, downloaded):
-        """Called when a certain number of bytes are downloaded."""
-        self._progressLabel.set_text(xstr("update_files_bytes") % \
-                                     (downloaded, self._totalSize))
-        self._setProgress()
-
-    def startRenaming(self):
-        """Called when the renaming of files has started."""
-        gobject.idle_add(self._startRenaming)
-
-    def _startRenaming(self):
-        """Called when the renaming of files has started."""
-        self._progressLabel.set_text(xstr("update_renaming"))
-
-    def renamed(self, path, count):
-        """Called when a file has been renamed."""
-        self._numModifiedOrNew = count
-        gobject.idle_add(self._renamed, path, count)
-
-    def _renamed(self, path, count):
-        """Called when a file has been renamed."""
-        self._progressLabel.set_text(xstr("update_renamed") % (path,))
-        self._setProgress()
-
-    def startRemoving(self):
-        """Called when the removing of files has started."""
-        gobject.idle_add(self._startRemoving)
-
-    def _startRemoving(self):
-        """Called when the removing of files has started."""
-        self._progressLabel.set_text(xstr("update_removing"))
-
-    def removed(self, path, count):
-        """Called when a file has been removed."""
-        self._numRemoved = count
-        gobject.idle_add(self._removed, path, count)
-
-    def _removed(self, path, count):
-        """Called when a file has been removed."""
-        self._progressLabel.set_text(xstr("update_removed") % (path,))
-        self._setProgress()
-
-    def writingManifest(self):
-        """Called when the writing of the new manifest file has started."""
-        gobject.idle_add(self._writingManifest)
-
-    def _writingManifest(self):
-        """Called when the writing of the new manifest file has started."""
-        self._progressLabel.set_text(xstr("update_writing_manifest"))
-        
-    def done(self):
-        """Called when the update has been done."""
-        gobject.idle_add(self._done)
-        self._restart = self._waitAfterFinish
-
-    def _done(self):
-        """Called when the writing of the new manifest file has started."""
-        self._progressBar.set_fraction(1)
-        if self._totalProgress>0:
-            self._progressLabel.set_text(xstr("update_finished"))
-            self._progressOKButton.set_sensitive(True)
-        else:
-            self._progressLabel.set_text(xstr("update_nothing"))
-        
-    def _setProgress(self):
-        """Set the progress bar based on the current stage."""
-        if self._totalProgress>0:
-            progress = self._downloaded + \
-                       (self._numModifiedOrNew + self._numRemoved) * \
-                       Updater.REMOVE2BYTES
-            self._progressBar.set_fraction(0.05 + 0.94 * progress / self._totalProgress)
-
-    def failed(self, what):
-        """Called when the downloading has failed."""
-        self._waitAfterFinish = True
-        gobject.idle_add(self._failed, what)
-
-    def _failed(self, what):
-        """Called when the downloading has failed."""        
-        self._progressLabel.set_text(xstr("update_failed"))
-        self._progressBar.set_fraction(1)
-        self._progressOKButton.set_sensitive(True)
-
-    def _startUpdate(self):
-        """Start the update.
-
-        It resets the GUI elements."""
-        self._progressLabel.set_text("")
-        self._progressBar.set_fraction(0)
-        self._progressWindow.show_all()
-        self._progressOKButton.set_sensitive(False)
-        self._progressOKButton.connect("clicked", self._progressOKClicked)
-
-    def _progressOKClicked(self, button):
-        """Called when the OK button on the progress window is clicked."""
-        self._progressWindow.hide()
-        if self._restart:
-            self._gui.restart()
-        else:
-            self._gui.updateDone()
-
-#-------------------------------------------------------------------------------
Index: src/mlx/gui/weighthelp.py
===================================================================
--- src/mlx/gui/weighthelp.py	(revision 303)
+++ src/mlx/gui/weighthelp.py	(revision 303)
@@ -0,0 +1,491 @@
+
+from mlx.gui.common import *
+
+from mlx.i18n import xstr
+from mlx.checks import PayloadChecker
+
+#-------------------------------------------------------------------------------
+
+## @package mlx.gui.weighthelp
+#
+# The weight calculation help tab.
+#
+# This module implements the tab containing the weight calculation help. 
+
+#-------------------------------------------------------------------------------
+
+class WeightHelp(gtk.VBox):
+    """The weight calculation help tab."""
+    @staticmethod
+    def _getMarkup(value, expectedValue = None, tolerance = None):
+        """Get the markup for the given value.
+
+        If it is too much different from the expected value, it will be
+        colored yellow (if within the tolerance), or red (if out of the tolerance)."""
+        markup = "%.0f" % (value,)
+        if expectedValue is not None and tolerance is not None:
+            colour = None
+            diff = abs(value - expectedValue)
+            if diff>tolerance: colour = "red"
+            elif (diff*10)>=tolerance: colour = "orange"
+            else: colour = "darkgreen"
+            if colour is not None:
+                markup = '<span foreground="' + colour + '">' + markup + '</span>'
+        return markup
+
+    def __init__(self, gui):
+        """Construct the tab."""
+        super(WeightHelp, self).__init__()
+
+        self._gui = gui
+
+        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                      xscale = 1.0, yscale = 1.0)
+        mainAlignment.set_padding(padding_top = 4, padding_bottom = 4,
+                                  padding_left = 12, padding_right = 12)
+        self.add(mainAlignment)
+
+        self._mainBox = mainBox = gtk.VBox()
+        mainAlignment.add(mainBox)
+
+        self._usingHelp = gtk.CheckButton(xstr("weighthelp_usinghelp"))
+        self._usingHelp.set_use_underline(True)
+        self._usingHelp.set_tooltip_text(xstr("weighthelp_usinghelp_tooltip"))
+        self._usingHelp.connect("toggled", self._usingHelpToggled)
+        mainBox.pack_start(self._usingHelp, False, False, 4)
+
+        
+        self._weightsTable = table = gtk.Table(16, 5)
+        table.set_homogeneous(False)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(table)
+        mainBox.pack_start(alignment, True, True, 4)
+
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.set_padding(padding_bottom = 16, padding_top = 0,
+                              padding_left = 0, padding_right = 0)
+        label = gtk.Label(xstr("weighthelp_header_calculated"))
+        label.set_use_markup(True)
+        # FIXME: should be a constant in common
+        label.set_justify(gtk.Justification.CENTER if pygobject
+                          else gtk.JUSTIFY_CENTER)        
+        alignment.add(label)
+        table.attach(alignment, 1, 2, 0, 1)
+        
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.set_padding(padding_bottom = 16, padding_top = 0,
+                              padding_left = 0, padding_right = 0)
+        button = gtk.Button(xstr("weighthelp_header_simulator"))
+        button.set_tooltip_markup(xstr("weighthelp_header_simulator_tooltip"))
+        button.connect("clicked", self._fsButtonClicked)
+        label = button.get_child()
+        label.set_justify(gtk.Justification.CENTER if pygobject
+                          else gtk.JUSTIFY_CENTER)        
+        alignment.add(button)
+        table.attach(alignment, 3, 4, 0, 1)
+        
+
+        self._crewLabel = gtk.Label(xstr("weighthelp_crew") % ("99",))
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._crewLabel)
+        table.attach(alignment, 0, 1, 1, 2)
+
+        self._crewWeight = gtk.Label("0")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._crewWeight)
+        table.attach(alignment, 1, 2, 1, 2)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 1, 2)
+
+        text = xstr("weighthelp_pax") % ("999",)
+        self._paxLabel = gtk.Label(text)
+        self._paxLabel.set_width_chars(len(text))
+        self._paxLabel.set_alignment(0.0, 0.5)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._paxLabel)
+        table.attach(alignment, 0, 1, 2, 3)
+
+        self._paxWeight = gtk.Label("20000")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._paxWeight)
+        table.attach(alignment, 1, 2, 2, 3)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 2, 3)
+        
+        label = gtk.Label(xstr("weighthelp_baggage"))
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 3, 4)
+
+        self._bagWeight = gtk.Label("2000")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._bagWeight)
+        table.attach(alignment, 1, 2, 3, 4)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 3, 4)
+        
+        label = gtk.Label(xstr("weighthelp_cargo"))
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 4, 5)
+
+        self._cargoWeight = gtk.Label("2000")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._cargoWeight)
+        table.attach(alignment, 1, 2, 4, 5)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 4, 5)
+        
+        label = gtk.Label(xstr("weighthelp_mail"))
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 5, 6)
+
+        self._mailWeight = gtk.Label("2000")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._mailWeight)
+        table.attach(alignment, 1, 2, 5, 6)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 5, 6)
+
+        table.attach(gtk.HSeparator(), 1, 2, 6, 7)
+
+        label = gtk.Label("<b>" + xstr("weighthelp_payload") + "</b>")
+        label.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 7, 8)
+
+        self._payload = gtk.Label("<b>32000</b>")
+        self._payload.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._payload)
+        table.attach(alignment, 1, 2, 7, 8)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 7, 8)
+
+        self._fsPayload = gtk.Label("<b>32001</b>")
+        self._fsPayload.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._fsPayload)
+        table.attach(alignment, 3, 4, 7, 8)
+        
+        table.attach(gtk.Label("kg"), 4, 5, 7, 8)
+
+        label = gtk.Label(xstr("weighthelp_dow"))
+        label.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 8, 9)
+
+        self._dow = gtk.Label("35000")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._dow)
+        table.attach(alignment, 1, 2, 8, 9)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 8, 9)
+
+        self._fsDOW = gtk.Label("33012")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._fsDOW)
+        table.attach(alignment, 3, 4, 8, 9)
+        
+        table.attach(gtk.Label("kg"), 4, 5, 8, 9)
+
+        table.attach(gtk.HSeparator(), 1, 2, 9, 10)
+
+        table.attach(gtk.HSeparator(), 3, 4, 9, 10)
+
+        label = gtk.Label("<b>" + xstr("weighthelp_zfw") + "</b>")
+        label.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 10, 11)
+
+        self._zfw = gtk.Label("<b>122000</b>")
+        self._zfw.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._zfw)
+        table.attach(alignment, 1, 2, 10, 11)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 10, 11)
+
+        self._fsZFW = gtk.Label("<b>124000</b>")
+        self._fsZFW.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._fsZFW)
+        table.attach(alignment, 3, 4, 10, 11)
+        
+        table.attach(gtk.Label("kg"), 4, 5, 10, 11)
+
+        table.attach(gtk.HSeparator(), 0, 5, 11, 12)
+        
+        label = gtk.Label(xstr("weighthelp_gross"))
+        label.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 12, 13)
+
+        self._fsGross = gtk.Label("124000")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._fsGross)
+        table.attach(alignment, 3, 4, 12, 13)
+        
+        table.attach(gtk.Label("kg"), 4, 5, 12, 13)
+
+        label = gtk.Label(xstr("weighthelp_mzfw"))
+        label.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 13, 14)
+
+        self._mzfw = gtk.Label("35000")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._mzfw)
+        table.attach(alignment, 1, 2, 13, 14)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 13, 14)
+
+        label = gtk.Label(xstr("weighthelp_mtow"))
+        label.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 14, 15)
+
+        self._mtow = gtk.Label("35000")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._mtow)
+        table.attach(alignment, 1, 2, 14, 15)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 14, 15)
+
+        label = gtk.Label(xstr("weighthelp_mlw"))
+        label.set_use_markup(True)
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(label)
+        table.attach(alignment, 0, 1, 15, 16)
+
+        self._mlw = gtk.Label("35000")
+        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        alignment.add(self._mlw)
+        table.attach(alignment, 1, 2, 15, 16)
+        
+        table.attach(gtk.Label("kg"), 2, 3, 15, 16)
+
+        self.show_all()
+
+    def disable(self):
+        """Disable the widget."""
+        self._mainBox.set_sensitive(False)
+    
+    def enable(self):
+        """Enable the widget."""
+        self._mainBox.set_sensitive(True)
+    
+    def reset(self):
+        """Reset all calculated and FS data."""
+        
+        self._usingHelp.set_active(False)
+        self._usingHelp.set_sensitive(True)
+        self._weightsTable.set_sensitive(False)
+        
+        self._crew = -1
+        self._pax = -1
+        self._humanWeight = 82.0
+        self._bag = -1
+        self._cargo = -1
+        self._mail = -1        
+        self._dowValue = -1
+        self._mzfwValue = -1
+        self._mtowValue = -1
+        self._mlwValue = -1
+
+        self._fsPayloadValue = -1
+        self._fsDOWValue = -1
+        self._fsZFWValue = -1
+        self._fsGrossValue = -1
+        
+        self._setupCalculated()
+        self._setupFS()
+
+    def _setupCalculated(self):
+        """Setup the labels for the calculated values."""
+        if self._crew<0:
+            self._crewLabel.set_text(xstr("weighthelp_crew") % ("-",))
+            self._crewWeight.set_text("-")
+        else:
+            self._crewLabel.set_text(xstr("weighthelp_crew") % (str(self._crew),))
+            crewWeight = self._crew * self._humanWeight
+            self._crewWeight.set_text("%.0f" % (crewWeight,))
+            
+        if self._pax<0:
+            self._paxLabel.set_text(xstr("weighthelp_pax") % ("-",))
+            self._paxWeight.set_text("-")
+        else:
+            self._paxLabel.set_text(xstr("weighthelp_pax") % (str(self._pax),))
+            paxWeight = self._pax * self._humanWeight
+            self._paxWeight.set_text("%.0f" % (paxWeight,))
+
+        self._setWeightLabel(self._bagWeight, self._bag)
+
+        self._setWeightLabel(self._cargoWeight, self._cargo)
+        
+        self._setWeightLabel(self._mailWeight, self._mail)
+            
+        (payload, zfw) = self._calculateWeights()
+
+        self._setWeightLabel(self._payload, payload, bold = True)
+            
+        self._setWeightLabel(self._dow, self._dowValue)
+
+        if zfw<0:
+            self._zfw.set_text("-")
+        else:
+            markup = "%.0f" % (zfw,)
+            if self._mzfwValue>0 and zfw>self._mzfwValue:
+                markup = '<span foreground="red">' + markup + '</span>'
+            markup = '<b>' + markup + '</b>'
+            self._zfw.set_markup(markup)
+
+        self._setWeightLabel(self._mzfw, self._mzfwValue)
+        
+        self._setWeightLabel(self._mtow, self._mtowValue)
+        
+        self._setWeightLabel(self._mlw, self._mlwValue)
+
+    def _setupFS(self):
+        if self._dowValue<0:
+            self._dow.set_text("-")
+        else:
+            self._dow.set_text("%.0f" % (self._dowValue,))
+
+        """Setup the labels for the FS values."""        
+        (payload, zfw) = self._calculateWeights()
+
+        if self._fsPayloadValue<0:
+            self._fsPayload.set_text("-")
+        else:
+            markup = WeightHelp._getMarkup(self._fsPayloadValue, payload,
+                                           PayloadChecker.TOLERANCE)
+            self._fsPayload.set_markup("<b>" + markup + "</b>")
+
+        if self._fsDOWValue<0:
+            self._fsDOW.set_text("-")
+        else:            
+            markup = WeightHelp._getMarkup(self._fsDOWValue,  self._dowValue,
+                                           PayloadChecker.TOLERANCE)
+            self._fsDOW.set_markup(markup)
+
+        if self._fsZFWValue<0:
+            self._fsZFW.set_text("-")
+        else:
+            markup = WeightHelp._getMarkup(self._fsZFWValue,  zfw,
+                                           PayloadChecker.TOLERANCE)
+            self._fsZFW.set_markup("<b>" + markup + "</b>")
+
+        self._setWeightLabel(self._fsGross, self._fsGrossValue)
+
+    def _calculateWeights(self):
+        """Calculate the payload and the zero-fuel weight.
+
+        It returns a tuple with these two items. If any of the items cannot be
+        calculated, that is -1."""
+        if self._crew<0 or self._pax<0 or \
+               self._bag<0 or self._cargo<0 or self._mail<0:
+            payload = -1
+        else:
+            payload = (self._crew + self._pax) * self._humanWeight + \
+                      self._bag + self._cargo + self._mail
+
+        if payload<0 or self._dowValue<0:
+            zfw = -1
+        else:
+            zfw = payload + self._dowValue
+
+        return (payload, zfw)
+
+    def _usingHelpToggled(self, button):
+        """Called when the Using help button is toggled."""
+        assert self._usingHelp.get_active()
+        self._usingHelp.set_sensitive(False)
+
+        self._gui.logger.untimedMessage("The weight calculation help function was used by the pilot")
+
+        self._crew = self._gui.numCrew
+        self._pax = self._gui.numPassengers
+        self._bag = self._gui.bagWeight
+        self._cargo = self._gui.cargoWeight
+        self._mail = self._gui.mailWeight
+        
+        aircraft = self._gui.flight.aircraft
+        self._humanWeight = aircraft.humanWeight
+        self._dowValue = aircraft.dow
+        self._mzfwValue = aircraft.mzfw
+        self._mtowValue = aircraft.mtow
+        self._mlwValue = aircraft.mlw
+
+        self._setupCalculated()
+        self._weightsTable.set_sensitive(True)
+
+    def _fsButtonClicked(self, button):
+        """Callback for the FS button being clicked."""
+        gui = self._gui
+        gui.beginBusy(xstr("weighthelp_busy"))
+        gui.simulator.requestWeights(self._handleWeights)
+
+    def _handleWeights(self, dow, payload, zfw, grossWeight):
+        """Handle the given weights."""
+        gobject.idle_add(self._processWeights, dow, payload, zfw, grossWeight)
+
+    def _processWeights(self, dow, payload, zfw, grossWeight):
+        """Process the given weights."""
+        self._gui.endBusy()
+        if self._usingHelp.get_active():
+            self._fsPayloadValue = payload
+            self._fsDOWValue = dow
+            self._fsZFWValue = zfw
+            self._fsGrossValue = grossWeight
+            self._setupFS()
+
+    def _setWeightLabel(self, label, weight, bold = False):
+        """Set the given weight label to the given weight."""
+        if weight<0:
+            label.set_text("-")
+        else:
+            markup = "%.0f" % (weight,)
+            if bold: markup = "<b>" + markup + "</b>"
+            label.set_markup(markup)
+                
+        
Index: src/mlx/gui/weighthelp.py
===================================================================
--- src/mlx/gui/weighthelp.py	(revision 303)
+++ 	(revision )
@@ -1,491 +1,0 @@
-
-from mlx.gui.common import *
-
-from mlx.i18n import xstr
-from mlx.checks import PayloadChecker
-
-#-------------------------------------------------------------------------------
-
-## @package mlx.gui.weighthelp
-#
-# The weight calculation help tab.
-#
-# This module implements the tab containing the weight calculation help. 
-
-#-------------------------------------------------------------------------------
-
-class WeightHelp(gtk.VBox):
-    """The weight calculation help tab."""
-    @staticmethod
-    def _getMarkup(value, expectedValue = None, tolerance = None):
-        """Get the markup for the given value.
-
-        If it is too much different from the expected value, it will be
-        colored yellow (if within the tolerance), or red (if out of the tolerance)."""
-        markup = "%.0f" % (value,)
-        if expectedValue is not None and tolerance is not None:
-            colour = None
-            diff = abs(value - expectedValue)
-            if diff>tolerance: colour = "red"
-            elif (diff*10)>=tolerance: colour = "orange"
-            else: colour = "darkgreen"
-            if colour is not None:
-                markup = '<span foreground="' + colour + '">' + markup + '</span>'
-        return markup
-
-    def __init__(self, gui):
-        """Construct the tab."""
-        super(WeightHelp, self).__init__()
-
-        self._gui = gui
-
-        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                      xscale = 1.0, yscale = 1.0)
-        mainAlignment.set_padding(padding_top = 4, padding_bottom = 4,
-                                  padding_left = 12, padding_right = 12)
-        self.add(mainAlignment)
-
-        self._mainBox = mainBox = gtk.VBox()
-        mainAlignment.add(mainBox)
-
-        self._usingHelp = gtk.CheckButton(xstr("weighthelp_usinghelp"))
-        self._usingHelp.set_use_underline(True)
-        self._usingHelp.set_tooltip_text(xstr("weighthelp_usinghelp_tooltip"))
-        self._usingHelp.connect("toggled", self._usingHelpToggled)
-        mainBox.pack_start(self._usingHelp, False, False, 4)
-
-        
-        self._weightsTable = table = gtk.Table(16, 5)
-        table.set_homogeneous(False)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(table)
-        mainBox.pack_start(alignment, True, True, 4)
-
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.0,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.set_padding(padding_bottom = 16, padding_top = 0,
-                              padding_left = 0, padding_right = 0)
-        label = gtk.Label(xstr("weighthelp_header_calculated"))
-        label.set_use_markup(True)
-        # FIXME: should be a constant in common
-        label.set_justify(gtk.Justification.CENTER if pygobject
-                          else gtk.JUSTIFY_CENTER)        
-        alignment.add(label)
-        table.attach(alignment, 1, 2, 0, 1)
-        
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.0,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.set_padding(padding_bottom = 16, padding_top = 0,
-                              padding_left = 0, padding_right = 0)
-        button = gtk.Button(xstr("weighthelp_header_simulator"))
-        button.set_tooltip_markup(xstr("weighthelp_header_simulator_tooltip"))
-        button.connect("clicked", self._fsButtonClicked)
-        label = button.get_child()
-        label.set_justify(gtk.Justification.CENTER if pygobject
-                          else gtk.JUSTIFY_CENTER)        
-        alignment.add(button)
-        table.attach(alignment, 3, 4, 0, 1)
-        
-
-        self._crewLabel = gtk.Label(xstr("weighthelp_crew") % ("99",))
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._crewLabel)
-        table.attach(alignment, 0, 1, 1, 2)
-
-        self._crewWeight = gtk.Label("0")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._crewWeight)
-        table.attach(alignment, 1, 2, 1, 2)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 1, 2)
-
-        text = xstr("weighthelp_pax") % ("999",)
-        self._paxLabel = gtk.Label(text)
-        self._paxLabel.set_width_chars(len(text))
-        self._paxLabel.set_alignment(0.0, 0.5)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._paxLabel)
-        table.attach(alignment, 0, 1, 2, 3)
-
-        self._paxWeight = gtk.Label("20000")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._paxWeight)
-        table.attach(alignment, 1, 2, 2, 3)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 2, 3)
-        
-        label = gtk.Label(xstr("weighthelp_baggage"))
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 3, 4)
-
-        self._bagWeight = gtk.Label("2000")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._bagWeight)
-        table.attach(alignment, 1, 2, 3, 4)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 3, 4)
-        
-        label = gtk.Label(xstr("weighthelp_cargo"))
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 4, 5)
-
-        self._cargoWeight = gtk.Label("2000")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._cargoWeight)
-        table.attach(alignment, 1, 2, 4, 5)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 4, 5)
-        
-        label = gtk.Label(xstr("weighthelp_mail"))
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 5, 6)
-
-        self._mailWeight = gtk.Label("2000")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._mailWeight)
-        table.attach(alignment, 1, 2, 5, 6)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 5, 6)
-
-        table.attach(gtk.HSeparator(), 1, 2, 6, 7)
-
-        label = gtk.Label("<b>" + xstr("weighthelp_payload") + "</b>")
-        label.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 7, 8)
-
-        self._payload = gtk.Label("<b>32000</b>")
-        self._payload.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._payload)
-        table.attach(alignment, 1, 2, 7, 8)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 7, 8)
-
-        self._fsPayload = gtk.Label("<b>32001</b>")
-        self._fsPayload.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._fsPayload)
-        table.attach(alignment, 3, 4, 7, 8)
-        
-        table.attach(gtk.Label("kg"), 4, 5, 7, 8)
-
-        label = gtk.Label(xstr("weighthelp_dow"))
-        label.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 8, 9)
-
-        self._dow = gtk.Label("35000")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._dow)
-        table.attach(alignment, 1, 2, 8, 9)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 8, 9)
-
-        self._fsDOW = gtk.Label("33012")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._fsDOW)
-        table.attach(alignment, 3, 4, 8, 9)
-        
-        table.attach(gtk.Label("kg"), 4, 5, 8, 9)
-
-        table.attach(gtk.HSeparator(), 1, 2, 9, 10)
-
-        table.attach(gtk.HSeparator(), 3, 4, 9, 10)
-
-        label = gtk.Label("<b>" + xstr("weighthelp_zfw") + "</b>")
-        label.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 10, 11)
-
-        self._zfw = gtk.Label("<b>122000</b>")
-        self._zfw.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._zfw)
-        table.attach(alignment, 1, 2, 10, 11)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 10, 11)
-
-        self._fsZFW = gtk.Label("<b>124000</b>")
-        self._fsZFW.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._fsZFW)
-        table.attach(alignment, 3, 4, 10, 11)
-        
-        table.attach(gtk.Label("kg"), 4, 5, 10, 11)
-
-        table.attach(gtk.HSeparator(), 0, 5, 11, 12)
-        
-        label = gtk.Label(xstr("weighthelp_gross"))
-        label.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 12, 13)
-
-        self._fsGross = gtk.Label("124000")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._fsGross)
-        table.attach(alignment, 3, 4, 12, 13)
-        
-        table.attach(gtk.Label("kg"), 4, 5, 12, 13)
-
-        label = gtk.Label(xstr("weighthelp_mzfw"))
-        label.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 13, 14)
-
-        self._mzfw = gtk.Label("35000")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._mzfw)
-        table.attach(alignment, 1, 2, 13, 14)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 13, 14)
-
-        label = gtk.Label(xstr("weighthelp_mtow"))
-        label.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 14, 15)
-
-        self._mtow = gtk.Label("35000")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._mtow)
-        table.attach(alignment, 1, 2, 14, 15)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 14, 15)
-
-        label = gtk.Label(xstr("weighthelp_mlw"))
-        label.set_use_markup(True)
-        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(label)
-        table.attach(alignment, 0, 1, 15, 16)
-
-        self._mlw = gtk.Label("35000")
-        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-        alignment.add(self._mlw)
-        table.attach(alignment, 1, 2, 15, 16)
-        
-        table.attach(gtk.Label("kg"), 2, 3, 15, 16)
-
-        self.show_all()
-
-    def disable(self):
-        """Disable the widget."""
-        self._mainBox.set_sensitive(False)
-    
-    def enable(self):
-        """Enable the widget."""
-        self._mainBox.set_sensitive(True)
-    
-    def reset(self):
-        """Reset all calculated and FS data."""
-        
-        self._usingHelp.set_active(False)
-        self._usingHelp.set_sensitive(True)
-        self._weightsTable.set_sensitive(False)
-        
-        self._crew = -1
-        self._pax = -1
-        self._humanWeight = 82.0
-        self._bag = -1
-        self._cargo = -1
-        self._mail = -1        
-        self._dowValue = -1
-        self._mzfwValue = -1
-        self._mtowValue = -1
-        self._mlwValue = -1
-
-        self._fsPayloadValue = -1
-        self._fsDOWValue = -1
-        self._fsZFWValue = -1
-        self._fsGrossValue = -1
-        
-        self._setupCalculated()
-        self._setupFS()
-
-    def _setupCalculated(self):
-        """Setup the labels for the calculated values."""
-        if self._crew<0:
-            self._crewLabel.set_text(xstr("weighthelp_crew") % ("-",))
-            self._crewWeight.set_text("-")
-        else:
-            self._crewLabel.set_text(xstr("weighthelp_crew") % (str(self._crew),))
-            crewWeight = self._crew * self._humanWeight
-            self._crewWeight.set_text("%.0f" % (crewWeight,))
-            
-        if self._pax<0:
-            self._paxLabel.set_text(xstr("weighthelp_pax") % ("-",))
-            self._paxWeight.set_text("-")
-        else:
-            self._paxLabel.set_text(xstr("weighthelp_pax") % (str(self._pax),))
-            paxWeight = self._pax * self._humanWeight
-            self._paxWeight.set_text("%.0f" % (paxWeight,))
-
-        self._setWeightLabel(self._bagWeight, self._bag)
-
-        self._setWeightLabel(self._cargoWeight, self._cargo)
-        
-        self._setWeightLabel(self._mailWeight, self._mail)
-            
-        (payload, zfw) = self._calculateWeights()
-
-        self._setWeightLabel(self._payload, payload, bold = True)
-            
-        self._setWeightLabel(self._dow, self._dowValue)
-
-        if zfw<0:
-            self._zfw.set_text("-")
-        else:
-            markup = "%.0f" % (zfw,)
-            if self._mzfwValue>0 and zfw>self._mzfwValue:
-                markup = '<span foreground="red">' + markup + '</span>'
-            markup = '<b>' + markup + '</b>'
-            self._zfw.set_markup(markup)
-
-        self._setWeightLabel(self._mzfw, self._mzfwValue)
-        
-        self._setWeightLabel(self._mtow, self._mtowValue)
-        
-        self._setWeightLabel(self._mlw, self._mlwValue)
-
-    def _setupFS(self):
-        if self._dowValue<0:
-            self._dow.set_text("-")
-        else:
-            self._dow.set_text("%.0f" % (self._dowValue,))
-
-        """Setup the labels for the FS values."""        
-        (payload, zfw) = self._calculateWeights()
-
-        if self._fsPayloadValue<0:
-            self._fsPayload.set_text("-")
-        else:
-            markup = WeightHelp._getMarkup(self._fsPayloadValue, payload,
-                                           PayloadChecker.TOLERANCE)
-            self._fsPayload.set_markup("<b>" + markup + "</b>")
-
-        if self._fsDOWValue<0:
-            self._fsDOW.set_text("-")
-        else:            
-            markup = WeightHelp._getMarkup(self._fsDOWValue,  self._dowValue,
-                                           PayloadChecker.TOLERANCE)
-            self._fsDOW.set_markup(markup)
-
-        if self._fsZFWValue<0:
-            self._fsZFW.set_text("-")
-        else:
-            markup = WeightHelp._getMarkup(self._fsZFWValue,  zfw,
-                                           PayloadChecker.TOLERANCE)
-            self._fsZFW.set_markup("<b>" + markup + "</b>")
-
-        self._setWeightLabel(self._fsGross, self._fsGrossValue)
-
-    def _calculateWeights(self):
-        """Calculate the payload and the zero-fuel weight.
-
-        It returns a tuple with these two items. If any of the items cannot be
-        calculated, that is -1."""
-        if self._crew<0 or self._pax<0 or \
-               self._bag<0 or self._cargo<0 or self._mail<0:
-            payload = -1
-        else:
-            payload = (self._crew + self._pax) * self._humanWeight + \
-                      self._bag + self._cargo + self._mail
-
-        if payload<0 or self._dowValue<0:
-            zfw = -1
-        else:
-            zfw = payload + self._dowValue
-
-        return (payload, zfw)
-
-    def _usingHelpToggled(self, button):
-        """Called when the Using help button is toggled."""
-        assert self._usingHelp.get_active()
-        self._usingHelp.set_sensitive(False)
-
-        self._gui.logger.untimedMessage("The weight calculation help function was used by the pilot")
-
-        self._crew = self._gui.numCrew
-        self._pax = self._gui.numPassengers
-        self._bag = self._gui.bagWeight
-        self._cargo = self._gui.cargoWeight
-        self._mail = self._gui.mailWeight
-        
-        aircraft = self._gui.flight.aircraft
-        self._humanWeight = aircraft.humanWeight
-        self._dowValue = aircraft.dow
-        self._mzfwValue = aircraft.mzfw
-        self._mtowValue = aircraft.mtow
-        self._mlwValue = aircraft.mlw
-
-        self._setupCalculated()
-        self._weightsTable.set_sensitive(True)
-
-    def _fsButtonClicked(self, button):
-        """Callback for the FS button being clicked."""
-        gui = self._gui
-        gui.beginBusy(xstr("weighthelp_busy"))
-        gui.simulator.requestWeights(self._handleWeights)
-
-    def _handleWeights(self, dow, payload, zfw, grossWeight):
-        """Handle the given weights."""
-        gobject.idle_add(self._processWeights, dow, payload, zfw, grossWeight)
-
-    def _processWeights(self, dow, payload, zfw, grossWeight):
-        """Process the given weights."""
-        self._gui.endBusy()
-        if self._usingHelp.get_active():
-            self._fsPayloadValue = payload
-            self._fsDOWValue = dow
-            self._fsZFWValue = zfw
-            self._fsGrossValue = grossWeight
-            self._setupFS()
-
-    def _setWeightLabel(self, label, weight, bold = False):
-        """Set the given weight label to the given weight."""
-        if weight<0:
-            label.set_text("-")
-        else:
-            markup = "%.0f" % (weight,)
-            if bold: markup = "<b>" + markup + "</b>"
-            label.set_markup(markup)
-                
-        
