Index: locale/en/mlx.po
===================================================================
--- locale/en/mlx.po	(revision 744)
+++ locale/en/mlx.po	(revision 753)
@@ -94,4 +94,7 @@
 msgstr "_Send..."
 
+msgid "button_register"
+msgstr "_Register"
+
 msgid "button_cancelFlight"
 msgstr "Cancel flight"
@@ -269,5 +272,5 @@
 msgid "login_remember_tooltip"
 msgstr ""
-"If checked, your password will be stored, so that you "
+"If checked, your password will be stored by the program, so that you "
 "should not have to enter it every time. Note, however, that "
 "the password is stored as text, and anybody who can access "
@@ -279,4 +282,10 @@
 msgid "login_entranceExam_tooltip"
 msgstr "Check this to log in to take your entrance exam."
+
+msgid "button_login_register"
+msgstr "R_egister..."
+
+msgid "button_login_register_tooltip"
+msgstr "Click here if you would like to register with Malév Virtual"
 
 msgid "button_offline"
@@ -2113,2 +2122,87 @@
 msgid "sendBugReport_error_sec"
 msgstr "There was some communication error, try again later."
+
+msgid "register_title"
+msgstr "Registration"
+
+msgid "register_help"
+msgstr ""
+"Enter the data below.\n"
+"Optional data items are in <i>italics</i>.\n"
+"One of VATSIM ID or IVAO ID is mandatory."
+
+msgid "register_name"
+msgstr "_Name:"
+
+msgid "register_name_tooltip"
+msgstr "Enter your real name."
+
+msgid "register_year_of_birth"
+msgstr "_Year of birth:"
+
+msgid "register_year_of_birth_tooltip"
+msgstr "Enter the year in which you were born."
+
+msgid "register_email"
+msgstr "_E-mail:"
+
+msgid "register_email_tooltip"
+msgstr "Enter your e-mail address."
+
+msgid "register_email_public"
+msgstr "P_ublic"
+
+msgid "register_email_public_tooltip"
+msgstr ""
+"If checked, your e-mail address will be displayed in our forums and "
+"possibly other internal website areas, and thus it will be made "
+"available to other members of MAVA."
+
+msgid "register_vatsim_id"
+msgstr "_VATSIM CID:"
+
+msgid "register_vatsim_id_tooltip"
+msgstr ""
+"Enter your VATSIM CID, if you are registered with VATSIM. "
+"Note that either a VATSIM CID or an IVAO VID is required for the registration."
+
+msgid "register_ivao_id"
+msgstr "IV_AO VID:"
+
+msgid "register_ivao_id_tooltip"
+msgstr ""
+"Enter your IVAO VID, if you are registered with IVAO. "
+"Note that either a VATSIM CID or an IVAO VID is required for the registration."
+
+msgid "register_phone_num"
+msgstr "<i>Phone num_ber</i>:"
+
+msgid "register_phone_num_tooltip"
+msgstr "Enter your phone number, if we can reach you by phone."
+
+msgid "register_nationality"
+msgstr "<i>Na_tionality</i>:"
+
+msgid "register_nationality_tooltip"
+msgstr "Enter your nationality, if you would like to share that info with us."
+
+msgid "register_password"
+msgstr "Pa_ssword:"
+
+msgid "register_password_tooltip"
+msgstr "Enter the password you would like to use for logging in."
+
+msgid "register_password2"
+msgstr "Pass_word again:"
+
+msgid "register_password2_tooltip"
+msgstr "Enter the same password again."
+
+msgid "register_password_ok"
+msgstr "<span foreground=\"green\">OK</span>"
+
+msgid "register_password_too_short"
+msgstr "<span foreground=\"orange\">Too short</span>"
+
+msgid "register_password_mismatch"
+msgstr "<span foreground=\"red\">Passwords differ</span>"
Index: locale/hu/mlx.po
===================================================================
--- locale/hu/mlx.po	(revision 744)
+++ locale/hu/mlx.po	(revision 753)
@@ -94,4 +94,7 @@
 msgstr "_Küldés..."
 
