Stop hildonizing widgets that don't exist, now and in the future
[gc-dialer] / src / dc_glade.py
index 14b6db5..33ad67a 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/python2.5
 
 """
 #!/usr/bin/python2.5
 
 """
-DialCentral - Front end for Google's Grand Central service.
+DialCentral - Front end for Google's GoogleVoice service.
 Copyright (C) 2008  Mark Bergman bergman AT merctech DOT com
 
 This library is free software; you can redistribute it and/or
 Copyright (C) 2008  Mark Bergman bergman AT merctech DOT com
 
 This library is free software; you can redistribute it and/or
@@ -17,14 +17,6 @@ Lesser General Public License for more details.
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-@todo Figure out how to integrate with the Maemo contacts app
-@todo Look into an actor system
-@bug Session timeouts are bad, possible solutions:
-       @li For every X minutes, if logged in, attempt login
-       @li Restructure so code handling login/dial/sms is beneath everything else and login attempts are made if those fail
-@todo Can't text from dialpad (so can't do any arbitrary number texts)
-@todo Add logging support to make debugging issues for people a lot easier
 """
 
 
 """
 
 
@@ -37,24 +29,23 @@ import threading
 import base64
 import ConfigParser
 import itertools
 import base64
 import ConfigParser
 import itertools
-import warnings
+import logging
 
 import gtk
 import gtk.glade
 
 
 import gtk
 import gtk.glade
 
-try:
-       import hildon
-except ImportError:
-       hildon = None
-
 import constants
 import constants
+import hildonize
 import gtk_toolbox
 
 
 import gtk_toolbox
 
 
+_moduleLogger = logging.getLogger("dc_glade")
+
+
 def getmtime_nothrow(path):
        try:
                return os.path.getmtime(path)
 def getmtime_nothrow(path):
        try:
                return os.path.getmtime(path)
-       except StandardError:
+       except Exception:
                return 0
 
 
                return 0
 
 
@@ -82,9 +73,8 @@ class Dialcentral(object):
        ACCOUNT_TAB = 4
 
        NULL_BACKEND = 0
        ACCOUNT_TAB = 4
 
        NULL_BACKEND = 0
-       GC_BACKEND = 1
        GV_BACKEND = 2
        GV_BACKEND = 2
-       BACKENDS = (NULL_BACKEND, GC_BACKEND, GV_BACKEND)
+       BACKENDS = (NULL_BACKEND, GV_BACKEND)
 
        def __init__(self):
                self._initDone = False
 
        def __init__(self):
                self._initDone = False
@@ -94,7 +84,7 @@ class Dialcentral(object):
 
                self._credentials = ("", "")
                self._selectedBackendId = self.NULL_BACKEND
 
                self._credentials = ("", "")
                self._selectedBackendId = self.NULL_BACKEND
-               self._defaultBackendId = self.GC_BACKEND
+               self._defaultBackendId = self.GV_BACKEND
                self._phoneBackends = None
                self._dialpads = None
                self._accountViews = None
                self._phoneBackends = None
                self._dialpads = None
                self._accountViews = None
@@ -119,45 +109,57 @@ class Dialcentral(object):
                self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
                self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
 
                self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
                self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
 
-               self._app = None
                self._isFullScreen = False
                self._isFullScreen = False
-               if hildon is not None:
-                       self._app = hildon.Program()
-                       oldWindow = self._window
-                       self._window = hildon.Window()
-                       oldWindow.get_child().reparent(self._window)
-                       self._app.add_window(self._window)
-
-                       try:
-                               self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
-                               self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
-                               self._widgetTree.get_widget("callbackcombo").get_child().set_property('hildon-input-mode', (1 << 4))
-                       except TypeError, e:
-                               warnings.warn(e.message)
-                       hildon.hildon_helper_set_thumb_scrollbar(self._widgetTree.get_widget('recent_scrolledwindow'), True)
-                       hildon.hildon_helper_set_thumb_scrollbar(self._widgetTree.get_widget('message_scrolledwindow'), True)
-                       hildon.hildon_helper_set_thumb_scrollbar(self._widgetTree.get_widget('contacts_scrolledwindow'), True)
-
-                       gtkMenu = self._widgetTree.get_widget("dialpad_menubar")
-                       menu = gtk.Menu()
-                       for child in gtkMenu.get_children():
-                               child.reparent(menu)
-                       self._window.set_menu(menu)
-                       gtkMenu.destroy()
-
-                       self._window.connect("key-press-event", self._on_key_press)
-                       self._window.connect("window-state-event", self._on_window_state_change)
-               else:
-                       pass # warnings.warn("No Hildon", UserWarning, 2)
+               self._app = hildonize.get_app_class()()
+               self._window = hildonize.hildonize_window(self._app, self._window)
+               hildonize.hildonize_text_entry(self._widgetTree.get_widget("usernameentry"))
+               hildonize.hildonize_password_entry(self._widgetTree.get_widget("passwordentry"))
+
+               for scrollingWidgetName in (
+                       'recent_scrolledwindow',
+                       'message_scrolledwindow',
+                       'contacts_scrolledwindow',
+                       "smsMessages_scrolledwindow",
+               ):
+                       scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
+                       assert scrollingWidget is not None, scrollingWidgetName
+                       hildonize.hildonize_scrollwindow(scrollingWidget)
+               for scrollingWidgetName in (
+                       "smsMessage_scrolledEntry",
+               ):
+                       scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
+                       assert scrollingWidget is not None, scrollingWidgetName
+                       hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
+
+               for buttonName in (
+                       "back",
+                       "addressbookSelectButton",
+                       "sendSmsButton",
+                       "dialButton",
+                       "selectButton",
+                       "cancelSmsButton",
+                       "callbackSelectButton",
+                       "minutesEntryButton",
+                       "clearcookies",
+                       "phoneTypeSelection",
+               ):
+                       button = self._widgetTree.get_widget(buttonName)
+                       assert button is not None, buttonName
+                       hildonize.set_button_thumb_selectable(button)
+
+               replacementButtons = [gtk.Button("Test")]
+               menu = hildonize.hildonize_menu(
+                       self._window,
+                       self._widgetTree.get_widget("dialpad_menubar"),
+                       replacementButtons
+               )
 
 
-               # If under hildon, rely on the application name being shown
-               if hildon is None:
-                       self._window.set_title("%s" % constants.__pretty_app_name__)
+               self._window.connect("key-press-event", self._on_key_press)
+               self._window.connect("window-state-event", self._on_window_state_change)
+               if not hildonize.IS_HILDON_SUPPORTED:
+                       _moduleLogger.warning("No hildonization support")
 
 
-               callbackMapping = {
-                       "on_dialpad_quit": self._on_close,
-               }
-               self._widgetTree.signal_autoconnect(callbackMapping)
+               hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
 
                self._window.connect("destroy", self._on_close)
                self._window.set_default_size(800, 300)
 
                self._window.connect("destroy", self._on_close)
                self._window.set_default_size(800, 300)
