Index: src/mlx/config.py
===================================================================
--- src/mlx/config.py	(revision 40)
+++ src/mlx/config.py	(revision 42)
@@ -20,8 +20,35 @@
         """Construct the configuration with default values."""
 
+        self._pilotID = ""
+        self._password = ""
+
         self._autoUpdate = True        
         self._updateURL = Config.DEFAULT_UPDATE_URL
 
         self._modified = False
+
+    @property
+    def pilotID(self):
+        """Get the pilot ID."""
+        return self._pilotID
+
+    @pilotID.setter
+    def pilotID(self, pilotID):
+        """Set the pilot ID."""
+        if pilotID!=self._pilotID:
+            self._pilotID = pilotID
+            self._modified = True
+
+    @property
+    def password(self):
+        """Get the password."""
+        return self._password
+
+    @password.setter
+    def password(self, password):
+        """Set the password."""
+        if password!=self._password:
+            self._password = password
+            self._modified = True
 
     @property
@@ -54,4 +81,7 @@
         config.read(configPath)
 
+        self._pilotID = self._get(config, "login", "id", "")
+        self._password = self._get(config, "login", "password", "")
+
         self._autoUpdate = self._getBoolean(config, "update", "auto", True)
         self._updateURL = self._get(config, "update", "url",
@@ -65,4 +95,8 @@
 
         config = ConfigParser.RawConfigParser()
+
+        config.add_section("login")
+        config.set("login", "id", self._pilotID)
+        config.set("login", "password", self._password)
 
         config.add_section("update")
Index: src/mlx/gui/common.py
===================================================================
--- src/mlx/gui/common.py	(revision 32)
+++ src/mlx/gui/common.py	(revision 42)
@@ -17,4 +17,7 @@
     except Exception, e:
         pass
+
+    MESSAGETYPE_ERROR = gtk.MESSAGE_ERROR
+    BUTTONSTYPE_OK = gtk.BUTTONS_OK
 else:
     print "Using PyGObject"
@@ -25,4 +28,7 @@
     from gi.repository import AppIndicator3 as appindicator
     appIndicator = True
+
+    MESSAGETYPE_ERROR = gtk.MessageType.ERROR
+    BUTTONSTYPE_OK = gtk.ButtonsType.OK
 
 import cairo
Index: src/mlx/gui/flight.py
===================================================================
--- src/mlx/gui/flight.py	(revision 42)
+++ src/mlx/gui/flight.py	(revision 42)
@@ -0,0 +1,161 @@
+# The flight handling "wizard"
+
+from mlx.gui.common import *
+
+#-----------------------------------------------------------------------------
+
+class Page(gtk.VBox):
+    """A page in the flight wizard."""
+    def __init__(self, wizard):
+        """Construct the page."""
+        super(Page, self).__init__()
+        self._wizard = wizard
+
+#-----------------------------------------------------------------------------
+
+class LoginPage(Page):
+    """The login page."""
+    def __init__(self, wizard):
+        """Construct the login page."""
+        super(LoginPage, self).__init__(wizard)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0, yscale = 0.3)
+        label = gtk.Label("Enter your pilot's ID and password to\n"
+                          "log in to the MAVA website and download\n"
+                          "your booked flights")
+        label.set_justify(gtk.Justification.CENTER if pygobject
+                          else gtk.JUSTIFY_CENTER)
+        alignment.add(label)
+        self.pack_start(alignment, True, True, 0)
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0, yscale = 0.0)
+
+        table = gtk.Table(2, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(32)
+        alignment.add(table)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        labelAlignment.add(gtk.Label("Pilot ID:"))
+        table.attach(labelAlignment, 0, 1, 0, 1)
+
+        self._pilotID = gtk.Entry()
+        self._pilotID.connect("changed", self._setLoginButton)
+        table.attach(self._pilotID, 1, 2, 0, 1)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        labelAlignment.add(gtk.Label("Password:"))
+        table.attach(labelAlignment, 0, 1, 1, 2)
+
+        self._password = gtk.Entry()
+        self._password.set_visibility(False)
+        self._password.connect("changed", self._setLoginButton)
+        table.attach(self._password, 1, 2, 1, 2)
+
+        self.pack_start(alignment, True, True, 0)
+
+        alignment = gtk.Alignment(xalign = 1.0, xscale=0.0)
+        alignment.set_padding(padding_top = 4, padding_bottom = 10,
+                              padding_left = 16, padding_right = 16)
+        
+        self._loginButton = gtk.Button("Login")
+        self._loginButton.set_sensitive(False)
+        self._loginButton.connect("clicked", self._loginClicked)
+        
+        alignment.add(self._loginButton)
+        self.pack_start(alignment, False, False, 0)
+
+        config = self._wizard.gui.config
+        self._pilotID.set_text(config.pilotID)
+        self._password.set_text(config.password)
+
+    def _setLoginButton(self, entry):
+        """Set the login button's sensitivity.
+
+        The button is sensitive only if both the pilot ID and the password
+        fields contain values."""
+        self._loginButton.set_sensitive(self._pilotID.get_text()!="" and
+                                        self._password.get_text()!="")
+
+    def _loginClicked(self, button):
+        """Called when the login button was clicked."""
+        self._wizard.gui.webHandler.login(self._pilotID.get_text(),
+                                          self._password.get_text(),
+                                          self._loginResultCallback)
+
+    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."""
+        if returned:
+            if result.loggedIn:
+                config = self._wizard.gui.config
+                config.pilotID = self._pilotID.get_text()
+                config.password = self._password.get_text()
+                self._wizard.nextPage()
+            else:
+                dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
+                                           buttons = BUTTONSTYPE_OK,
+                                           message_format =
+                                           "Invalid pilot's ID or password.")
+                dialog.format_secondary_markup("Check the ID and try to reenter"
+                                               " the password.")
+                dialog.run()
+                dialog.hide()
+        else:
+            dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
+                                       buttons = BUTTONSTYPE_OK,
+                                       message_format = 
+                                       "Failed to connect to the MAVA website.")
+            dialog.format_secondary_markup("Try again in a few minutes.")
+            dialog.run()
+            dialog.hide()
+
+#-----------------------------------------------------------------------------
+
+class FlightSelectionPage(Page):
+    """The page to select the flight."""
+    def __init__(self, wizard):
+        """Construct the flight selection page."""
+        super(FlightSelectionPage, self).__init__(wizard)
+        self.pack_start(gtk.Label("Hello, te lo!"), False, False, 0)
+
+#-----------------------------------------------------------------------------
+
+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._pages.append(LoginPage(self))
+        self._pages.append(FlightSelectionPage(self))
+
+        self.setCurrentPage(0)
+
+    def setCurrentPage(self, index):
+        """Set the current page to the one with the given index."""
+        assert index < len(self._pages)
+        
+        if self._currentPage is not None:
+            self.remove(self._pages[self._currentPage])
+
+        self._currentPage = index
+        self.add(self._pages[index])
+        self.show_all()
+
+    def nextPage(self):
+        """Go to the next page."""
+        self.setCurrentPage(self._currentPage + 1)
+    
+#-----------------------------------------------------------------------------
+
Index: src/mlx/gui/gui.py
===================================================================
--- src/mlx/gui/gui.py	(revision 41)
+++ src/mlx/gui/gui.py	(revision 42)
@@ -5,4 +5,5 @@
 from update import Updater
 from mlx.gui.common import *
+from mlx.gui.flight import Wizard
 
 import mlx.const as const
@@ -40,5 +41,5 @@
 
         self._programDirectory = programDirectory
-        self._config = config
+        self.config = config
         self._connecting = False
         self._connected = False
@@ -69,15 +70,26 @@
         window.add(mainVBox)
 
+        notebook = gtk.Notebook()
+        mainVBox.add(notebook)
+
+        notebook.append_page(Wizard(self), gtk.Label("Flight"))
+
+        dataVBox = gtk.VBox()
+        notebook.append_page(dataVBox, gtk.Label("Data"))
+
         setupFrame = self._buildSetupFrame()
         setupFrame.set_border_width(8)
-        mainVBox.pack_start(setupFrame, False, False, 0)
+        dataVBox.pack_start(setupFrame, False, False, 0)
 
         dataFrame = self._buildDataFrame()
         dataFrame.set_border_width(8)
-        mainVBox.pack_start(dataFrame, False, False, 0)
-
+        dataVBox.pack_start(dataFrame, False, False, 0)
+
+        logVBox = gtk.VBox()
+        notebook.append_page(logVBox, gtk.Label("Log"))
+        
         logFrame = self._buildLogFrame()
         logFrame.set_border_width(8)
-        mainVBox.pack_start(logFrame, True, True, 0)
+        logVBox.pack_start(logFrame, True, True, 0)
 
         mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
@@ -94,8 +106,8 @@
     def run(self):
         """Run the GUI."""
-        if self._config.autoUpdate:
+        if self.config.autoUpdate:
             self._updater = Updater(self,
                                     self._programDirectory,
-                                    self._config.updateURL,
+                                    self.config.updateURL,
                                     self._mainWindow)
             self._updater.start()