+msgid "button_register"
+msgstr "_Regisztráció"
+
 msgid "button_cancelFlight"
 msgstr "Járat megszakítása"
@@ -278,4 +281,10 @@
 msgid "login_entranceExam_tooltip"
 msgstr "Ha ezt bejelölöd, ellenörző repülésre jelentkezhetsz be."
+
+msgid "button_login_register"
+msgstr "_Regisztráció..."
+
+msgid "button_login_register_tooltip"
+msgstr "Kattints ide a Malév Virtualhoz való regisztrálás céljából."
 
 msgid "button_offline"
@@ -2127,2 +2136,87 @@
 msgid "sendBugReport_error_sec"
 msgstr "Valamilyen kommunikációs probléma adódott, próbáld újra később."
+
+msgid "register_title"
+msgstr "Regisztráció"
+
+msgid "register_help"
+msgstr ""
+"Töltsd ki a lenti űrlapot.\n"
+"A <i>dőlt</i> betűs adatokat nem kötelező megadnod.\n"
+"Vagy VATSIM vagy IVAO azonosítót meg kell adni."
+
+msgid "register_name"
+msgstr "Né_v:"
+
+msgid "register_name_tooltip"
+msgstr "Írd be a valódi nevedet"
+
+msgid "register_year_of_birth"
+msgstr "S_zületés éve:"
+
+msgid "register_year_of_birth_tooltip"
+msgstr "Írd be az évnek a számát, amelyben születtél.."
+
+msgid "register_email"
+msgstr "E-m_ail:"
+
+msgid "register_email_tooltip"
+msgstr "Írd be az e-mail címedet."
+
+msgid "register_email_public"
+msgstr "P_ublikus"
+
+msgid "register_email_public_tooltip"
+msgstr ""
+"Ha ezt bejelölöd, az e-mail címed megjelenhet a fórumunkban és "
+"egyéb belső weboldalakon is, így azt a MAVA többi tagja "
+"láthatja."
+
+msgid "register_vatsim_id"
+msgstr "VATSIM _CID:"
+
+msgid "register_vatsim_id_tooltip"
+msgstr ""
+"Ha rendelkezel VATSIM regisztrációval, írd be ide az azonosítódat. "
+"A regisztrációhoz vagy VATSIM vagy IVAO azonosító szükséges."
+
+msgid "register_ivao_id"
+msgstr "IVA_O VID:"
+
+msgid "register_ivao_id_tooltip"
+msgstr ""
+"Ha rendelkezel IVAO regisztrációval, írd be ide az azonosítódat. "
+"A regisztrációhoz vagy VATSIM vagy IVAO azonosító szükséges."
+
+msgid "register_phone_num"
+msgstr "<i>Tele_fonszám</i>:"
+
+msgid "register_phone_num_tooltip"
+msgstr "Ha telefonon is kereshetünk, írd be a számodat."
+
+msgid "register_nationality"
+msgstr "<i>Nemzetisé_g</i>:"
+
+msgid "register_nationality_tooltip"
+msgstr "Írd be a nemzetiségedet, ha meg kívánod azt velünk osztani."
+
+msgid "register_password"
+msgstr "Je_lszó:"
+
+msgid "register_password_tooltip"
+msgstr "Írd be a jelszót, amivel be szeretnél majd jelentkezni."
+
+msgid "register_password2"
+msgstr "Jelszó új_ra:"
+
+msgid "register_password2_tooltip"
+msgstr "Írd be ugyanazt a jelszót megint."
+
+msgid "register_password_ok"
+msgstr "<span foreground=\"green\">Rendben</span>"
+
+msgid "register_password_too_short"
+msgstr "<span foreground=\"orange\">Túl rövid</span>"
+
+msgid "register_password_mismatch"
+msgstr "<span foreground=\"red\">A két jelszó különbözik</span>"
Index: src/mlx/gui/flight.py
===================================================================
--- src/mlx/gui/flight.py	(revision 739)
+++ src/mlx/gui/flight.py	(revision 753)
@@ -20,4 +20,5 @@
 import tempfile
 import threading