@@ -165,7 +167,7 @@ class Dialcentral(object):
 
                self._loginSink = gtk_toolbox.threaded_stage(
                        gtk_toolbox.comap(
 
                self._loginSink = gtk_toolbox.threaded_stage(
                        gtk_toolbox.comap(
-                               self.attempt_login,
+                               self._attempt_login,
                                gtk_toolbox.null_sink(),
                        )
                )
                                gtk_toolbox.null_sink(),
                        )
                )
@@ -178,8 +180,8 @@ class Dialcentral(object):
                """
                If something can be done after the UI loads, push it here so it's not blocking the UI
                """
                """
                If something can be done after the UI loads, push it here so it's not blocking the UI
                """
+               # Barebones UI handlers
                try:
                try:
-                       # Barebones UI handlers
                        import null_backend
                        import null_views
 
                        import null_backend
                        import null_views
 
@@ -196,11 +198,15 @@ class Dialcentral(object):
                                self._recentViews[self._selectedBackendId].enable()
                                self._messagesViews[self._selectedBackendId].enable()
                                self._contactsViews[self._selectedBackendId].enable()
                                self._recentViews[self._selectedBackendId].enable()
                                self._messagesViews[self._selectedBackendId].enable()
                                self._contactsViews[self._selectedBackendId].enable()
+               except Exception, e:
+                       with gtk_toolbox.gtk_lock():
+                               self._errorDisplay.push_exception()
 
 
-                       # Setup maemo specifics
+               # Setup maemo specifics
+               try:
                        try:
                                import osso
                        try:
                                import osso
-                       except ImportError:
+                       except (ImportError, OSError):
                                osso = None
                        self._osso = None
                        if osso is not None:
                                osso = None
                        self._osso = None
                        if osso is not None:
@@ -208,22 +214,31 @@ class Dialcentral(object):
                                device = osso.DeviceState(self._osso)
                                device.set_device_state_callback(self._on_device_state_change, 0)
                        else:
                                device = osso.DeviceState(self._osso)
                                device.set_device_state_callback(self._on_device_state_change, 0)
                        else:
-                               pass # warnings.warn("No OSSO", UserWarning, 2)
+                               _moduleLogger.warning("No device state support")
 
                        try:
                                import alarm_handler
                                self._alarmHandler = alarm_handler.AlarmHandler()
 
                        try:
                                import alarm_handler
                                self._alarmHandler = alarm_handler.AlarmHandler()
-                       except ImportError:
+                       except (ImportError, OSError):
                                alarm_handler = None
                                alarm_handler = None
-                       if hildon is not None:
-                               import led_handler
-                               self._ledHandler = led_handler.LedHandler()
-                               self._ledHandler.off()
+                       except Exception:
+                               with gtk_toolbox.gtk_lock():
+                                       self._errorDisplay.push_exception()
+                               alarm_handler = None
+                               _moduleLogger.warning("No notification support")
+                       if hildonize.IS_HILDON_SUPPORTED:
+                               try:
+                                       import led_handler
+                                       self._ledHandler = led_handler.LedHandler()
+                               except Exception, e:
+                                       _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
+                                       self._ledHandler = None
+                       else:
+                               self._ledHandler = None
 
 
-                       # Setup maemo specifics
                        try:
                                import conic
                        try:
                                import conic
-                       except ImportError:
+                       except (ImportError, OSError):
                                conic = None
                        self._connection = None
                        if conic is not None:
                                conic = None
                        self._connection = None
                        if conic is not None:
@@ -231,98 +246,81 @@ class Dialcentral(object):
                                self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
                                self._connection.request_connection(conic.CONNECT_FLAG_NONE)
                        else:
                                self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
                                self._connection.request_connection(conic.CONNECT_FLAG_NONE)
                        else:
