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/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 401)
+++ src/mlx/gui/common.py	(revision 401)
@@ -0,0 +1,331 @@
+
+import mlx.const as _const
+from mlx.i18n import xstr
+
+from mlx.util import secondaryInstallation
+
+import os
+
+#-----------------------------------------------------------------------------
+
+## @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 \ref 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 os.name=="nt" or "FORCE_PYGTK" in os.environ:
+    print "Using PyGTK"
+    pygobject = False
+    import pygtk
+    import gtk.gdk as gdk
+    import gtk
+    import gobject
+    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_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
+
+    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
+
+    POLICY_AUTOMATIC = gtk.POLICY_AUTOMATIC
+
+    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
+
+    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:
+    print "Using PyGObject"
+    pygobject = True
+    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 GObject as gobject
+    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_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
+
+    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
+
+    POLICY_AUTOMATIC = gtk.PolicyType.AUTOMATIC
+
+    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
+
+    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
+
+#------------------------------------------------------------------------------
+
+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_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") }
+
+#------------------------------------------------------------------------------
+
+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)
+
+#------------------------------------------------------------------------------
Index: src/mlx/gui/common.py
===================================================================
--- src/mlx/gui/common.py	(revision 401)
+++ 	(revision )
@@ -1,331 +1,0 @@
-
-import mlx.const as _const
-from mlx.i18n import xstr
-
-from mlx.util import secondaryInstallation
-
-import os
-
-#-----------------------------------------------------------------------------
-
-## @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 \ref 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 os.name=="nt" or "FORCE_PYGTK" in os.environ:
-    print "Using PyGTK"
-    pygobject = False
-    import pygtk
-    import gtk.gdk as gdk
-    import gtk
-    import gobject
-    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_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
-
-    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
-
-    POLICY_AUTOMATIC = gtk.POLICY_AUTOMATIC
-
-    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
-
-    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:
-    print "Using PyGObject"
-    pygobject = True
-    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 GObject as gobject
-    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_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
-
-    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
-
-    POLICY_AUTOMATIC = gtk.PolicyType.AUTOMATIC
-
-    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
-
-    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
-
-#------------------------------------------------------------------------------
-
-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_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") }
-
-#------------------------------------------------------------------------------
-
-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)
-
-#------------------------------------------------------------------------------
Index: src/mlx/gui/flight.py
===================================================================
--- src/mlx/gui/flight.py	(revision 402)
+++ src/mlx/gui/flight.py	(revision 402)
@@ -0,0 +1,3506 @@
+
+from mlx.gui.common import *
+
+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
+import mlx.util as util
+from mlx.pirep import PIREP
+from mlx.i18n import xstr
+from mlx.sound import startSound
+import mlx.web as web
+
+import datetime
+import time
+import os
+
+#-----------------------------------------------------------------------------
+
+## @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.
+
+#-----------------------------------------------------------------------------
+
+class Page(gtk.Alignment):
+    """A page in the flight wizard."""
+    def __init__(self, wizard, 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(completedHelp)
+        # 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._cancelFlightButton = None
+
+        self._completed = False
+        self._fromPage = None
+
+    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):
+        """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:
+            button.connect("clicked", clicked)
+        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."""
+        style = self.get_style() if pygobject else 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 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, xstr("login"),
+                                        xstr("loginHelp"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(4, 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._setControls)
+        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._entranceExam = gtk.CheckButton(xstr("login_entranceExam"))
+        self._entranceExam.set_use_underline(True)
+        self._entranceExam.set_tooltip_text(xstr("login_entranceExam_tooltip"))
+        self._entranceExam.connect("toggled", self._setControls)
+        table.attach(self._entranceExam, 1, 2, 3, 4, ypadding = 12)
+
+        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 entranceExam(self):
+        """Get whether an entrance exam is being performed."""
+        return self._entranceExam.get_active() and \
+               self._pilotID.get_text()!=""
+
+    @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 _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()
+        entranceExam = self._entranceExam.get_active()
+        self._password.set_sensitive(not entranceExam)
+        self._rememberButton.set_sensitive(password!="" and not entranceExam)
+        self._entranceExam.set_sensitive(pilotID!="")
+        self._loginButton.set_sensitive(pilotID!="" and
+                                        (password!="" or entranceExam))
+
+    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: logged in"
+        self._wizard.login(self._handleLoginResult,
+                           self._pilotID.get_text(),
+                           self._password.get_text(),
+                           self.entranceExam)
+
+    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()
+            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, xstr("flightsel_title"),
+                                                  help, completedHelp = completedHelp)
+
+
+        self._listStore = gtk.ListStore(str, str, str, str)
+        self._flightList = gtk.TreeView(self._listStore)
+        column = gtk.TreeViewColumn(xstr("flightsel_no"), gtk.CellRendererText(),
+                                    text = 1)
+        column.set_expand(True)
+        self._flightList.append_column(column)
+        column = gtk.TreeViewColumn(xstr("flightsel_deptime"), gtk.CellRendererText(),
+                                    text = 0)
+        column.set_expand(True)
+        self._flightList.append_column(column)
+        column = gtk.TreeViewColumn(xstr("flightsel_from"), gtk.CellRendererText(),
+                                    text = 2)
+        column.set_expand(True)
+        self._flightList.append_column(column)
+        column = gtk.TreeViewColumn(xstr("flightsel_to"), gtk.CellRendererText(),
+                                    text = 3)
+        column.set_expand(True)
+        self._flightList.append_column(column)
+        self._flightList.connect("row-activated", self._rowActivated)
+        self._flightList.connect("button-press-event", self._listButtonPressed)
+
+        self._flightListPopupMenu = None
+
+        flightSelection = self._flightList.get_selection()
+        flightSelection.connect("changed", self._selectionChanged)
+
+        scrolledWindow = gtk.ScrolledWindow()
+        scrolledWindow.add(self._flightList)
+        scrolledWindow.set_size_request(400, -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)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
+        alignment.add(scrolledWindow)
+
+        self.setMainWidget(alignment)
+
+        self._saveButton = self.addButton(xstr("flightsel_save"),
+                                          sensitive = False,
+                                          clicked = self._saveClicked,
+                                          tooltip = xstr("flightsel_save_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 = []
+
+    def activate(self):
+        """Fill the flight list."""
+        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 _buildFlights(self):
+        """Rebuild the flights from the login result."""
+        self._flights = []
+        self._listStore.clear()
+        if self._wizard.loggedIn:
+            for flight in self._wizard.loginResult.flights:
+                self._addFlight(flight)
+
+    def _addFlight(self, flight):
+        """Add the given file to the list of flights."""
+        self._flights.append(flight)
+        self._listStore.append([str(flight.departureTime),
+                                flight.callsign,
+                                flight.departureICAO,
+                                flight.arrivalICAO])
+
+    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 _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 and result.loggedIn:
+            self._buildFlights()
+
+    def _selectionChanged(self, selection):
+        """Called when the selection is changed."""
+        selected = selection.count_selected_rows()==1
+        self._saveButton.set_sensitive(selected)
+        self._button.set_sensitive(selected)
+
+    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._nextDistance, finalize = False)
+        else:
+            self._flightSelected()
+
+    def _rowActivated(self, flightList, path, column):
+        """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()
+
+        self._updateDepartureGate()
+
+    def _getSelectedFlight(self):
+        """Get the currently selected flight."""
+        selection = self._flightList.get_selection()
+        (listStore, iter) = selection.get_selected()
+        path = listStore.get_path(iter)
+        [index] = path.get_indices() if pygobject else path
+
+        return self._flights[index]
+
+    def _listButtonPressed(self, widget, event):
+        """Called when a mouse button is pressed on the flight list."""
+        if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
+            return
+
+        (path, _, _, _) = self._flightList.get_path_at_pos(int(event.x),
+                                                           int(event.y))
+        selection = self._flightList.get_selection()
+        selection.unselect_all()
+        selection.select_path(path)
+
+        menu = self._getListPopupMenu()
+        if pygobject:
+            menu.popup(None, None, None, None, event.button, event.time)
+        else:
+            menu.popup(None, None, None, event.button, event.time)
+
+    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._nextDistance = 2
+            self._wizard.jumpPage(2)
+
+    def _fleetRetrieved(self, fleet):
+        """Called when the fleet has been retrieved."""
+        if fleet is None:
+            self._nextDistance = 2
+            self._wizard.jumpPage(2)
+        else:
+            plane = fleet[self._wizard._bookedFlight.tailNumber]
+            if plane is None:
+                self._nextDistance = 2
+                self._wizard.jumpPage(2)
+            elif plane.gateNumber is not None and \
+                 not fleet.isGateConflicting(plane):
+                self._wizard._departureGate = plane.gateNumber
+                self._nextDistance = 2
+                self._wizard.jumpPage(2)
+            else:
+                self._nextDistance = 1
+                self._wizard.nextPage()
+
+    def _planeUpdated(self, success):
+        """Callback for the plane updating."""
+        self._nextDistance = 2
+        self._wizard.jumpPage(2)
+
+    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 _getListPopupMenu(self):
+        """Get the flight list popup menu."""
+        if self._flightListPopupMenu is None:
+            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)
+
+            self._flightListPopupMenu = menu
+
+        return self._flightListPopupMenu
+
+    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()
+
+#-----------------------------------------------------------------------------
+
+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, 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 gateNumber in const.lhbpGateNumbers:
+            if gateNumber not in occupiedGateNumbers:
+                self._listStore.append([gateNumber])
+
+    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.nextPage()
+
+    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.nextPage()
+        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.nextPage()
+        else:
+            self.activate()
+
+#-----------------------------------------------------------------------------
+
+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, xstr("connect_title"),
+                                          xstr("connect_help"),
+                                          completedHelp = xstr("connect_chelp"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(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)
+
+        self.addCancelFlightButton()
+
+        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)
+
+    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."""
+        self._wizard._connectSimulator()
+
+    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, 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, 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 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, xstr("fuel_title"),
+                                       xstr("fuel_help"),
+                                       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)
+
+    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()
+        else:
+            self._wizard.nextPage()
+
+    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()
+            self._wizard.nextPage()
+        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)
+
+#-----------------------------------------------------------------------------
+
+class RoutePage(Page):
+    """The page containing the route and the flight level."""
+    def __init__(self, wizard):
+        """Construct the page."""
+        super(RoutePage, self).__init__(wizard, 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 = 50, max = 500)
+        self._cruiseLevel.set_tooltip_text(xstr("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)
+
+        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)
+
+        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()
+
+    def activate(self):
+        """Setup the route from the booked flight."""
+        self._cruiseLevel.set_value(240)
+        self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
+        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."""
+        self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
+                                   self._getRoute()!="")
+
+    def _cruiseLevelChanged(self, spinButton):
+        """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 _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._completed:
+            self._wizard.nextPage()
+        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)
+
+    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
+
+        title = xstr("briefing_title") % (1 if departure else 2,
+                                          xstr("briefing_departure")
+                                          if departure
+                                          else xstr("briefing_arrival"))
+        super(BriefingPage, self).__init__(wizard, 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._uppercasingMETAR = 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 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.begin)
+                if notam.end is not None:
+                    s += " - " + str(notam.end)
+                elif notam.permanent:
+                    s += " - PERMANENT"
+                s += "\n"
+                if notam.repeatCycle:
+                    s += "Repeat cycle: " + notam.repeatCycle + "\n"
+                s += notam.notice + "\n"
+                s += "-------------------- * --------------------\n"
+            buffer.set_text(s)
+
+        self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
+        buffer = self._metar.get_buffer()
+        if metar is None:
+            buffer.set_text(xstr("briefing_metar_failed"))
+        else:
+            buffer.set_text(metar)
+
+        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."""
+        if not self._uppercasingMETAR:
+            self.metarEdited = True
+            self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
+                                                       buffer.get_end_iter(),
+                                                       True)!="")
+
+    def _metarInserted(self, textBuffer, iter, text, length):
+        """Called when new characters are inserted into the METAR.
+
+        It uppercases all characters."""
+        if not self._uppercasingMETAR:
+            self._uppercasingMETAR = True
+
+            iter1 = iter.copy()
+            iter1.backward_chars(length)
+            textBuffer.delete(iter, iter1)
+
+            textBuffer.insert(iter, text.upper())
+
+            self._uppercasingMETAR = False
+
+#-----------------------------------------------------------------------------
+
+class TakeoffPage(Page):
+    """Page for entering the takeoff data."""
+    def __init__(self, wizard):
+        """Construct the takeoff page."""
+        super(TakeoffPage, self).__init__(wizard, 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(8, 4)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        label = gtk.Label(xstr("takeoff_runway"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 0, 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, 0, 1)
+        label.set_mnemonic_widget(self._runway)
+
+        label = gtk.Label(xstr("takeoff_sid"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 0, 1, 1, 2)
+
+        self._sid = gtk.Entry()
+        self._sid.set_width_chars(10)
+        self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
+        self._sid.connect("changed", self._upperChanged)
+        table.attach(self._sid, 1, 3, 1, 2)
+        label.set_mnemonic_widget(self._sid)
+
+        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, 2, 3)
+
+        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, 2, 3)
+        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, 2, 3)
+
+        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, 3, 4)
+
+        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, 3, 4)
+        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, 3, 4)
+
+        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, 4, 5)
+
+        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, 4, 5)
+        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, 4, 5)
+
+        self._hasDerate = False
+
+        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, 5, 6)
+
+        self._derate = gtk.Entry()
+        self._derate.set_width_chars(10)
+        self._derate.set_tooltip_text(xstr("takeoff_derate_tooltip"))
+        self._derate.set_alignment(1.0)
+        self._derate.connect("changed", self._derateChanged)
+        table.attach(self._derate, 1, 3, 5, 6)
+        self._derateLabel.set_mnemonic_widget(self._derate)
+
+        self._derateUnit = gtk.Label("")
+        self._derateUnit.set_use_markup(True)
+        self._derateUnit.set_alignment(0.0, 0.5)
+        table.attach(self._derateUnit, 3, 4, 5, 6)
+
+        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, 6, 7)
+
+        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, 7, 8, ypadding = 8)
+
+        self.addCancelFlightButton()
+
+        self.addPreviousButton(clicked = self._backClicked)
+
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+    @property
+    def runway(self):
+        """Get the runway."""
+        return self._runway.get_text()
+
+    @property
+    def sid(self):
+        """Get the SID."""
+        return self._sid.get_text()
+
+    @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._hasDerate:
+            derate = self._derate.get_text()
+            return derate if derate else None
+        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."""
+        self._runway.set_text("")
+        self._runway.set_sensitive(True)
+        self._sid.set_text("")
+        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))
+
+        (derateLabel, derateUnit) = \
+             self._wizard.gui.flight.aircraft.derateLabels
+
+        self._hasDerate = derateLabel is not None
+
+        if self._hasDerate:
+            self._derateLabel.set_markup(derateLabel)
+            self._derateLabel.set_use_underline(True)
+            self._derateUnit.set_markup("" if derateUnit is None
+                                        else derateUnit)
+        else:
+            self._derateLabel.set_markup(xstr("takeoff_derate"))
+            self._derateUnit.set_text("")
+
+        self._derate.set_text("")
+
+        self._derateLabel.set_sensitive(self._hasDerate)
+        self._derate.set_sensitive(self._hasDerate)
+        self._derateUnit.set_sensitive(self._hasDerate)
+
+        self._rto.set_active(False)
+        self._rto.set_sensitive(False)
+
+        self._button.set_sensitive(False)
+        self._forwardAllowed = False
+
+    def allowForward(self):
+        """Allow going to the next page."""
+        self._forwardAllowed = True
+        self._updateForwardButton()
+
+    def reset(self):
+        """Reset the page if the wizard is reset."""
+        super(TakeoffPage, self).reset()
+        self._v1.reset()
+        self._vr.reset()
+        self._v2.reset()
+        self._hasDerate = False
+        self._antiIceOn.set_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 _updateForwardButton(self):
+        """Update the sensitivity of the forward button based on some conditions."""
+        sensitive = self._forwardAllowed and \
+                    self._runway.get_text()!="" and \
+                    self._sid.get_text()!="" 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 \
+                    (not self._hasDerate or self._derate.get_text()!="")
+        self._button.set_sensitive(sensitive)
+
+    def _valueChanged(self, widget, arg = None):
+        """Called when the value of some widget has changed."""
+        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."""
+        entry.set_text(entry.get_text().upper())
+        self._valueChanged(entry, arg)
+
+    def _derateChanged(self, entry):
+        """Called when the value of the derate is changed."""
+        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._hasDerate:
+            aircraft.updateDerate()
+        aircraft.updateTakeoffAntiIce()
+        self._wizard.nextPage()
+
+#-----------------------------------------------------------------------------
+
+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, 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, 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(6, 5)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        self._starButton = gtk.CheckButton()
+        self._starButton.connect("clicked", self._starButtonClicked)
+        table.attach(self._starButton, 0, 1, 0, 1)
+
+        label = gtk.Label(xstr("landing_star"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, 0, 1)
+
+        self._star = gtk.Entry()
+        self._star.set_width_chars(10)
+        self._star.set_tooltip_text(xstr("landing_star_tooltip"))
+        self._star.connect("changed", self._upperChanged)
+        self._star.set_sensitive(False)
+        table.attach(self._star, 2, 4, 0, 1)
+        label.set_mnemonic_widget(self._starButton)
+
+        self._transitionButton = gtk.CheckButton()
+        self._transitionButton.connect("clicked", self._transitionButtonClicked)
+        table.attach(self._transitionButton, 0, 1, 1, 2)
+
+        label = gtk.Label(xstr("landing_transition"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, 1, 2)
+
+        self._transition = gtk.Entry()
+        self._transition.set_width_chars(10)
+        self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
+        self._transition.connect("changed", self._upperChanged)
+        self._transition.set_sensitive(False)
+        table.attach(self._transition, 2, 4, 1, 2)
+        label.set_mnemonic_widget(self._transitionButton)
+
+        label = gtk.Label(xstr("landing_runway"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, 2, 3)
+
+        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, 2, 3)
+        label.set_mnemonic_widget(self._runway)
+
+        label = gtk.Label(xstr("landing_approach"))
+        label.set_use_underline(True)
+        label.set_alignment(0.0, 0.5)
+        table.attach(label, 1, 2, 3, 4)
+
+        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, 3, 4)
+        label.set_mnemonic_widget(self._approachType)
+
+        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, 4, 5)
+
+        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, 4, 5)
+        label.set_mnemonic_widget(self._vref)
+
+        self._vrefUnit = gtk.Label(xstr("label_knots"))
+        table.attach(self._vrefUnit, 4, 5, 4, 5)
+
+        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, 5, 6)
+
+        self.addCancelFlightButton()
+
+        self.addPreviousButton(clicked = self._backClicked)
+
+        self._button = self.addNextButton(clicked = self._forwardClicked)
+
+        # These are needed for correct size calculations
+        self._starButton.set_active(True)
+        self._transitionButton.set_active(True)
+
+    @property
+    def star(self):
+        """Get the STAR or None if none entered."""
+        return self._star.get_text() if self._starButton.get_active() else None
+
+    @property
+    def transition(self):
+        """Get the transition or None if none entered."""
+        return self._transition.get_text() \
+               if self._transitionButton.get_active() 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
+
+    def activate(self):
+        """Called when the page is activated."""
+        self._starButton.set_sensitive(True)
+        self._starButton.set_active(False)
+        self._star.set_text("")
+
+        self._transitionButton.set_sensitive(True)
+        self._transitionButton.set_active(False)
+        self._transition.set_text("")
+
+        self._runway.set_text("")
+        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()
+
+    def flightEnded(self):
+        """Called when the flight has ended."""
+        super(LandingPage, self).flightEnded()
+        self._flightEnded = True
+        self._updateForwardButton()
+
+    def _starButtonClicked(self, button):
+        """Called when the STAR button is clicked."""
+        active = button.get_active()
+        self._star.set_sensitive(active)
+        if active:
+            self._star.grab_focus()
+        self._updateForwardButton()
+
+    def _transitionButtonClicked(self, button):
+        """Called when the Transition button is clicked."""
+        active = button.get_active()
+        self._transition.set_sensitive(active)
+        if active:
+            self._transition.grab_focus()
+        self._updateForwardButton()
+
+    def _updateForwardButton(self):
+        """Update the sensitivity of the forward button."""
+        sensitive = self._flightEnded and \
+                    (self._starButton.get_active() or \
+                     self._transitionButton.get_active()) and \
+                    (self._star.get_text()!="" or
+                     not self._starButton.get_active()) and \
+                    (self._transition.get_text()!="" or
+                     not self._transitionButton.get_active()) 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 _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."""
+        aircraft = self._wizard.gui.flight.aircraft
+        aircraft.updateVRef()
+        aircraft.updateLandingAntiIce()
+        if self._wizard.gui.config.onlineGateSystem and \
+           self._wizard.loggedIn and not self._completed and \
+           self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
+           not self._wizard.entranceExam:
+            self._wizard.getFleet(callback = self._fleetRetrieved,
+                                  force = True)
+        else:
+            self._wizard.nextPage()
+
+    def _fleetRetrieved(self, fleet):
+        """Callback for the fleet retrieval."""
+        self._wizard.nextPage()
+
+#-----------------------------------------------------------------------------
+
+class FinishPage(Page):
+    """Flight finish page."""
+    _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
+                     ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
+                     ("flighttype_vip", const.FLIGHTTYPE_VIP),
+                     ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
+
+    def __init__(self, wizard):
+        """Construct the finish page."""
+        super(FinishPage, self).__init__(wizard, xstr("finish_title"),
+                                         xstr("finish_help"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(8, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(False)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_rating"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 0, 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, 0, 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, 1, 2)
+
+        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, 1, 2)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_block_time"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 2, 3)
+
+        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, 2, 3)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_distance"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 3, 4)
+
+        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, 3, 4)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label(xstr("finish_fuel"))
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 4, 5)
+
+        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, 4, 5)
+
+        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, 5, 6)
+
+        flightTypeModel = gtk.ListStore(str, int)
+        for (name, type) in FinishPage._flightTypes:
+            flightTypeModel.append([xstr(name), type])
+
+        self._flightType = gtk.ComboBox(model = flightTypeModel)
+        renderer = gtk.CellRendererText()
+        self._flightType.pack_start(renderer, True)
+        self._flightType.add_attribute(renderer, "text", 0)
+        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, 5, 6)
+        label.set_mnemonic_widget(self._flightType)
+
+        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, 6, 7)
+
+        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, 7, 8)
+
+        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, 7, 8)
+        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._saveButton = self.addButton(xstr("finish_save"),
+                                          sensitive = False,
+                                          clicked = self._saveClicked,
+                                          tooltip = xstr("finish_save_tooltip"))
+        self._savePIREPDialog = None
+        self._lastSavePath = None
+
+        self._pirepSaved = False
+        self._pirepSent = False
+
+        self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
+                                          sensitive = False,
+                                          clicked = self._sendClicked,
+                                          tooltip = xstr("sendPIREP_tooltip"))
+
+    @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._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:
+            occupiedGates = self._wizard._fleet.getOccupiedGateNumbers()
+            for gateNumber in const.lhbpGateNumbers:
+                if gateNumber not in occupiedGates:
+                    self._gatesModel.append([gateNumber])
+            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)
+
+    def _backClicked(self, button):
+        """Called when the Back button is pressed."""
+        self.goBack()
+
+    def _updateButtons(self):
+        """Update the sensitivity state of the buttons."""
+        sensitive = self._flightType.get_active()>=0 and \
+                    (self._gatesModel.get_iter_first() is None or
+                     self._gate.get_active()>=0)
+
+        wasSensitive = self._saveButton.get_sensitive()
+
+        gui = self._wizard.gui
+        config = gui.config
+        if config.pirepAutoSave and sensitive and not wasSensitive:
+            self._lastSavePath = os.path.join(config.pirepDirectory,
+                                              self._getDefaultPIREPName())
+            self._lastSavePath = text2unicode(self._lastSavePath)
+            self._savePIREP(automatic = True)
+
+        self._saveButton.set_sensitive(sensitive)
+        self._sendButton.set_sensitive(sensitive and
+                                       self._wizard.bookedFlight.id is not None)
+
+    def _flightTypeChanged(self, comboBox):
+        """Called when the flight type has changed."""
+        self._updateButtons()
+
+    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 _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):
+        """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()
+
+    def _savePIREP(self, 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
+            self._pirepSaved = True
+
+        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
+
+
+    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
+
+#-----------------------------------------------------------------------------
+
+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._pages.append(FlightSelectionPage(self))
+        self._pages.append(GateSelectionPage(self))
+        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._pages.append(FuelPage(self))
+        self._routePage = RoutePage(self)
+        self._pages.append(self._routePage)
+        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)
+        self._finishPage = FinishPage(self)
+        self._pages.append(self._finishPage)
+
+        maxWidth = 0
+        maxHeight = 0
+        for page in self._pages:
+            page.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)
+            page.setStyle()
+        maxWidth += 16
+        maxHeight += 32
+        self.set_size_request(maxWidth, maxHeight)
+
+        self._initialize()
+
+    @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._loginPage.entranceExam
+
+    @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
+
+    def setCurrentPage(self, index, finalize = False):
+        """Set the current page to the one with the given index."""
+        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)
+
+        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 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
+
+    def nextPage(self, finalize = True):
+        """Go to the next page."""
+        self.jumpPage(1, finalize)
+
+    def jumpPage(self, count, finalize = True):
+        """Go to the page which is 'count' pages after the current one."""
+        self.setCurrentPage(self._currentPage + count, finalize = finalize)
+
+    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
+
+        firstPage = 0 if self._loginResult is None else 1
+        for page in self._pages[firstPage:]:
+            page.reset()
+
+        self.setCurrentPage(firstPage)
+
+    def login(self, callback, pilotID, password, entranceExam):
+        """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
+            entranceExam = loginResult.entranceExam
+            busyMessage = xstr("reload_busy")
+        else:
+            self._loginResult = None
+            busyMessage = xstr("login_busy")
+
+        self.gui.beginBusy(busyMessage)
+
+        self.gui.webHandler.login(self._loginResultCallback,
+                                  pilotID, password,
+                                  entranceExam = entranceExam)
+
+    def reloadFlights(self, callback):
+        """Reload the flights from the MAVA server."""
+        self.login(callback, None, None, None)
+
+    def cruiseLevelChanged(self):
+        """Called when the cruise level is changed."""
+        return self.gui.cruiseLevelChanged()
+
+    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
+            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 rtoToggled(self, indicated):
+        """Called when the RTO indication has changed."""
+        self.gui.rtoToggled(indicated)
+
+    def _connectSimulator(self):
+        """Connect to the simulator."""
+        self.gui.connectSimulator(self._bookedFlight.aircraftType)
+
+    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)
+
+#-----------------------------------------------------------------------------
Index: src/mlx/gui/flight.py
===================================================================
--- src/mlx/gui/flight.py	(revision 402)
+++ 	(revision )
@@ -1,3506 +1,0 @@
-
-from mlx.gui.common import *
-
-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
-import mlx.util as util
-from mlx.pirep import PIREP
-from mlx.i18n import xstr
-from mlx.sound import startSound
-import mlx.web as web
-
-import datetime
-import time
-import os
-
-#-----------------------------------------------------------------------------
-
-## @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.
-
-#-----------------------------------------------------------------------------
-
-class Page(gtk.Alignment):
-    """A page in the flight wizard."""
-    def __init__(self, wizard, 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(completedHelp)
-        # 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._cancelFlightButton = None
-
-        self._completed = False
-        self._fromPage = None
-
-    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):
-        """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:
-            button.connect("clicked", clicked)
-        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."""
-        style = self.get_style() if pygobject else 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 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, xstr("login"),
-                                        xstr("loginHelp"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(4, 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._setControls)
-        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._entranceExam = gtk.CheckButton(xstr("login_entranceExam"))
-        self._entranceExam.set_use_underline(True)
-        self._entranceExam.set_tooltip_text(xstr("login_entranceExam_tooltip"))
-        self._entranceExam.connect("toggled", self._setControls)
-        table.attach(self._entranceExam, 1, 2, 3, 4, ypadding = 12)
-
-        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 entranceExam(self):
-        """Get whether an entrance exam is being performed."""
-        return self._entranceExam.get_active() and \
-               self._pilotID.get_text()!=""
-
-    @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 _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()
-        entranceExam = self._entranceExam.get_active()
-        self._password.set_sensitive(not entranceExam)
-        self._rememberButton.set_sensitive(password!="" and not entranceExam)
-        self._entranceExam.set_sensitive(pilotID!="")
-        self._loginButton.set_sensitive(pilotID!="" and
-                                        (password!="" or entranceExam))
-
-    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: logged in"
-        self._wizard.login(self._handleLoginResult,
-                           self._pilotID.get_text(),
-                           self._password.get_text(),
-                           self.entranceExam)
-
-    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()
-            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, xstr("flightsel_title"),
-                                                  help, completedHelp = completedHelp)
-
-
-        self._listStore = gtk.ListStore(str, str, str, str)
-        self._flightList = gtk.TreeView(self._listStore)
-        column = gtk.TreeViewColumn(xstr("flightsel_no"), gtk.CellRendererText(),
-                                    text = 1)
-        column.set_expand(True)
-        self._flightList.append_column(column)
-        column = gtk.TreeViewColumn(xstr("flightsel_deptime"), gtk.CellRendererText(),
-                                    text = 0)
-        column.set_expand(True)
-        self._flightList.append_column(column)
-        column = gtk.TreeViewColumn(xstr("flightsel_from"), gtk.CellRendererText(),
-                                    text = 2)
-        column.set_expand(True)
-        self._flightList.append_column(column)
-        column = gtk.TreeViewColumn(xstr("flightsel_to"), gtk.CellRendererText(),
-                                    text = 3)
-        column.set_expand(True)
-        self._flightList.append_column(column)
-        self._flightList.connect("row-activated", self._rowActivated)
-        self._flightList.connect("button-press-event", self._listButtonPressed)
-
-        self._flightListPopupMenu = None
-
-        flightSelection = self._flightList.get_selection()
-        flightSelection.connect("changed", self._selectionChanged)
-
-        scrolledWindow = gtk.ScrolledWindow()
-        scrolledWindow.add(self._flightList)
-        scrolledWindow.set_size_request(400, -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)
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
-        alignment.add(scrolledWindow)
-
-        self.setMainWidget(alignment)
-
-        self._saveButton = self.addButton(xstr("flightsel_save"),
-                                          sensitive = False,
-                                          clicked = self._saveClicked,
-                                          tooltip = xstr("flightsel_save_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 = []
-
-    def activate(self):
-        """Fill the flight list."""
-        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 _buildFlights(self):
-        """Rebuild the flights from the login result."""
-        self._flights = []
-        self._listStore.clear()
-        if self._wizard.loggedIn:
-            for flight in self._wizard.loginResult.flights:
-                self._addFlight(flight)
-
-    def _addFlight(self, flight):
-        """Add the given file to the list of flights."""
-        self._flights.append(flight)
-        self._listStore.append([str(flight.departureTime),
-                                flight.callsign,
-                                flight.departureICAO,
-                                flight.arrivalICAO])
-
-    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 _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 and result.loggedIn:
-            self._buildFlights()
-
-    def _selectionChanged(self, selection):
-        """Called when the selection is changed."""
-        selected = selection.count_selected_rows()==1
-        self._saveButton.set_sensitive(selected)
-        self._button.set_sensitive(selected)
-
-    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._nextDistance, finalize = False)
-        else:
-            self._flightSelected()
-
-    def _rowActivated(self, flightList, path, column):
-        """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()
-
-        self._updateDepartureGate()
-
-    def _getSelectedFlight(self):
-        """Get the currently selected flight."""
-        selection = self._flightList.get_selection()
-        (listStore, iter) = selection.get_selected()
-        path = listStore.get_path(iter)
-        [index] = path.get_indices() if pygobject else path
-
-        return self._flights[index]
-
-    def _listButtonPressed(self, widget, event):
-        """Called when a mouse button is pressed on the flight list."""
-        if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
-            return
-
-        (path, _, _, _) = self._flightList.get_path_at_pos(int(event.x),
-                                                           int(event.y))
-        selection = self._flightList.get_selection()
-        selection.unselect_all()
-        selection.select_path(path)
-
-        menu = self._getListPopupMenu()
-        if pygobject:
-            menu.popup(None, None, None, None, event.button, event.time)
-        else:
-            menu.popup(None, None, None, event.button, event.time)
-
-    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._nextDistance = 2
-            self._wizard.jumpPage(2)
-
-    def _fleetRetrieved(self, fleet):
-        """Called when the fleet has been retrieved."""
-        if fleet is None:
-            self._nextDistance = 2
-            self._wizard.jumpPage(2)
-        else:
-            plane = fleet[self._wizard._bookedFlight.tailNumber]
-            if plane is None:
-                self._nextDistance = 2
-                self._wizard.jumpPage(2)
-            elif plane.gateNumber is not None and \
-                 not fleet.isGateConflicting(plane):
-                self._wizard._departureGate = plane.gateNumber
-                self._nextDistance = 2
-                self._wizard.jumpPage(2)
-            else:
-                self._nextDistance = 1
-                self._wizard.nextPage()
-
-    def _planeUpdated(self, success):
-        """Callback for the plane updating."""
-        self._nextDistance = 2
-        self._wizard.jumpPage(2)
-
-    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 _getListPopupMenu(self):
-        """Get the flight list popup menu."""
-        if self._flightListPopupMenu is None:
-            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)
-
-            self._flightListPopupMenu = menu
-
-        return self._flightListPopupMenu
-
-    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()
-
-#-----------------------------------------------------------------------------
-
-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, 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 gateNumber in const.lhbpGateNumbers:
-            if gateNumber not in occupiedGateNumbers:
-                self._listStore.append([gateNumber])
-
-    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.nextPage()
-
-    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.nextPage()
-        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.nextPage()
-        else:
-            self.activate()
-
-#-----------------------------------------------------------------------------
-
-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, xstr("connect_title"),
-                                          xstr("connect_help"),
-                                          completedHelp = xstr("connect_chelp"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(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)
-
-        self.addCancelFlightButton()
-
-        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)
-
-    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."""
-        self._wizard._connectSimulator()
-
-    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, 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, 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 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, xstr("fuel_title"),
-                                       xstr("fuel_help"),
-                                       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)
-
-    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()
-        else:
-            self._wizard.nextPage()
-
-    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()
-            self._wizard.nextPage()
-        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)
-
-#-----------------------------------------------------------------------------
-
-class RoutePage(Page):
-    """The page containing the route and the flight level."""
-    def __init__(self, wizard):
-        """Construct the page."""
-        super(RoutePage, self).__init__(wizard, 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 = 50, max = 500)
-        self._cruiseLevel.set_tooltip_text(xstr("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)
-
-        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)
-
-        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()
-
-    def activate(self):
-        """Setup the route from the booked flight."""
-        self._cruiseLevel.set_value(240)
-        self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
-        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."""
-        self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
-                                   self._getRoute()!="")
-
-    def _cruiseLevelChanged(self, spinButton):
-        """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 _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._completed:
-            self._wizard.nextPage()
-        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)
-
-    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
-
-        title = xstr("briefing_title") % (1 if departure else 2,
-                                          xstr("briefing_departure")
-                                          if departure
-                                          else xstr("briefing_arrival"))
-        super(BriefingPage, self).__init__(wizard, 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._uppercasingMETAR = 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 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.begin)
-                if notam.end is not None:
-                    s += " - " + str(notam.end)
-                elif notam.permanent:
-                    s += " - PERMANENT"
-                s += "\n"
-                if notam.repeatCycle:
-                    s += "Repeat cycle: " + notam.repeatCycle + "\n"
-                s += notam.notice + "\n"
-                s += "-------------------- * --------------------\n"
-            buffer.set_text(s)
-
-        self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
-        buffer = self._metar.get_buffer()
-        if metar is None:
-            buffer.set_text(xstr("briefing_metar_failed"))
-        else:
-            buffer.set_text(metar)
-
-        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."""
-        if not self._uppercasingMETAR:
-            self.metarEdited = True
-            self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
-                                                       buffer.get_end_iter(),
-                                                       True)!="")
-
-    def _metarInserted(self, textBuffer, iter, text, length):
-        """Called when new characters are inserted into the METAR.
-
-        It uppercases all characters."""
-        if not self._uppercasingMETAR:
-            self._uppercasingMETAR = True
-
-            iter1 = iter.copy()
-            iter1.backward_chars(length)
-            textBuffer.delete(iter, iter1)
-
-            textBuffer.insert(iter, text.upper())
-
-            self._uppercasingMETAR = False
-
-#-----------------------------------------------------------------------------
-
-class TakeoffPage(Page):
-    """Page for entering the takeoff data."""
-    def __init__(self, wizard):
-        """Construct the takeoff page."""
-        super(TakeoffPage, self).__init__(wizard, 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(8, 4)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        label = gtk.Label(xstr("takeoff_runway"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 0, 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, 0, 1)
-        label.set_mnemonic_widget(self._runway)
-
-        label = gtk.Label(xstr("takeoff_sid"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 0, 1, 1, 2)
-
-        self._sid = gtk.Entry()
-        self._sid.set_width_chars(10)
-        self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
-        self._sid.connect("changed", self._upperChanged)
-        table.attach(self._sid, 1, 3, 1, 2)
-        label.set_mnemonic_widget(self._sid)
-
-        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, 2, 3)
-
-        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, 2, 3)
-        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, 2, 3)
-
-        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, 3, 4)
-
-        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, 3, 4)
-        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, 3, 4)
-
-        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, 4, 5)
-
-        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, 4, 5)
-        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, 4, 5)
-
-        self._hasDerate = False
-
-        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, 5, 6)
-
-        self._derate = gtk.Entry()
-        self._derate.set_width_chars(10)
-        self._derate.set_tooltip_text(xstr("takeoff_derate_tooltip"))
-        self._derate.set_alignment(1.0)
-        self._derate.connect("changed", self._derateChanged)
-        table.attach(self._derate, 1, 3, 5, 6)
-        self._derateLabel.set_mnemonic_widget(self._derate)
-
-        self._derateUnit = gtk.Label("")
-        self._derateUnit.set_use_markup(True)
-        self._derateUnit.set_alignment(0.0, 0.5)
-        table.attach(self._derateUnit, 3, 4, 5, 6)
-
-        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, 6, 7)
-
-        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, 7, 8, ypadding = 8)
-
-        self.addCancelFlightButton()
-
-        self.addPreviousButton(clicked = self._backClicked)
-
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-    @property
-    def runway(self):
-        """Get the runway."""
-        return self._runway.get_text()
-
-    @property
-    def sid(self):
-        """Get the SID."""
-        return self._sid.get_text()
-
-    @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._hasDerate:
-            derate = self._derate.get_text()
-            return derate if derate else None
-        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."""
-        self._runway.set_text("")
-        self._runway.set_sensitive(True)
-        self._sid.set_text("")
-        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))
-
-        (derateLabel, derateUnit) = \
-             self._wizard.gui.flight.aircraft.derateLabels
-
-        self._hasDerate = derateLabel is not None
-
-        if self._hasDerate:
-            self._derateLabel.set_markup(derateLabel)
-            self._derateLabel.set_use_underline(True)
-            self._derateUnit.set_markup("" if derateUnit is None
-                                        else derateUnit)
-        else:
-            self._derateLabel.set_markup(xstr("takeoff_derate"))
-            self._derateUnit.set_text("")
-
-        self._derate.set_text("")
-
-        self._derateLabel.set_sensitive(self._hasDerate)
-        self._derate.set_sensitive(self._hasDerate)
-        self._derateUnit.set_sensitive(self._hasDerate)
-
-        self._rto.set_active(False)
-        self._rto.set_sensitive(False)
-
-        self._button.set_sensitive(False)
-        self._forwardAllowed = False
-
-    def allowForward(self):
-        """Allow going to the next page."""
-        self._forwardAllowed = True
-        self._updateForwardButton()
-
-    def reset(self):
-        """Reset the page if the wizard is reset."""
-        super(TakeoffPage, self).reset()
-        self._v1.reset()
-        self._vr.reset()
-        self._v2.reset()
-        self._hasDerate = False
-        self._antiIceOn.set_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 _updateForwardButton(self):
-        """Update the sensitivity of the forward button based on some conditions."""
-        sensitive = self._forwardAllowed and \
-                    self._runway.get_text()!="" and \
-                    self._sid.get_text()!="" 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 \
-                    (not self._hasDerate or self._derate.get_text()!="")
-        self._button.set_sensitive(sensitive)
-
-    def _valueChanged(self, widget, arg = None):
-        """Called when the value of some widget has changed."""
-        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."""
-        entry.set_text(entry.get_text().upper())
-        self._valueChanged(entry, arg)
-
-    def _derateChanged(self, entry):
-        """Called when the value of the derate is changed."""
-        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._hasDerate:
-            aircraft.updateDerate()
-        aircraft.updateTakeoffAntiIce()
-        self._wizard.nextPage()
-
-#-----------------------------------------------------------------------------
-
-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, 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, 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(6, 5)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        self._starButton = gtk.CheckButton()
-        self._starButton.connect("clicked", self._starButtonClicked)
-        table.attach(self._starButton, 0, 1, 0, 1)
-
-        label = gtk.Label(xstr("landing_star"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, 0, 1)
-
-        self._star = gtk.Entry()
-        self._star.set_width_chars(10)
-        self._star.set_tooltip_text(xstr("landing_star_tooltip"))
-        self._star.connect("changed", self._upperChanged)
-        self._star.set_sensitive(False)
-        table.attach(self._star, 2, 4, 0, 1)
-        label.set_mnemonic_widget(self._starButton)
-
-        self._transitionButton = gtk.CheckButton()
-        self._transitionButton.connect("clicked", self._transitionButtonClicked)
-        table.attach(self._transitionButton, 0, 1, 1, 2)
-
-        label = gtk.Label(xstr("landing_transition"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, 1, 2)
-
-        self._transition = gtk.Entry()
-        self._transition.set_width_chars(10)
-        self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
-        self._transition.connect("changed", self._upperChanged)
-        self._transition.set_sensitive(False)
-        table.attach(self._transition, 2, 4, 1, 2)
-        label.set_mnemonic_widget(self._transitionButton)
-
-        label = gtk.Label(xstr("landing_runway"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, 2, 3)
-
-        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, 2, 3)
-        label.set_mnemonic_widget(self._runway)
-
-        label = gtk.Label(xstr("landing_approach"))
-        label.set_use_underline(True)
-        label.set_alignment(0.0, 0.5)
-        table.attach(label, 1, 2, 3, 4)
-
-        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, 3, 4)
-        label.set_mnemonic_widget(self._approachType)
-
-        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, 4, 5)
-
-        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, 4, 5)
-        label.set_mnemonic_widget(self._vref)
-
-        self._vrefUnit = gtk.Label(xstr("label_knots"))
-        table.attach(self._vrefUnit, 4, 5, 4, 5)
-
-        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, 5, 6)
-
-        self.addCancelFlightButton()
-
-        self.addPreviousButton(clicked = self._backClicked)
-
-        self._button = self.addNextButton(clicked = self._forwardClicked)
-
-        # These are needed for correct size calculations
-        self._starButton.set_active(True)
-        self._transitionButton.set_active(True)
-
-    @property
-    def star(self):
-        """Get the STAR or None if none entered."""
-        return self._star.get_text() if self._starButton.get_active() else None
-
-    @property
-    def transition(self):
-        """Get the transition or None if none entered."""
-        return self._transition.get_text() \
-               if self._transitionButton.get_active() 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
-
-    def activate(self):
-        """Called when the page is activated."""
-        self._starButton.set_sensitive(True)
-        self._starButton.set_active(False)
-        self._star.set_text("")
-
-        self._transitionButton.set_sensitive(True)
-        self._transitionButton.set_active(False)
-        self._transition.set_text("")
-
-        self._runway.set_text("")
-        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()
-
-    def flightEnded(self):
-        """Called when the flight has ended."""
-        super(LandingPage, self).flightEnded()
-        self._flightEnded = True
-        self._updateForwardButton()
-
-    def _starButtonClicked(self, button):
-        """Called when the STAR button is clicked."""
-        active = button.get_active()
-        self._star.set_sensitive(active)
-        if active:
-            self._star.grab_focus()
-        self._updateForwardButton()
-
-    def _transitionButtonClicked(self, button):
-        """Called when the Transition button is clicked."""
-        active = button.get_active()
-        self._transition.set_sensitive(active)
-        if active:
-            self._transition.grab_focus()
-        self._updateForwardButton()
-
-    def _updateForwardButton(self):
-        """Update the sensitivity of the forward button."""
-        sensitive = self._flightEnded and \
-                    (self._starButton.get_active() or \
-                     self._transitionButton.get_active()) and \
-                    (self._star.get_text()!="" or
-                     not self._starButton.get_active()) and \
-                    (self._transition.get_text()!="" or
-                     not self._transitionButton.get_active()) 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 _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."""
-        aircraft = self._wizard.gui.flight.aircraft
-        aircraft.updateVRef()
-        aircraft.updateLandingAntiIce()
-        if self._wizard.gui.config.onlineGateSystem and \
-           self._wizard.loggedIn and not self._completed and \
-           self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
-           not self._wizard.entranceExam:
-            self._wizard.getFleet(callback = self._fleetRetrieved,
-                                  force = True)
-        else:
-            self._wizard.nextPage()
-
-    def _fleetRetrieved(self, fleet):
-        """Callback for the fleet retrieval."""
-        self._wizard.nextPage()
-
-#-----------------------------------------------------------------------------
-
-class FinishPage(Page):
-    """Flight finish page."""
-    _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
-                     ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
-                     ("flighttype_vip", const.FLIGHTTYPE_VIP),
-                     ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
-
-    def __init__(self, wizard):
-        """Construct the finish page."""
-        super(FinishPage, self).__init__(wizard, xstr("finish_title"),
-                                         xstr("finish_help"))
-
-        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                  xscale = 0.0, yscale = 0.0)
-
-        table = gtk.Table(8, 2)
-        table.set_row_spacings(4)
-        table.set_col_spacings(16)
-        table.set_homogeneous(False)
-        alignment.add(table)
-        self.setMainWidget(alignment)
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_rating"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 0, 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, 0, 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, 1, 2)
-
-        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, 1, 2)
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_block_time"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 2, 3)
-
-        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, 2, 3)
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_distance"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 3, 4)
-
-        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, 3, 4)
-
-        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
-        label = gtk.Label(xstr("finish_fuel"))
-        labelAlignment.add(label)
-        table.attach(labelAlignment, 0, 1, 4, 5)
-
-        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, 4, 5)
-
-        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, 5, 6)
-
-        flightTypeModel = gtk.ListStore(str, int)
-        for (name, type) in FinishPage._flightTypes:
-            flightTypeModel.append([xstr(name), type])
-
-        self._flightType = gtk.ComboBox(model = flightTypeModel)
-        renderer = gtk.CellRendererText()
-        self._flightType.pack_start(renderer, True)
-        self._flightType.add_attribute(renderer, "text", 0)
-        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, 5, 6)
-        label.set_mnemonic_widget(self._flightType)
-
-        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, 6, 7)
-
-        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, 7, 8)
-
-        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, 7, 8)
-        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._saveButton = self.addButton(xstr("finish_save"),
-                                          sensitive = False,
-                                          clicked = self._saveClicked,
-                                          tooltip = xstr("finish_save_tooltip"))
-        self._savePIREPDialog = None
-        self._lastSavePath = None
-
-        self._pirepSaved = False
-        self._pirepSent = False
-
-        self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
-                                          sensitive = False,
-                                          clicked = self._sendClicked,
-                                          tooltip = xstr("sendPIREP_tooltip"))
-
-    @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._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:
-            occupiedGates = self._wizard._fleet.getOccupiedGateNumbers()
-            for gateNumber in const.lhbpGateNumbers:
-                if gateNumber not in occupiedGates:
-                    self._gatesModel.append([gateNumber])
-            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)
-
-    def _backClicked(self, button):
-        """Called when the Back button is pressed."""
-        self.goBack()
-
-    def _updateButtons(self):
-        """Update the sensitivity state of the buttons."""
-        sensitive = self._flightType.get_active()>=0 and \
-                    (self._gatesModel.get_iter_first() is None or
-                     self._gate.get_active()>=0)
-
-        wasSensitive = self._saveButton.get_sensitive()
-
-        gui = self._wizard.gui
-        config = gui.config
-        if config.pirepAutoSave and sensitive and not wasSensitive:
-            self._lastSavePath = os.path.join(config.pirepDirectory,
-                                              self._getDefaultPIREPName())
-            self._lastSavePath = text2unicode(self._lastSavePath)
-            self._savePIREP(automatic = True)
-
-        self._saveButton.set_sensitive(sensitive)
-        self._sendButton.set_sensitive(sensitive and
-                                       self._wizard.bookedFlight.id is not None)
-
-    def _flightTypeChanged(self, comboBox):
-        """Called when the flight type has changed."""
-        self._updateButtons()
-
-    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 _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):
-        """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()
-
-    def _savePIREP(self, 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
-            self._pirepSaved = True
-
-        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
-
-
-    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
-
-#-----------------------------------------------------------------------------
-
-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._pages.append(FlightSelectionPage(self))
-        self._pages.append(GateSelectionPage(self))
-        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._pages.append(FuelPage(self))
-        self._routePage = RoutePage(self)
-        self._pages.append(self._routePage)
-        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)
-        self._finishPage = FinishPage(self)
-        self._pages.append(self._finishPage)
-
-        maxWidth = 0
-        maxHeight = 0
-        for page in self._pages:
-            page.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)
-            page.setStyle()
-        maxWidth += 16
-        maxHeight += 32
-        self.set_size_request(maxWidth, maxHeight)
-
-        self._initialize()
-
-    @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._loginPage.entranceExam
-
-    @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
-
-    def setCurrentPage(self, index, finalize = False):
-        """Set the current page to the one with the given index."""
-        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)
-
-        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 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
-
-    def nextPage(self, finalize = True):
-        """Go to the next page."""
-        self.jumpPage(1, finalize)
-
-    def jumpPage(self, count, finalize = True):
-        """Go to the page which is 'count' pages after the current one."""
-        self.setCurrentPage(self._currentPage + count, finalize = finalize)
-
-    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
-
-        firstPage = 0 if self._loginResult is None else 1
-        for page in self._pages[firstPage:]:
-            page.reset()
-
-        self.setCurrentPage(firstPage)
-
-    def login(self, callback, pilotID, password, entranceExam):
-        """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
-            entranceExam = loginResult.entranceExam
-            busyMessage = xstr("reload_busy")
-        else:
-            self._loginResult = None
-            busyMessage = xstr("login_busy")
-
-        self.gui.beginBusy(busyMessage)
-
-        self.gui.webHandler.login(self._loginResultCallback,
-                                  pilotID, password,
-                                  entranceExam = entranceExam)
-
-    def reloadFlights(self, callback):
-        """Reload the flights from the MAVA server."""
-        self.login(callback, None, None, None)
-
-    def cruiseLevelChanged(self):
-        """Called when the cruise level is changed."""
-        return self.gui.cruiseLevelChanged()
-
-    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
-            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 rtoToggled(self, indicated):
-        """Called when the RTO indication has changed."""
-        self.gui.rtoToggled(indicated)
-
-    def _connectSimulator(self):
-        """Connect to the simulator."""
-        self.gui.connectSimulator(self._bookedFlight.aircraftType)
-
-    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)
-
-#-----------------------------------------------------------------------------
Index: src/mlx/gui/gates.py
===================================================================
--- src/mlx/gui/gates.py	(revision 300)
+++ src/mlx/gui/gates.py	(revision 300)
@@ -0,0 +1,180 @@
+
+from mlx.gui.common import *
+
+from mlx.i18n import xstr
+import mlx.const as const
+
+#-------------------------------------------------------------------------------
+
+## @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(14, 4)
+        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 gateNumber in const.lhbpGateNumbers:
+            label = gtk.Label()
+            label.set_markup("<b>" + gateNumber + "</b>")
+            table.attach(label, column, column + 1, row, row + 1)
+
+            self._gateLabels[gateNumber] = label
+            
+            if column==1 and row==12:
+                column = 2
+                row = 1
+            elif row==13:
+                column += 1
+                row = 0
+            else:
+                row += 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 gateNumber in const.lhbpGateNumbers:
+                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 300)
+++ 	(revision )
@@ -1,180 +1,0 @@
-
-from mlx.gui.common import *
-
-from mlx.i18n import xstr
-import mlx.const as const
-
-#-------------------------------------------------------------------------------
-
-## @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(14, 4)
-        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 gateNumber in const.lhbpGateNumbers:
-            label = gtk.Label()
-            label.set_markup("<b>" + gateNumber + "</b>")
-            table.attach(label, column, column + 1, row, row + 1)
-
-            self._gateLabels[gateNumber] = label
-            
-            if column==1 and row==12:
-                column = 2
-                row = 1
-            elif row==13:
-                column += 1
-                row = 0
-            else:
-                row += 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 gateNumber in const.lhbpGateNumbers:
-                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 393)
+++ src/mlx/gui/gui.py	(revision 393)
@@ -0,0 +1,1469 @@
+# -*- 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.pirep import PIREPViewer
+
+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._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.webHandler = web.Handler()
+        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)
+        accelGroup = gtk.AccelGroup()
+        window.add_accel_group(accelGroup)
+
+        mainVBox = gtk.VBox()
+        window.add(mainVBox)
+
+        self._preferences = Preferences(self)
+        self._checklistEditor = ChecklistEditor(self)
+        self._approachCalloutsEditor = ApproachCalloutsEditor(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._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)
+
+        window.show_all()
+        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 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.flightDefects
+
+    @property
+    def delayCodes(self):
+        """Get the delay codes."""
+        return self._flightInfo.delayCodes
+
+    def run(self):
+        """Run the GUI."""
+        if self.config.autoUpdate:
+            self._updater = Updater(self,
+                                    self._programDirectory,
+                                    self.config.updateURL,
+                                    self._mainWindow)
+            self._updater.start()
+
+        singleton.raiseCallback = self.raiseCallback
+        gtk.main()
+        singleton.raiseCallback = None
+
+        self._disconnect()
+
+    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._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")
+
+        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):
+        """Enable the flight info tab."""
+        self._flightInfo.enable()
+
+    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._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.reloadFlights(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 _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 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 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):
+        """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.beginBusy(xstr("fleet_busy"))
+            self.webHandler.getFleet(self._fleetResultCallback)
+        else:
+            callback(self._fleet)
+
+    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 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
+        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, aircraftType):
+        """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 = aircraftType
+        self._flight.aircraft = acft.Aircraft.create(self._flight)
+        self._flight.aircraft._checkers.append(self)
+
+        if self._simulator is None:
+            self._simulator = fs.createSimulator(const.SIM_MSFS9, 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)
+
+        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)
+
+        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 _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 _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 _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._pirepViewer.setPIREP(pirep)
+                        self._pirepViewer.show_all()
+                        self._pirepViewer.run()
+                        self._pirepViewer.hide()
+                    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 _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
Index: src/mlx/gui/gui.py
===================================================================
--- src/mlx/gui/gui.py	(revision 393)
+++ 	(revision )
@@ -1,1469 +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.pirep import PIREPViewer
-
-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._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.webHandler = web.Handler()
-        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)
-        accelGroup = gtk.AccelGroup()
-        window.add_accel_group(accelGroup)
-
-        mainVBox = gtk.VBox()
-        window.add(mainVBox)
-
-        self._preferences = Preferences(self)
-        self._checklistEditor = ChecklistEditor(self)
-        self._approachCalloutsEditor = ApproachCalloutsEditor(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._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)
-
-        window.show_all()
-        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 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.flightDefects
-
-    @property
-    def delayCodes(self):
-        """Get the delay codes."""
-        return self._flightInfo.delayCodes
-
-    def run(self):
-        """Run the GUI."""
-        if self.config.autoUpdate:
-            self._updater = Updater(self,
-                                    self._programDirectory,
-                                    self.config.updateURL,
-                                    self._mainWindow)
-            self._updater.start()
-
-        singleton.raiseCallback = self.raiseCallback
-        gtk.main()
-        singleton.raiseCallback = None
-
-        self._disconnect()
-
-    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._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")
-
-        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):
-        """Enable the flight info tab."""
-        self._flightInfo.enable()
-
-    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._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.reloadFlights(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 _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 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 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):
-        """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.beginBusy(xstr("fleet_busy"))
-            self.webHandler.getFleet(self._fleetResultCallback)
-        else:
-            callback(self._fleet)
-
-    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 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
-        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, aircraftType):
-        """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 = aircraftType
-        self._flight.aircraft = acft.Aircraft.create(self._flight)
-        self._flight.aircraft._checkers.append(self)
-
-        if self._simulator is None:
-            self._simulator = fs.createSimulator(const.SIM_MSFS9, 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)
-
-        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)
-
-        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 _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 _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 _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._pirepViewer.setPIREP(pirep)
-                        self._pirepViewer.show_all()
-                        self._pirepViewer.run()
-                        self._pirepViewer.hide()
-                    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 _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
Index: src/mlx/gui/info.py
===================================================================
--- src/mlx/gui/info.py	(revision 349)
+++ src/mlx/gui/info.py	(revision 349)
@@ -0,0 +1,178 @@
+
+from common import *
+
+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 text areas for the
+# comments and the flight defects at the top next to each other, and the frame
+# for the delay codes at the bottom in the centre.
+
+#------------------------------------------------------------------------------
+
+class FlightInfo(gtk.VBox):
+    """The flight info tab."""
+    @staticmethod
+    def _delayCodes():
+        """Get an array of delay codes."""
+        return [ (const.DELAYCODE_LOADING, xstr("info_delay_loading")),
+                 (const.DELAYCODE_VATSIM, xstr("info_delay_vatsim")),
+                 (const.DELAYCODE_NETWORK, xstr("info_delay_net")),
+                 (const.DELAYCODE_CONTROLLER, xstr("info_delay_atc")),
+                 (const.DELAYCODE_SYSTEM, xstr("info_delay_system")),
+                 (const.DELAYCODE_NAVIGATION, xstr("info_delay_nav")),
+                 (const.DELAYCODE_TRAFFIC, xstr("info_delay_traffic")),
+                 (const.DELAYCODE_APRON, xstr("info_delay_apron")),
+                 (const.DELAYCODE_WEATHER, xstr("info_delay_weather")),
+                 (const.DELAYCODE_PERSONAL, xstr("info_delay_personal")) ]
+
+    @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()
+        # FIXME: these should be constants
+        scroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
+                            else gtk.POLICY_AUTOMATIC,
+                            gtk.PolicyType.AUTOMATIC if pygobject
+                            else gtk.POLICY_AUTOMATIC)
+        scroller.set_shadow_type(gtk.ShadowType.IN if pygobject
+                                 else gtk.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):
+        """Construct the flight info tab."""
+        super(FlightInfo, self).__init__()
+        self._gui = gui
+
+        self._commentsAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                                xscale = 1.0, yscale = 1.0)
+        commentsBox = gtk.HBox()
+
+        (frame, self._comments) = FlightInfo._createCommentArea(xstr("info_comments"))
+        commentsBox.pack_start(frame, True, True, 8)
+        self._comments.get_buffer().connect("changed", self._commentsChanged)
+
+        (frame, self._flightDefects) = \
+             FlightInfo._createCommentArea(xstr("info_defects"))
+        commentsBox.pack_start(frame, 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 = 0.0, yscale = 0.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 4,
+                              padding_left = 8, padding_right = 8)
+
+        self._delayTable = table = gtk.Table(5, 2)
+        table.set_col_spacings(16)
+
+        row = 0
+        column = 0
+
+        self._delayCodeWidgets = []
+        for (_code, label) in FlightInfo._delayCodes():
+            button = gtk.CheckButton(label)
+            button.set_use_underline(True)
+            table.attach(button, column, column + 1, row, row + 1)
+            self._delayCodeWidgets.append(button)
+            if column==0:
+                column += 1
+            else:
+                row += 1
+                column = 0
+
+        alignment.add(table)
+        frame.add(alignment)
+
+        self._delayAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                             xscale = 0.0, yscale = 0.0)
+        self._delayAlignment.add(frame)
+
+        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))
+
+    @property
+    def hasComments(self):
+        """Get whether there is any text in comments field."""
+        return self._comments.get_buffer().get_char_count()>0
+
+    @property
+    def flightDefects(self):
+        """Get the flight defects."""
+        buffer = self._flightDefects.get_buffer()
+        return text2unicode(buffer.get_text(buffer.get_start_iter(),
+                                            buffer.get_end_iter(), True))
+
+    @property
+    def delayCodes(self):
+        """Get the list of delay codes checked by the user."""
+        codes =  []
+        delayCodes = FlightInfo._delayCodes()
+        for index in range(0, len(delayCodes)):
+            if self._delayCodeWidgets[index].get_active():
+                codes.append(delayCodes[index][0])
+        return codes
+
+    def enable(self):
+        """Enable the flight info tab."""
+        self._comments.set_sensitive(True)
+        self._flightDefects.set_sensitive(True)
+        self._delayTable.set_sensitive(True)
+
+    def disable(self):
+        """Enable the flight info tab."""
+        self._comments.set_sensitive(False)
+        self._flightDefects.set_sensitive(False)
+        self._delayTable.set_sensitive(False)
+
+    def reset(self):
+        """Reset the flight info tab."""
+        self._comments.get_buffer().set_text("")
+        self._flightDefects.get_buffer().set_text("")
+
+        for widget in self._delayCodeWidgets:
+            widget.set_active(False)
+
+    def _commentsChanged(self, textbuffer):
+        """Called when the comments have changed."""
+        self._gui.updateRTO(inLoop = True)
Index: src/mlx/gui/info.py
===================================================================
--- src/mlx/gui/info.py	(revision 349)
+++ 	(revision )
@@ -1,178 +1,0 @@
-
-from common import *
-
-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 text areas for the
-# comments and the flight defects at the top next to each other, and the frame
-# for the delay codes at the bottom in the centre.
-
-#------------------------------------------------------------------------------
-
-class FlightInfo(gtk.VBox):
-    """The flight info tab."""
-    @staticmethod
-    def _delayCodes():
-        """Get an array of delay codes."""
-        return [ (const.DELAYCODE_LOADING, xstr("info_delay_loading")),
-                 (const.DELAYCODE_VATSIM, xstr("info_delay_vatsim")),
-                 (const.DELAYCODE_NETWORK, xstr("info_delay_net")),
-                 (const.DELAYCODE_CONTROLLER, xstr("info_delay_atc")),
-                 (const.DELAYCODE_SYSTEM, xstr("info_delay_system")),
-                 (const.DELAYCODE_NAVIGATION, xstr("info_delay_nav")),
-                 (const.DELAYCODE_TRAFFIC, xstr("info_delay_traffic")),
-                 (const.DELAYCODE_APRON, xstr("info_delay_apron")),
-                 (const.DELAYCODE_WEATHER, xstr("info_delay_weather")),
-                 (const.DELAYCODE_PERSONAL, xstr("info_delay_personal")) ]
-
-    @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()
-        # FIXME: these should be constants
-        scroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
-                            else gtk.POLICY_AUTOMATIC,
-                            gtk.PolicyType.AUTOMATIC if pygobject
-                            else gtk.POLICY_AUTOMATIC)
-        scroller.set_shadow_type(gtk.ShadowType.IN if pygobject
-                                 else gtk.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):
-        """Construct the flight info tab."""
-        super(FlightInfo, self).__init__()
-        self._gui = gui
-
-        self._commentsAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                                xscale = 1.0, yscale = 1.0)
-        commentsBox = gtk.HBox()
-
-        (frame, self._comments) = FlightInfo._createCommentArea(xstr("info_comments"))
-        commentsBox.pack_start(frame, True, True, 8)
-        self._comments.get_buffer().connect("changed", self._commentsChanged)
-
-        (frame, self._flightDefects) = \
-             FlightInfo._createCommentArea(xstr("info_defects"))
-        commentsBox.pack_start(frame, 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 = 0.0, yscale = 0.0)
-        alignment.set_padding(padding_top = 4, padding_bottom = 4,
-                              padding_left = 8, padding_right = 8)
-
-        self._delayTable = table = gtk.Table(5, 2)
-        table.set_col_spacings(16)
-
-        row = 0
-        column = 0
-
-        self._delayCodeWidgets = []
-        for (_code, label) in FlightInfo._delayCodes():
-            button = gtk.CheckButton(label)
-            button.set_use_underline(True)
-            table.attach(button, column, column + 1, row, row + 1)
-            self._delayCodeWidgets.append(button)
-            if column==0:
-                column += 1
-            else:
-                row += 1
-                column = 0
-
-        alignment.add(table)
-        frame.add(alignment)
-
-        self._delayAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
-                                             xscale = 0.0, yscale = 0.0)
-        self._delayAlignment.add(frame)
-
-        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))
-
-    @property
-    def hasComments(self):
-        """Get whether there is any text in comments field."""
-        return self._comments.get_buffer().get_char_count()>0
-
-    @property
-    def flightDefects(self):
-        """Get the flight defects."""
-        buffer = self._flightDefects.get_buffer()
-        return text2unicode(buffer.get_text(buffer.get_start_iter(),
-                                            buffer.get_end_iter(), True))
-
-    @property
-    def delayCodes(self):
-        """Get the list of delay codes checked by the user."""
-        codes =  []
-        delayCodes = FlightInfo._delayCodes()
-        for index in range(0, len(delayCodes)):
-            if self._delayCodeWidgets[index].get_active():
-                codes.append(delayCodes[index][0])
-        return codes
-
-    def enable(self):
-        """Enable the flight info tab."""
-        self._comments.set_sensitive(True)
-        self._flightDefects.set_sensitive(True)
-        self._delayTable.set_sensitive(True)
-
-    def disable(self):
-        """Enable the flight info tab."""
-        self._comments.set_sensitive(False)
-        self._flightDefects.set_sensitive(False)
-        self._delayTable.set_sensitive(False)
-
-    def reset(self):
-        """Reset the flight info tab."""
-        self._comments.get_buffer().set_text("")
-        self._flightDefects.get_buffer().set_text("")
-
-        for widget in self._delayCodeWidgets:
-            widget.set_active(False)
-
-    def _commentsChanged(self, textbuffer):
-        """Called when the comments have changed."""
-        self._gui.updateRTO(inLoop = True)
Index: src/mlx/gui/monitor.py
===================================================================
--- src/mlx/gui/monitor.py	(revision 408)
+++ src/mlx/gui/monitor.py	(revision 408)
@@ -0,0 +1,407 @@
+
+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)
+
+        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("-")
+        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 += ", "
+                    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)
+            self._strobeLightsOn.set_sensitive(aircraftState.strobeLightsOn)
+
+            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)
+            self._parking.set_sensitive(aircraftState.parking)
+            self._gearControlDown.set_sensitive(aircraftState.gearControlDown)
+            self._gearsDown.set_sensitive(aircraftState.gearsDown)
+            self._spoilersArmed.set_sensitive(aircraftState.spoilersArmed)
+            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)
+
+#------------------------------------------------------------------------------
Index: src/mlx/gui/monitor.py
===================================================================
--- src/mlx/gui/monitor.py	(revision 398)
+++ 	(revision )
@@ -1,401 +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 = 7, 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)
-
-        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("-")
-        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)
-
-            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 += ", "
-                    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)
-            self._strobeLightsOn.set_sensitive(aircraftState.strobeLightsOn)
-
-            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)
-            self._parking.set_sensitive(aircraftState.parking)
-            self._gearControlDown.set_sensitive(aircraftState.gearControlDown)
-            self._gearsDown.set_sensitive(aircraftState.gearsDown)
-            self._spoilersArmed.set_sensitive(aircraftState.spoilersArmed)
-            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)
-
-#------------------------------------------------------------------------------
Index: src/mlx/gui/pirep.py
===================================================================
--- src/mlx/gui/pirep.py	(revision 303)
+++ src/mlx/gui/pirep.py	(revision 303)
@@ -0,0 +1,616 @@
+
+from common import *
+
+from mlx.pirep import PIREP
+from mlx.const import *
+
+import time
+
+#------------------------------------------------------------------------------
+
+## @package mlx.gui.pirep
+#
+# The detailed PIREP viewer.
+#
+# 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 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):
+        """Get a bold label with the given text."""
+        label = gtk.Label("<b>" + text + "</b>")
+        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):
+        """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(False)
+        textView.set_cursor_visible(False)
+        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.
+        
+        Returnsd 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 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):
+        """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._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 += PIREP.delayCodeNames[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
+
+        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
+        
+#------------------------------------------------------------------------------
Index: src/mlx/gui/pirep.py
===================================================================
--- src/mlx/gui/pirep.py	(revision 303)
+++ 	(revision )
@@ -1,616 +1,0 @@
-
-from common import *
-
-from mlx.pirep import PIREP
-from mlx.const import *
-
-import time
-
-#------------------------------------------------------------------------------
-
-## @package mlx.gui.pirep
-#
-# The detailed PIREP viewer.
-#
-# 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 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):
-        """Get a bold label with the given text."""
-        label = gtk.Label("<b>" + text + "</b>")
-        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):
-        """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(False)
-        textView.set_cursor_visible(False)
-        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.
-        
-        Returnsd 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 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):
-        """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._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 += PIREP.delayCodeNames[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
-
-        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
-        
-#------------------------------------------------------------------------------
Index: src/mlx/gui/prefs.py
===================================================================
--- src/mlx/gui/prefs.py	(revision 392)
+++ src/mlx/gui/prefs.py	(revision 392)
@@ -0,0 +1,838 @@
+
+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)
+
+        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._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.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()
+
+    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)
+
+        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)
+
+        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 392)
+++ 	(revision )
@@ -1,838 +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)
-
-        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._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.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()
-
-    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)
-
-        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)
-
-        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 404)
+++ src/mlx/gui/statusbar.py	(revision 404)
@@ -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(12)
+        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 404)
+++ 	(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(12)
-        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 404)
+++ src/mlx/gui/statusicon.py	(revision 404)
@@ -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 404)
+++ 	(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/update.py
===================================================================
--- src/mlx/gui/update.py	(revision 300)
+++ src/mlx/gui/update.py	(revision 300)
@@ -0,0 +1,289 @@
+
+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)
+
+    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()
+
+#-------------------------------------------------------------------------------
Index: src/mlx/gui/update.py
===================================================================
--- src/mlx/gui/update.py	(revision 300)
+++ 	(revision )
@@ -1,289 +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)
-
-    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()
-
-#-------------------------------------------------------------------------------
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)
-                
-        