+import re
 
 #-----------------------------------------------------------------------------
@@ -327,4 +328,8 @@
         table.attach(self._entranceExam, 1, 2, 3, 4, ypadding = 12)
 
+        self.addButton(xstr("button_login_register"),
+                       clicked = self._registerClicked,
+                       tooltip = xstr("button_login_register_tooltip"))
+
         self.addButton(xstr("button_offline"),
                        clicked = self._offlineClicked,
@@ -385,4 +390,8 @@
         self._loginButton.set_sensitive(pilotID!="" and
                                         (password!="" or entranceExam))
+
+    def _registerClicked(self, button):
+        """Called when the Register button was clicked."""
+        self._wizard.jumpPage(3)
 
     def _offlineClicked(self, button):
@@ -658,22 +667,22 @@
                                          const.PLANE_AWAY)
         else:
-            self._nextDistance = 2
-            self._wizard.jumpPage(2)
+            self._nextDistance = 4
+            self._wizard.jumpPage(4)
 
     def _fleetRetrieved(self, fleet):
         """Called when the fleet has been retrieved."""
         if fleet is None:
-            self._nextDistance = 2
-            self._wizard.jumpPage(2)
+            self._nextDistance = 4
+            self._wizard.jumpPage(4)
         else:
             plane = fleet[self._wizard._bookedFlight.tailNumber]
             if plane is None:
-                self._nextDistance = 2
-                self._wizard.jumpPage(2)
+                self._nextDistance = 4
+                self._wizard.jumpPage(4)
             elif plane.gateNumber is not None and \
                  not fleet.isGateConflicting(plane):
                 self._wizard._departureGate = plane.gateNumber
-                self._nextDistance = 2
-                self._wizard.jumpPage(2)
+                self._nextDistance = 4
+                self._wizard.jumpPage(4)
             else:
                 self._nextDistance = 1
@@ -682,6 +691,6 @@
     def _planeUpdated(self, success):
         """Callback for the plane updating."""
-        self._nextDistance = 2
-        self._wizard.jumpPage(2)
+        self._nextDistance = 4
+        self._wizard.jumpPage(4)
 
     def _getSaveDialog(self):
@@ -849,5 +858,5 @@
             self._gateSelected()
         else:
-            self._wizard.nextPage()
+            self._wizard.jumpPage(3)
 
     def _rowActivated(self, flightList, path, column):
@@ -871,5 +880,5 @@
         """Callback for the plane updating call."""
         if success is None or success:
-            self._wizard.nextPage()
+            self._wizard.jumpPage(3)
         else:
             dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
@@ -887,7 +896,280 @@
         """Called when the fleet has been retrieved."""
         if fleet is None:
-            self._wizard.nextPage()
+            self._wizard.jumpPage(3)
         else:
             self.activate()