-                               pass # warnings.warn("No Internet Connectivity API ", UserWarning)
+                               _moduleLogger.warning("No connection support")
+               except Exception, e:
+                       with gtk_toolbox.gtk_lock():
+                               self._errorDisplay.push_exception()
 
 
-                       # Setup costly backends
+               # Setup costly backends
+               try:
                        import gv_backend
                        import gv_backend
-                       import gc_backend
                        import file_backend
                        import file_backend
-                       import evo_backend
-                       import gc_views
+                       import gv_views
 
                        try:
                                os.makedirs(constants._data_path_)
                        except OSError, e:
                                if e.errno != 17:
                                        raise
 
                        try:
                                os.makedirs(constants._data_path_)
                        except OSError, e:
                                if e.errno != 17:
                                        raise
-                       gcCookiePath = os.path.join(constants._data_path_, "gc_cookies.txt")
                        gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
                        gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
-                       self._defaultBackendId = self._guess_preferred_backend((
-                               (self.GC_BACKEND, gcCookiePath),
-                               (self.GV_BACKEND, gvCookiePath),
-                       ))
 
                        self._phoneBackends.update({
 
                        self._phoneBackends.update({
-                               self.GC_BACKEND: gc_backend.GCDialer(gcCookiePath),
                                self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
                        })
                        with gtk_toolbox.gtk_lock():
                                self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
                        })
                        with gtk_toolbox.gtk_lock():
-                               unifiedDialpad = gc_views.Dialpad(self._widgetTree, self._errorDisplay)
-                               unifiedDialpad.set_number("")
+                               unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
                                self._dialpads.update({
                                self._dialpads.update({
-                                       self.GC_BACKEND: unifiedDialpad,
                                        self.GV_BACKEND: unifiedDialpad,
                                })
                                self._accountViews.update({
                                        self.GV_BACKEND: unifiedDialpad,
                                })
                                self._accountViews.update({
-                                       self.GC_BACKEND: gc_views.AccountInfo(
-                                               self._widgetTree, self._phoneBackends[self.GC_BACKEND], None, self._errorDisplay
-                                       ),
-                                       self.GV_BACKEND: gc_views.AccountInfo(
+                                       self.GV_BACKEND: gv_views.AccountInfo(
                                                self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
                                        ),
                                })
                                                self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
                                        ),
                                })
-                               self._accountViews[self.GC_BACKEND].save_everything = lambda *args: None
                                self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
                                self._recentViews.update({
                                self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
                                self._recentViews.update({
-                                       self.GC_BACKEND: gc_views.RecentCallsView(
-                                               self._widgetTree, self._phoneBackends[self.GC_BACKEND], self._errorDisplay
-                                       ),
-                                       self.GV_BACKEND: gc_views.RecentCallsView(
+                                       self.GV_BACKEND: gv_views.RecentCallsView(
                                                self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
                                        ),
                                })
                                self._messagesViews.update({
                                                self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
                                        ),
                                })
                                self._messagesViews.update({
-                                       self.GC_BACKEND: null_views.MessagesView(self._widgetTree),
-                                       self.GV_BACKEND: gc_views.MessagesView(
+                                       self.GV_BACKEND: gv_views.MessagesView(
                                                self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
                                        ),
                                })
                                self._contactsViews.update({
                                                self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
                                        ),
                                })
                                self._contactsViews.update({
-                                       self.GC_BACKEND: gc_views.ContactsView(
-                                               self._widgetTree, self._phoneBackends[self.GC_BACKEND], self._errorDisplay
-                                       ),
-                                       self.GV_BACKEND: gc_views.ContactsView(
+                                       self.GV_BACKEND: gv_views.ContactsView(
                                                self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
                                        ),
                                })
 
                                                self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
                                        ),
                                })
 
-                       evoBackend = evo_backend.EvolutionAddressBook()
                        fsContactsPath = os.path.join(constants._data_path_, "contacts")
                        fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
                        fsContactsPath = os.path.join(constants._data_path_, "contacts")
                        fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
-                       for backendId in (self.GV_BACKEND, self.GC_BACKEND):
-                               self._dialpads[backendId].number_selected = self._select_action
-                               self._recentViews[backendId].number_selected = self._select_action
-                               self._messagesViews[backendId].number_selected = self._select_action
-                               self._contactsViews[backendId].number_selected = self._select_action
-
-                               addressBooks = [
-                                       self._phoneBackends[backendId],
-                                       evoBackend,
-                                       fileBackend,
-                               ]
-                               mergedBook = gc_views.MergedAddressBook(addressBooks, gc_views.MergedAddressBook.advanced_lastname_sorter)
-                               self._contactsViews[backendId].append(mergedBook)
-                               self._contactsViews[backendId].extend(addressBooks)
-                               self._contactsViews[backendId].open_addressbook(*self._contactsViews[backendId].get_addressbooks().next()[0][0:2])
+
+                       self._dialpads[self.GV_BACKEND].number_selected = self._select_action
+                       self._recentViews[self.GV_BACKEND].number_selected = self._select_action
+                       self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
+                       self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
+
+                       addressBooks = [
+                               self._phoneBackends[self.GV_BACKEND],
+                               fileBackend,
+                       ]
+                       mergedBook = gv_views.MergedAddressBook(addressBooks, gv_views.MergedAddressBook.advanced_lastname_sorter)
+                       self._contactsViews[self.GV_BACKEND].append(mergedBook)
+                       self._contactsViews[self.GV_BACKEND].extend(addressBooks)
+                       self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
 
                        callbackMapping = {
                                "on_paste": self._on_paste,
                                "on_refresh": self._on_menu_refresh,
                                "on_clearcookies_clicked": self._on_clearcookies_clicked,
 
                        callbackMapping = {
                                "on_paste": self._on_paste,
                                "on_refresh": self._on_menu_refresh,
                                "on_clearcookies_clicked": self._on_clearcookies_clicked,
-                               "on_notebook_switch_page": self._on_notebook_switch_page,
                                "on_about_activate": self._on_about_activate,
                        }
                                "on_about_activate": self._on_about_activate,
                        }