+
+#-----------------------------------------------------------------------------
+
+class RegisterPage(Page):
+    """A page to enter the registration data."""
+
+    # The minimal year of birth
+    _minYearOfBirth = 1900
+
+    # The maximal year of birth
+    _maxYearOfBirth = datetime.date.today().year
+
+    # The regular expression to check the e-mail address with
+    _emailAddressRE = re.compile("[^@]+@[^@]+\.[^@]+")
+
+    def __init__(self, wizard):
+        """Construct the registration page."""
+        super(RegisterPage, self).__init__(wizard, xstr("register_title"),
+                                           xstr("register_help"))
+
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(11, 3)
+        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("register_name"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 0, 1)
+
+        self._name = gtk.Entry()
+        self._name.connect("changed", self._updateButtons)
+        self._name.set_tooltip_text(xstr("register_name_tooltip"))
+        self._name.set_width_chars(30)
+        table.attach(self._name, 1, 2, 0, 1)
+        label.set_mnemonic_widget(self._name)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_year_of_birth"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 1, 2)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+
+        self._yearOfBirth = gtk.SpinButton()
+        self._yearOfBirth.set_increments(step = 1, page = 10)
+        self._yearOfBirth.set_range(min = RegisterPage._minYearOfBirth,
+                                    max = RegisterPage._maxYearOfBirth)
+        self._yearOfBirth.set_numeric(True)
+        self._yearOfBirth.set_tooltip_text(xstr("register_year_of_birth_tooltip"))
+        self._yearOfBirth.set_width_chars(5)
+        self._yearOfBirth.connect("changed", self._updateButtons)
+        self._yearOfBirth.connect("value-changed", self._updateButtons)
+        alignment.add(self._yearOfBirth)
+        table.attach(alignment, 1, 2, 1, 2)
+        label.set_mnemonic_widget(self._yearOfBirth)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_email"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 2, 3)
+
+        self._emailAddress = gtk.Entry()
+        self._emailAddress.connect("changed", self._updateButtons)
+        self._emailAddress.set_tooltip_text(xstr("register_email_tooltip"))
+        table.attach(self._emailAddress, 1, 2, 2, 3)
+        label.set_mnemonic_widget(self._emailAddress)
+
+        self._emailAddressPublic = gtk.CheckButton(xstr("register_email_public"))
+        self._emailAddressPublic.set_use_underline(True)
+        self._emailAddressPublic.set_tooltip_text(xstr("register_email_public_tooltip"))
+        table.attach(self._emailAddressPublic, 2, 3, 2, 3)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_vatsim_id"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 3, 4)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+        self._vatsimID = IntegerEntry()
+        self._vatsimID.connect("changed", self._updateButtons)
+        self._vatsimID.set_tooltip_text(xstr("register_vatsim_id_tooltip"))
+        self._vatsimID.set_width_chars(7)
+        alignment.add(self._vatsimID)
+        table.attach(alignment, 1, 2, 3, 4)
+        label.set_mnemonic_widget(self._vatsimID)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_ivao_id"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 4, 5)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
+                                  xscale = 0.0, yscale = 0.0)
+        self._ivaoID = IntegerEntry()
+        self._ivaoID.connect("changed", self._updateButtons)
+        self._ivaoID.set_tooltip_text(xstr("register_ivao_id_tooltip"))
+        self._ivaoID.set_width_chars(7)
+        alignment.add(self._ivaoID)
+        table.attach(alignment, 1, 2, 4, 5)
+        label.set_mnemonic_widget(self._ivaoID)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_phone_num"))
+        label.set_use_underline(True)
+        label.set_use_markup(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 5, 6)
+
+        self._phoneNumber = gtk.Entry()
+        self._phoneNumber.set_tooltip_text(xstr("register_phone_num_tooltip"))
+        table.attach(self._phoneNumber, 1, 2, 5, 6)
+        label.set_mnemonic_widget(self._phoneNumber)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_nationality"))
+        label.set_use_underline(True)
+        label.set_use_markup(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 6, 7)
+
+
+        self._nationality = gtk.Entry()
+        self._nationality.set_tooltip_text(xstr("register_nationality_tooltip"))
+        table.attach(self._nationality, 1, 2, 6, 7)
+        label.set_mnemonic_widget(self._nationality)
+
+        placeholder = gtk.Label()
+        placeholder.set_text(xstr("register_password_mismatch"))
+        placeholder.set_use_markup(True)
+        placeholder.set_child_visible(False)
+        placeholder.hide()
+        table.attach(placeholder, 2, 3, 6, 7)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_password"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 8, 9)
+
+        self._password = gtk.Entry()
+        self._password.set_visibility(False)
+        self._password.connect("changed", self._updateButtons)
+        self._password.set_tooltip_text(xstr("register_password_tooltip"))
+        table.attach(self._password, 1, 2, 8, 9)
+        label.set_mnemonic_widget(self._password)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        self._passwordStatus = gtk.Label()
+        alignment.add(self._passwordStatus)
+        table.attach(alignment, 2, 3, 8, 9)
+
+        labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
+                                       xscale = 0.0, yscale = 0.0)
+        label = gtk.Label(xstr("register_password2"))
+        label.set_use_underline(True)
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 9, 10)
+
+        self._password2 = gtk.Entry()
+        self._password2.set_visibility(False)
+        self._password2.connect("changed", self._updateButtons)
+        self._password2.set_tooltip_text(xstr("register_password2_tooltip"))
+        table.attach(self._password2, 1, 2, 9, 10)
+        label.set_mnemonic_widget(self._password2)
+
+        alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+        self._password2Status = gtk.Label()
+        alignment.add(self._password2Status)
+        table.attach(alignment, 2, 3, 9, 10)
+
+        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, 10, 11)
+
+        cancelButton = \
+          self.addButton(xstr("button_cancel"))
+        cancelButton.connect("clicked", self._cancelClicked)
+
+        self._registerButton = \
+          self.addButton(xstr("button_register"), default = True,
+                              tooltip = xstr("button_register_tooltip"))
+        self._registerButton.connect("clicked", self._registerClicked)
+
+        self._updateButtons()
+
+    def activate(self):
+        """Setup the route from the booked flight."""
+        self._yearOfBirth.set_value(0)
+        self._yearOfBirth.set_text("")
+        self._updateButtons()
+
+    def _updateButtons(self, widget = None):
+        """Update the sensitive state of the buttons"""
+        name = self._name.get_text()
+        nameLength = len(name)
+        nameSpacePosition = name.find(" ")
+
+        yearOfBirthText = self._yearOfBirth.get_text()
+        yearOfBirth = int(yearOfBirthText) if yearOfBirthText else 0
+
+        emailAddressText = self._emailAddress.get_text()
+        emailAddressMatch = RegisterPage._emailAddressRE.match(emailAddressText)
+
+        vatsimID = self._vatsimID.get_int()
+        ivaoID = self._ivaoID.get_int()
+
+        password = self._password.get_text()
+        password2 = self._password2.get_text()
+        if not password:
+            self._passwordStatus.set_text("")
+        elif len(password)<5:
+            self._passwordStatus.set_text(xstr("register_password_too_short"))
+        else:
+            self._passwordStatus.set_text(xstr("register_password_ok"))
+        self._passwordStatus.set_use_markup(True)
+
+        if len(password)<5 or not password2:
+            self._password2Status.set_text("")
+        elif password!=password2:
+            self._password2Status.set_text(xstr("register_password_mismatch"))
+        else:
+            self._password2Status.set_text(xstr("register_password_ok"))
+        self._password2Status.set_use_markup(True)
+
+        sensitive = \
+            nameLength >= 5 and nameSpacePosition != -1 and \
+            nameSpacePosition<(nameLength-2) and \
+            yearOfBirth>=RegisterPage._minYearOfBirth and \
+            yearOfBirth<=RegisterPage._maxYearOfBirth and \
+            emailAddressMatch is not None and \
+            (vatsimID>=800000 or ivaoID>=100000) and \
+            len(password)>=5 and password==password2
+
+        self._registerButton.set_sensitive(sensitive)
+
+    def _cancelClicked(self, button):
+        """Called when the Cancel button is clicked."""
+        self.goBack()
+
+    def _registerClicked(self, button):
+        """Called when the Register button is clicked."""
+
+#-----------------------------------------------------------------------------
+
+class StudentPage(Page):
+    """A page displayed to students after logging in."""
+    def __init__(self, wizard):
+        """Construct the student page."""
+        super(StudentPage, self).__init__(wizard, xstr("student_title"),
+                                          xstr("student_help"))
 
 #-----------------------------------------------------------------------------
@@ -4256,4 +4538,6 @@
         self._pages.append(FlightSelectionPage(self))
         self._pages.append(GateSelectionPage(self))
+        self._pages.append(RegisterPage(self))
+        self._pages.append(StudentPage(self))
         self._pages.append(ConnectPage(self))
         self._payloadPage = PayloadPage(self)