-                       self._widgetTree.signal_autoconnect(callbackMapping)
+                       if hildonize.GTK_MENU_USED:
+                               self._widgetTree.signal_autoconnect(callbackMapping)
+                       self._notebook.connect("switch-page", self._on_notebook_switch_page)
+                       self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
 
                        with gtk_toolbox.gtk_lock():
                                self._originalCurrentLabels = [
 
                        with gtk_toolbox.gtk_lock():
                                self._originalCurrentLabels = [
@@ -342,16 +340,17 @@ class Dialcentral(object):
                        config.read(constants._user_settings_)
                        with gtk_toolbox.gtk_lock():
                                self.load_settings(config)
                        config.read(constants._user_settings_)
                        with gtk_toolbox.gtk_lock():
                                self.load_settings(config)
-
-                       self._spawn_attempt_login(2)
                except Exception, e:
                        with gtk_toolbox.gtk_lock():
                                self._errorDisplay.push_exception()
                except Exception, e:
                        with gtk_toolbox.gtk_lock():
                                self._errorDisplay.push_exception()
+               finally:
+                       self._spawn_attempt_login(2)
 
 
-       def attempt_login(self, numOfAttempts = 10, force = False):
-               """
-               @todo Handle user notification better like attempting to login and failed login
+       def _spawn_attempt_login(self, *args):
+               self._loginSink.send(args)
 
 
+       def _attempt_login(self, numOfAttempts = 10, force = False):
+               """
                @note This must be run outside of the UI lock
                """
                try:
                @note This must be run outside of the UI lock
                """
                try:
@@ -361,25 +360,31 @@ class Dialcentral(object):
                        serviceId = self.NULL_BACKEND
                        loggedIn = False
                        if not force:
                        serviceId = self.NULL_BACKEND
                        loggedIn = False
                        if not force:
+                               with gtk_toolbox.gtk_lock():
+                                       banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
                                try:
                                        self.refresh_session()
                                        serviceId = self._defaultBackendId
                                        loggedIn = True
                                try:
                                        self.refresh_session()
                                        serviceId = self._defaultBackendId
                                        loggedIn = True
-                               except StandardError, e:
-                                       warnings.warn('Session refresh failed with the following message "%s"' % e.message, UserWarning, 2)
+                               except Exception, e:
+                                       _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
+                               finally:
+                                       with gtk_toolbox.gtk_lock():
+                                               hildonize.show_busy_banner_end(banner)
 
                        if not loggedIn:
                                loggedIn, serviceId = self._login_by_user(numOfAttempts)
 
                        with gtk_toolbox.gtk_lock():
                                self._change_loggedin_status(serviceId)
 
                        if not loggedIn:
                                loggedIn, serviceId = self._login_by_user(numOfAttempts)
 
                        with gtk_toolbox.gtk_lock():
                                self._change_loggedin_status(serviceId)
-               except StandardError, e:
+                               if loggedIn:
+                                       hildonize.show_information_banner(self._window, "Logged In")
+                               else:
+                                       hildonize.show_information_banner(self._window, "Login Failed")
+               except Exception, e:
                        with gtk_toolbox.gtk_lock():
                                self._errorDisplay.push_exception()
 
                        with gtk_toolbox.gtk_lock():
                                self._errorDisplay.push_exception()
 
-       def _spawn_attempt_login(self, *args):
-               self._loginSink.send(args)
-
        def refresh_session(self):
                """
                @note Thread agnostic
        def refresh_session(self):
                """
                @note Thread agnostic
@@ -401,10 +406,7 @@ class Dialcentral(object):
                """
                loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
                if loggedIn:
                """
                loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
                if loggedIn:
-                       warnings.warn(
-                               "Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId],
-                               UserWarning, 2
-                       )
+                       _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
                return loggedIn
 
        def _login_by_settings(self):
                return loggedIn
 
        def _login_by_settings(self):
@@ -415,10 +417,7 @@ class Dialcentral(object):
                loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
                if loggedIn:
                        self._credentials = username, password
                loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
                if loggedIn:
                        self._credentials = username, password
-                       warnings.warn(
-                               "Logged into %r through settings" % self._phoneBackends[self._defaultBackendId],
-                               UserWarning, 2
-                       )
+                       _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
                return loggedIn
 
        def _login_by_user(self, numOfAttempts):
                return loggedIn
 
        def _login_by_user(self, numOfAttempts):
@@ -426,30 +425,32 @@ class Dialcentral(object):
                @note This must be run outside of the UI lock
                """
                loggedIn, (username, password) = False, self._credentials
                @note This must be run outside of the UI lock
                """
                loggedIn, (username, password) = False, self._credentials
-               tmpServiceId = self.NULL_BACKEND
+               tmpServiceId = self.GV_BACKEND
                for attemptCount in xrange(numOfAttempts):
                        if loggedIn:
                                break
                for attemptCount in xrange(numOfAttempts):
                        if loggedIn:
                                break
-                       availableServices = (
-                               (self.GV_BACKEND, "Google Voice"),
-                               (self.GC_BACKEND, "Grand Central"),
-                       )
                        with gtk_toolbox.gtk_lock():
                        with gtk_toolbox.gtk_lock():
-                               credentials = self._credentialsDialog.request_credentials_from(
-                                       availableServices, defaultCredentials = self._credentials
+                               credentials = self._credentialsDialog.request_credentials(
+                                       defaultCredentials = self._credentials
                                )
                                )
-                       tmpServiceId, username, password = credentials
-                       loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
+                               if not self._phoneBackends[tmpServiceId].get_callback_number():
+                                       # subtle reminder to the users to configure things
+                                       self._notebook.set_current_page(self.ACCOUNT_TAB)
+                               banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
+                       try:
+                               username, password = credentials
+                               loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
+                       finally:
+                               with gtk_toolbox.gtk_lock():
+                                       hildonize.show_busy_banner_end(banner)
 
                if loggedIn:
                        serviceId = tmpServiceId
                        self._credentials = username, password
 
                if loggedIn:
                        serviceId = tmpServiceId
                        self._credentials = username, password
-                       warnings.warn(
-                               "Logged into %r through user request" % self._phoneBackends[serviceId],
-                               UserWarning, 2
-                       )
+                       _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
                else:
                        serviceId = self.NULL_BACKEND
                else:
                        serviceId = self.NULL_BACKEND
+                       self._notebook.set_current_page(self.ACCOUNT_TAB)
 
                return loggedIn, serviceId
 
 
                return loggedIn, serviceId
 
@@ -484,16 +485,18 @@ class Dialcentral(object):
 
                if self._phoneBackends[self._selectedBackendId].get_callback_number() is None:
                        self._phoneBackends[self._selectedBackendId].set_sane_callback()
 
                if self._phoneBackends[self._selectedBackendId].get_callback_number() is None:
                        self._phoneBackends[self._selectedBackendId].set_sane_callback()
-               self._accountViews[self._selectedBackendId].update()
 
                self._selectedBackendId = newStatus
 
 
                self._selectedBackendId = newStatus
 
+               self._accountViews[self._selectedBackendId].update()
+               self._refresh_active_tab()
+
        def load_settings(self, config):
                """
                @note UI Thread
                """
                try:
        def load_settings(self, config):
                """
                @note UI Thread
                """
                try:
-                       self._defaultBackendId = int(config.get(constants.__pretty_app_name__, "active"))
+                       self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
                        blobs = (
                                config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
                                for i in xrange(len(self._credentials))
                        blobs = (
                                config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
                                for i in xrange(len(self._credentials))
@@ -507,20 +510,18 @@ class Dialcentral(object):
                        if self._alarmHandler is not None:
                                self._alarmHandler.load_settings(config, "alarm")
                except ConfigParser.NoOptionError, e:
                        if self._alarmHandler is not None:
                                self._alarmHandler.load_settings(config, "alarm")
                except ConfigParser.NoOptionError, e:
-                       warnings.warn(
+                       _moduleLogger.exception(
                                "Settings file %s is missing section %s" % (
                                        constants._user_settings_,
                                        e.section,
                                ),
                                "Settings file %s is missing section %s" % (
                                        constants._user_settings_,
                                        e.section,
                                ),
-                               stacklevel=2
                        )
                except ConfigParser.NoSectionError, e:
                        )
                except ConfigParser.NoSectionError, e:
-                       warnings.warn(
+                       _moduleLogger.exception(
                                "Settings file %s is missing section %s" % (
                                        constants._user_settings_,
                                        e.section,
                                ),
                                "Settings file %s is missing section %s" % (
                                        constants._user_settings_,
                                        e.section,
                                ),
-                               stacklevel=2
                        )
 
                for backendId, view in itertools.chain(
                        )
 
                for backendId, view in itertools.chain(
@@ -534,28 +535,48 @@ class Dialcentral(object):
                        try:
                                view.load_settings(config, sectionName)
                        except ConfigParser.NoOptionError, e:
                        try:
                                view.load_settings(config, sectionName)
                        except ConfigParser.NoOptionError, e:
-                               warnings.warn(
+                               _moduleLogger.exception(
                                        "Settings file %s is missing section %s" % (
                                                constants._user_settings_,
                                                e.section,
                                        ),
                                        "Settings file %s is missing section %s" % (
                                                constants._user_settings_,
                                                e.section,
                                        ),
-                                       stacklevel=2
                                )
                        except ConfigParser.NoSectionError, e:
                                )
                        except ConfigParser.NoSectionError, e:
-                               warnings.warn(
+                               _moduleLogger.exception(
                                        "Settings file %s is missing section %s" % (
                                                constants._user_settings_,
                                                e.section,
                                        ),
                                        "Settings file %s is missing section %s" % (
                                                constants._user_settings_,
                                                e.section,
                                        ),
-                                       stacklevel=2
                                )
 
                                )
 
+               try:
+                       previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
+                       if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
+                               hildonize.window_to_landscape(self._window)
+                       elif previousOrientation == gtk.ORIENTATION_VERTICAL:
+                               hildonize.window_to_portrait(self._window)
+               except ConfigParser.NoOptionError, e:
+                       _moduleLogger.exception(
+                               "Settings file %s is missing section %s" % (
+                                       constants._user_settings_,
+                                       e.section,
+                               ),
+                       )
+               except ConfigParser.NoSectionError, e:
+                       _moduleLogger.exception(
+                               "Settings file %s is missing section %s" % (
+                                       constants._user_settings_,
+                                       e.section,
+                               ),
+                       )
+
        def save_settings(self, config):
                """
                @note Thread Agnostic
                """
                config.add_section(constants.__pretty_app_name__)
                config.set(constants.__pretty_app_name__, "active", str(self._selectedBackendId))
        def save_settings(self, config):
                """
                @note Thread Agnostic
                """
                config.add_section(constants.__pretty_app_name__)
                config.set(constants.__pretty_app_name__, "active", str(self._selectedBackendId))
+               config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
                for i, value in enumerate(self._credentials):
                        blob = base64.b64encode(value)
                        config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
                for i, value in enumerate(self._credentials):
                        blob = base64.b64encode(value)
                        config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
@@ -574,14 +595,6 @@ class Dialcentral(object):
                        config.add_section(sectionName)
                        view.save_settings(config, sectionName)
 
                        config.add_section(sectionName)
                        view.save_settings(config, sectionName)
 
-       def _guess_preferred_backend(self, backendAndCookiePaths):
-               modTimeAndPath = [
-                       (getmtime_nothrow(path), backendId, path)
-                       for backendId, path in backendAndCookiePaths
-               ]
-               modTimeAndPath.sort()
-               return modTimeAndPath[-1][1]
-
        def _save_settings(self):
                """
                @note Thread Agnostic
        def _save_settings(self):
                """
                @note Thread Agnostic
@@ -592,9 +605,6 @@ class Dialcentral(object):
                        config.write(configFile)
 
        def _refresh_active_tab(self):
                        config.write(configFile)
 
        def _refresh_active_tab(self):
-               if self._ledHandler is not None:
-                       self._ledHandler.off()
-
                pageIndex = self._notebook.get_current_page()
                if pageIndex == self.CONTACTS_TAB:
                        self._contactsViews[self._selectedBackendId].update(force=True)
                pageIndex = self._notebook.get_current_page()
                if pageIndex == self.CONTACTS_TAB:
                        self._contactsViews[self._selectedBackendId].update(force=True)
@@ -603,6 +613,10 @@ class Dialcentral(object):
                elif pageIndex == self.MESSAGES_TAB:
                        self._messagesViews[self._selectedBackendId].update(force=True)
 
                elif pageIndex == self.MESSAGES_TAB:
                        self._messagesViews[self._selectedBackendId].update(force=True)
 
+               if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
+                       if self._ledHandler is not None:
+                               self._ledHandler.off()
+
        def _on_close(self, *args, **kwds):
                try:
                        if self._osso is not None:
        def _on_close(self, *args, **kwds):
                try:
                        if self._osso is not None:
@@ -620,161 +634,217 @@ class Dialcentral(object):
 
                @note Hildon specific
                """
 
                @note Hildon specific
                """
-               if memory_low:
-                       for backendId in self.BACKENDS:
-                               self._phoneBackends[backendId].clear_caches()
-                       self._contactsViews[self._selectedBackendId].clear_caches()
-                       gc.collect()
+               try:
+                       if memory_low:
+                               for backendId in self.BACKENDS:
+                                       self._phoneBackends[backendId].clear_caches()
+                               self._contactsViews[self._selectedBackendId].clear_caches()
+                               gc.collect()
 
 
-               if save_unsaved_data or shutdown:
-                       self._save_settings()
+                       if save_unsaved_data or shutdown:
+                               self._save_settings()
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
        def _on_connection_change(self, connection, event, magicIdentifier):
                """
                @note Hildon specific
                """
 
        def _on_connection_change(self, connection, event, magicIdentifier):
                """
                @note Hildon specific
                """
-               import conic
-
-               status = event.get_status()
-               error = event.get_error()
-               iap_id = event.get_iap_id()
-               bearer = event.get_bearer_type()
-
-               if status == conic.STATUS_CONNECTED:
-                       if self._initDone:
-                               self._spawn_attempt_login(2)
-               elif status == conic.STATUS_DISCONNECTED:
-                       if self._initDone:
-                               self._defaultBackendId = self._selectedBackendId
-                               self._change_loggedin_status(self.NULL_BACKEND)
+               try:
+                       import conic
+
+                       status = event.get_status()
+                       error = event.get_error()
+                       iap_id = event.get_iap_id()
+                       bearer = event.get_bearer_type()
+
+                       if status == conic.STATUS_CONNECTED:
+                               if self._initDone:
+                                       self._spawn_attempt_login(2)
+                       elif status == conic.STATUS_DISCONNECTED:
+                               if self._initDone:
+                                       self._defaultBackendId = self._selectedBackendId
+                                       self._change_loggedin_status(self.NULL_BACKEND)
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
        def _on_window_state_change(self, widget, event, *args):
                """
                @note Hildon specific
                """
 
        def _on_window_state_change(self, widget, event, *args):
                """
                @note Hildon specific
                """
-               if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
-                       self._isFullScreen = True
-               else:
-                       self._isFullScreen = False
+               try:
+                       if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
+                               self._isFullScreen = True
+                       else:
+                               self._isFullScreen = False
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
        def _on_key_press(self, widget, event, *args):
                """
                @note Hildon specific
                """
 
        def _on_key_press(self, widget, event, *args):
                """
                @note Hildon specific
                """
-               if event.keyval == gtk.keysyms.F6:
-                       if self._isFullScreen:
-                               self._window.unfullscreen()
-                       else:
-                               self._window.fullscreen()
+               RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
+               try:
+                       if (
+                               event.keyval == gtk.keysyms.F6 or
+                               event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
+                       ):
+                               if self._isFullScreen:
+                                       self._window.unfullscreen()
+                               else:
+                                       self._window.fullscreen()
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
        def _on_clearcookies_clicked(self, *args):
 
        def _on_clearcookies_clicked(self, *args):
-               self._phoneBackends[self._selectedBackendId].logout()
-               self._accountViews[self._selectedBackendId].clear()
-               self._recentViews[self._selectedBackendId].clear()
-               self._messagesViews[self._selectedBackendId].clear()
-               self._contactsViews[self._selectedBackendId].clear()
-               self._change_loggedin_status(self.NULL_BACKEND)
-
-               self._spawn_attempt_login(2, True)
+               try:
+                       self._phoneBackends[self._selectedBackendId].logout()
+                       self._accountViews[self._selectedBackendId].clear()
+                       self._recentViews[self._selectedBackendId].clear()
+                       self._messagesViews[self._selectedBackendId].clear()
+                       self._contactsViews[self._selectedBackendId].clear()
+                       self._change_loggedin_status(self.NULL_BACKEND)
+
+                       self._spawn_attempt_login(2, True)
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
        def _on_notebook_switch_page(self, notebook, page, pageIndex):
 
        def _on_notebook_switch_page(self, notebook, page, pageIndex):
-               self._reset_tab_refresh()
-               if pageIndex == self.RECENT_TAB:
-                       self._recentViews[self._selectedBackendId].update()
-               elif pageIndex == self.MESSAGES_TAB:
-                       self._messagesViews[self._selectedBackendId].update()
-               elif pageIndex == self.CONTACTS_TAB:
-                       self._contactsViews[self._selectedBackendId].update()
-               elif pageIndex == self.ACCOUNT_TAB:
-                       self._accountViews[self._selectedBackendId].update()
+               try:
+                       self._reset_tab_refresh()
+
+                       didRecentUpdate = False
+                       didMessagesUpdate = False
+
+                       if pageIndex == self.RECENT_TAB:
+                               didRecentUpdate = self._recentViews[self._selectedBackendId].update()
+                       elif pageIndex == self.MESSAGES_TAB:
+                               didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
+                       elif pageIndex == self.CONTACTS_TAB:
+                               self._contactsViews[self._selectedBackendId].update()
+                       elif pageIndex == self.ACCOUNT_TAB:
+                               self._accountViews[self._selectedBackendId].update()
+
+                       if didRecentUpdate or didMessagesUpdate:
+                               if self._ledHandler is not None:
+                                       self._ledHandler.off()
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
        def _set_tab_refresh(self, *args):
 
        def _set_tab_refresh(self, *args):
-               pageIndex = self._notebook.get_current_page()
-               child = self._notebook.get_nth_page(pageIndex)
-               self._notebook.get_tab_label(child).set_text("Refresh?")
+               try:
+                       pageIndex = self._notebook.get_current_page()
+                       child = self._notebook.get_nth_page(pageIndex)
+                       self._notebook.get_tab_label(child).set_text("Refresh?")
+               except Exception, e:
+                       self._errorDisplay.push_exception()
                return False
 
        def _reset_tab_refresh(self, *args):
                return False
 
        def _reset_tab_refresh(self, *args):
-               pageIndex = self._notebook.get_current_page()
-               child = self._notebook.get_nth_page(pageIndex)
-               self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
+               try:
+                       pageIndex = self._notebook.get_current_page()
+                       child = self._notebook.get_nth_page(pageIndex)
+                       self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
+               except Exception, e:
+                       self._errorDisplay.push_exception()
                return False
 
        def _on_tab_refresh(self, *args):
                return False
 
        def _on_tab_refresh(self, *args):
-               self._refresh_active_tab()
-               self._reset_tab_refresh()
+               try:
+                       self._refresh_active_tab()
+                       self._reset_tab_refresh()
+               except Exception, e:
+                       self._errorDisplay.push_exception()
                return False
 
        def _on_sms_clicked(self, number, message):
                return False
 
        def _on_sms_clicked(self, number, message):
-               assert number, "No number specified"
-               assert message, "Empty message"
                try:
                try:
-                       loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
-               except StandardError, e:
-                       loggedIn = False
-                       self._errorDisplay.push_exception()
-                       return
+                       assert number, "No number specified"
+                       assert message, "Empty message"
+                       try:
+                               loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
+                       except Exception, e:
+                               loggedIn = False
+                               self._errorDisplay.push_exception()
+                               return
 
 
-               if not loggedIn:
-                       self._errorDisplay.push_message(
-                               "Backend link with grandcentral is not working, please try again"
-                       )
-                       return
+                       if not loggedIn:
+                               self._errorDisplay.push_message(
+                                       "Backend link with GoogleVoice is not working, please try again"
+                               )
+                               return
 
 
-               dialed = False
-               try:
-                       self._phoneBackends[self._selectedBackendId].send_sms(number, message)
-                       dialed = True
-               except StandardError, e:
-                       self._errorDisplay.push_exception()
-               except ValueError, e:
+                       dialed = False
+                       try:
+                               self._phoneBackends[self._selectedBackendId].send_sms(number, message)
+                               hildonize.show_information_banner(self._window, "Sending to %s" % number)
+                               dialed = True
+                       except Exception, e:
+                               self._errorDisplay.push_exception()
+
+                       if dialed:
+                               self._dialpads[self._selectedBackendId].clear()
+               except Exception, e:
                        self._errorDisplay.push_exception()
 
        def _on_dial_clicked(self, number):
                        self._errorDisplay.push_exception()
 
        def _on_dial_clicked(self, number):
-               assert number, "No number to call"
                try:
                try:
-                       loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
-               except StandardError, e:
-                       loggedIn = False
-                       self._errorDisplay.push_exception()
-                       return
+                       assert number, "No number to call"
+                       try:
+                               loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
+                       except Exception, e:
+                               loggedIn = False
+                               self._errorDisplay.push_exception()
+                               return
 
 
-               if not loggedIn:
-                       self._errorDisplay.push_message(
-                               "Backend link with grandcentral is not working, please try again"
-                       )
-                       return
+                       if not loggedIn:
+                               self._errorDisplay.push_message(
+                                       "Backend link with GoogleVoice is not working, please try again"
+                               )
+                               return
 
 
-               dialed = False
-               try:
-                       assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
-                       self._phoneBackends[self._selectedBackendId].dial(number)
-                       dialed = True
-               except StandardError, e:
-                       self._errorDisplay.push_exception()
-               except ValueError, e:
-                       self._errorDisplay.push_exception()
+                       dialed = False
+                       try:
+                               assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
+                               self._phoneBackends[self._selectedBackendId].dial(number)
+                               hildonize.show_information_banner(self._window, "Calling %s" % number)
+                               dialed = True
+                       except Exception, e:
+                               self._errorDisplay.push_exception()
 
 
-               if dialed:
-                       self._dialpads[self._selectedBackendId].clear()
+                       if dialed:
+                               self._dialpads[self._selectedBackendId].clear()
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
        def _on_menu_refresh(self, *args):
 
        def _on_menu_refresh(self, *args):
-               self._refresh_active_tab()
+               try:
+                       self._refresh_active_tab()
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
        def _on_paste(self, *args):
 
        def _on_paste(self, *args):
-               contents = self._clipboard.wait_for_text()
-               self._dialpads[self._selectedBackendId].set_number(contents)
+               try:
+                       contents = self._clipboard.wait_for_text()
+                       if contents is not None:
+                               self._dialpads[self._selectedBackendId].set_number(contents)
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
        def _on_about_activate(self, *args):
 
        def _on_about_activate(self, *args):
-               dlg = gtk.AboutDialog()
-               dlg.set_name(constants.__pretty_app_name__)
-               dlg.set_version(constants.__version__)
-               dlg.set_copyright("Copyright 2008 - LGPL")
-               dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice/Grandcentral account.  This application is not affiliated with Google in any way")
-               dlg.set_website("http://gc-dialer.garage.maemo.org/")
-               dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
-               dlg.run()
-               dlg.destroy()
+               try:
+                       dlg = gtk.AboutDialog()
+                       dlg.set_name(constants.__pretty_app_name__)
+                       dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
+                       dlg.set_copyright("Copyright 2008 - LGPL")
+                       dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account.  This application is not affiliated with Google in any way")
+                       dlg.set_website("http://gc-dialer.garage.maemo.org/")
+                       dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
+                       dlg.run()
+                       dlg.destroy()
+               except Exception, e:
+                       self._errorDisplay.push_exception()
 
 
 def run_doctest():
 
 
 def run_doctest():
@@ -790,13 +860,13 @@ def run_doctest():
 
 def run_dialpad():
        _lock_file = os.path.join(constants._data_path_, ".lock")
 
 def run_dialpad():
        _lock_file = os.path.join(constants._data_path_, ".lock")
-       with gtk_toolbox.flock(_lock_file, 0):
-               gtk.gdk.threads_init()
+       #with gtk_toolbox.flock(_lock_file, 0):
+       gtk.gdk.threads_init()
 
 
-               if hildon is not None:
-                       gtk.set_application_name(constants.__pretty_app_name__)
-               handle = Dialcentral()
-               gtk.main()
+       if hildonize.IS_HILDON_SUPPORTED:
+               gtk.set_application_name(constants.__pretty_app_name__)
+       handle = Dialcentral()
+       gtk.main()
 
 
 class DummyOptions(object):
 
 
 class DummyOptions(object):
@@ -806,21 +876,25 @@ class DummyOptions(object):
 
 
 if __name__ == "__main__":
 
 
 if __name__ == "__main__":
-       if len(sys.argv) > 1:
-               try:
-                       import optparse
-               except ImportError:
-                       optparse = None
-
-               if optparse is not None:
-                       parser = optparse.OptionParser()
-                       parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
-                       (commandOptions, commandArgs) = parser.parse_args()
-       else:
-               commandOptions = DummyOptions()
-               commandArgs = []
+       logging.basicConfig(level=logging.DEBUG)
+       try:
+               if len(sys.argv) > 1:
+                       try:
+                               import optparse
+                       except ImportError:
+                               optparse = None
 
 
-       if commandOptions.test:
-               run_doctest()
-       else:
-               run_dialpad()
+                       if optparse is not None:
+                               parser = optparse.OptionParser()
+                               parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
+                               (commandOptions, commandArgs) = parser.parse_args()
+               else:
+                       commandOptions = DummyOptions()
+                       commandArgs = []
+
+               if commandOptions.test:
+                       run_doctest()
+               else:
+                       run_dialpad()
+       finally:
+               logging.shutdown()