4 DialCentral - Front end for Google's GoogleVoice service.
5 Copyright (C) 2008 Mark Bergman bergman AT merctech DOT com
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 from __future__ import with_statement
42 _moduleLogger = logging.getLogger("dc_glade")
43 PROFILE_STARTUP = False
46 def getmtime_nothrow(path):
48 return os.path.getmtime(path)
53 def display_error_message(msg):
54 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
56 def close(dialog, response):
58 error_dialog.connect("response", close)
62 class Dialcentral(object):
65 os.path.join(os.path.dirname(__file__), "dialcentral.glade"),
66 os.path.join(os.path.dirname(__file__), "../lib/dialcentral.glade"),
67 '/usr/lib/dialcentral/dialcentral.glade',
77 # 1 Was GrandCentral support so the gap was maintained for compatibility
79 BACKENDS = (NULL_BACKEND, GV_BACKEND)
82 self._initDone = False
83 self._connection = None
85 self._clipboard = gtk.clipboard_get()
87 self._credentials = ("", "")
88 self._selectedBackendId = self.NULL_BACKEND
89 self._defaultBackendId = self.GV_BACKEND
90 self._phoneBackends = None
92 self._accountViews = None
93 self._messagesViews = None
94 self._recentViews = None
95 self._contactsViews = None
96 self._alarmHandler = None
97 self._ledHandler = None
98 self._originalCurrentLabels = []
100 for path in self._glade_files:
101 if os.path.isfile(path):
102 self._widgetTree = gtk.glade.XML(path)
105 display_error_message("Cannot find dialcentral.glade")
109 self._window = self._widgetTree.get_widget("mainWindow")
110 self._notebook = self._widgetTree.get_widget("notebook")
111 self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
112 self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
114 self._isFullScreen = False
115 self._app = hildonize.get_app_class()()
116 self._window = hildonize.hildonize_window(self._app, self._window)
117 hildonize.hildonize_text_entry(self._widgetTree.get_widget("usernameentry"))
118 hildonize.hildonize_password_entry(self._widgetTree.get_widget("passwordentry"))
120 for scrollingWidgetName in (
121 'recent_scrolledwindow',
122 'message_scrolledwindow',
123 'contacts_scrolledwindow',
124 "smsMessages_scrolledwindow",
126 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
127 assert scrollingWidget is not None, scrollingWidgetName
128 hildonize.hildonize_scrollwindow(scrollingWidget)
129 for scrollingWidgetName in (
130 "smsMessage_scrolledEntry",
132 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
133 assert scrollingWidget is not None, scrollingWidgetName
134 hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
138 "addressbookSelectButton",
142 "callbackSelectButton",
143 "minutesEntryButton",
145 "phoneTypeSelection",
147 button = self._widgetTree.get_widget(buttonName)
148 assert button is not None, buttonName
149 hildonize.set_button_thumb_selectable(button)
151 replacementButtons = [gtk.Button("Test")]
152 menu = hildonize.hildonize_menu(
154 self._widgetTree.get_widget("dialpad_menubar"),
158 self._window.connect("key-press-event", self._on_key_press)
159 self._window.connect("window-state-event", self._on_window_state_change)
160 if not hildonize.IS_HILDON_SUPPORTED:
161 _moduleLogger.warning("No hildonization support")
163 hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
165 self._window.connect("destroy", self._on_close)
166 self._window.set_default_size(800, 300)
167 self._window.show_all()
169 self._loginSink = gtk_toolbox.threaded_stage(
172 gtk_toolbox.null_sink(),
176 if not PROFILE_STARTUP:
177 backgroundSetup = threading.Thread(target=self._idle_setup)
178 backgroundSetup.setDaemon(True)
179 backgroundSetup.start()
183 def _idle_setup(self):
185 If something can be done after the UI loads, push it here so it's not blocking the UI
187 # Barebones UI handlers
192 self._phoneBackends = {self.NULL_BACKEND: null_backend.NullDialer()}
193 with gtk_toolbox.gtk_lock():
194 self._dialpads = {self.NULL_BACKEND: null_views.Dialpad(self._widgetTree)}
195 self._accountViews = {self.NULL_BACKEND: null_views.AccountInfo(self._widgetTree)}
196 self._recentViews = {self.NULL_BACKEND: null_views.RecentCallsView(self._widgetTree)}
197 self._messagesViews = {self.NULL_BACKEND: null_views.MessagesView(self._widgetTree)}
198 self._contactsViews = {self.NULL_BACKEND: null_views.ContactsView(self._widgetTree)}
200 self._dialpads[self._selectedBackendId].enable()
201 self._accountViews[self._selectedBackendId].enable()
202 self._recentViews[self._selectedBackendId].enable()
203 self._messagesViews[self._selectedBackendId].enable()
204 self._contactsViews[self._selectedBackendId].enable()
206 with gtk_toolbox.gtk_lock():
207 self._errorDisplay.push_exception()
209 # Setup maemo specifics
213 except (ImportError, OSError):
217 self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
218 device = osso.DeviceState(self._osso)
219 device.set_device_state_callback(self._on_device_state_change, 0)
221 _moduleLogger.warning("No device state support")
225 if alarm_handler.AlarmHandler is not alarm_handler._NoneAlarmHandler:
226 self._alarmHandler = alarm_handler.AlarmHandler()
228 self._alarmHandler = None
229 except (ImportError, OSError):
232 with gtk_toolbox.gtk_lock():
233 self._errorDisplay.push_exception()
235 if alarm_handler is None:
236 _moduleLogger.warning("No notification support")
237 if hildonize.IS_HILDON_SUPPORTED:
240 self._ledHandler = led_handler.LedHandler()
242 _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
243 self._ledHandler = None
245 self._ledHandler = None
249 except (ImportError, OSError):
251 self._connection = None
252 if conic is not None:
253 self._connection = conic.Connection()
254 self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
255 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
257 _moduleLogger.warning("No connection support")
259 with gtk_toolbox.gtk_lock():
260 self._errorDisplay.push_exception()
262 # Setup costly backends
269 os.makedirs(constants._data_path_)
273 gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
275 self._phoneBackends.update({
276 self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
278 with gtk_toolbox.gtk_lock():
279 unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
280 self._dialpads.update({
281 self.GV_BACKEND: unifiedDialpad,
283 self._accountViews.update({
284 self.GV_BACKEND: gv_views.AccountInfo(
285 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
288 self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
289 self._recentViews.update({
290 self.GV_BACKEND: gv_views.RecentCallsView(
291 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
294 self._messagesViews.update({
295 self.GV_BACKEND: gv_views.MessagesView(
296 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
299 self._contactsViews.update({
300 self.GV_BACKEND: gv_views.ContactsView(
301 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
305 fsContactsPath = os.path.join(constants._data_path_, "contacts")
306 fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
308 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
309 self._recentViews[self.GV_BACKEND].number_selected = self._select_action
310 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
311 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
314 self._phoneBackends[self.GV_BACKEND],
317 mergedBook = gv_views.MergedAddressBook(addressBooks, gv_views.MergedAddressBook.advanced_lastname_sorter)
318 self._contactsViews[self.GV_BACKEND].append(mergedBook)
319 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
320 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
323 "on_paste": self._on_paste,
324 "on_refresh": self._on_menu_refresh,
325 "on_clearcookies_clicked": self._on_clearcookies_clicked,
326 "on_about_activate": self._on_about_activate,
328 if hildonize.GTK_MENU_USED:
329 self._widgetTree.signal_autoconnect(callbackMapping)
330 self._notebook.connect("switch-page", self._on_notebook_switch_page)
331 self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
333 with gtk_toolbox.gtk_lock():
334 self._originalCurrentLabels = [
335 self._notebook.get_tab_label(self._notebook.get_nth_page(pageIndex)).get_text()
336 for pageIndex in xrange(self._notebook.get_n_pages())
338 self._notebookTapHandler = gtk_toolbox.TapOrHold(self._notebook)
339 self._notebookTapHandler.enable()
340 self._notebookTapHandler.on_tap = self._reset_tab_refresh
341 self._notebookTapHandler.on_hold = self._on_tab_refresh
342 self._notebookTapHandler.on_holding = self._set_tab_refresh
343 self._notebookTapHandler.on_cancel = self._reset_tab_refresh
345 config = ConfigParser.SafeConfigParser()
346 config.read(constants._user_settings_)
347 with gtk_toolbox.gtk_lock():
348 self.load_settings(config)
350 with gtk_toolbox.gtk_lock():
351 self._errorDisplay.push_exception()
353 self._initDone = True
354 self._spawn_attempt_login()
356 def _spawn_attempt_login(self, *args):
357 self._loginSink.send(args)
359 def _attempt_login(self, force = False):
361 @note This must be run outside of the UI lock
364 assert self._initDone, "Attempting login before app is fully loaded"
366 serviceId = self.NULL_BACKEND
368 if not force and self._defaultBackendId != self.NULL_BACKEND:
369 with gtk_toolbox.gtk_lock():
370 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
372 self.refresh_session()
373 serviceId = self._defaultBackendId
376 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
378 with gtk_toolbox.gtk_lock():
379 hildonize.show_busy_banner_end(banner)
382 loggedIn, serviceId = self._login_by_user()
384 with gtk_toolbox.gtk_lock():
385 self._change_loggedin_status(serviceId)
387 hildonize.show_information_banner(self._window, "Logged In")
389 hildonize.show_information_banner(self._window, "Login Failed")
390 if not self._phoneBackends[self._defaultBackendId].get_callback_number():
391 # subtle reminder to the users to configure things
392 self._notebook.set_current_page(self.ACCOUNT_TAB)
395 with gtk_toolbox.gtk_lock():
396 self._errorDisplay.push_exception()
398 def refresh_session(self):
400 @note Thread agnostic
402 assert self._initDone, "Attempting login before app is fully loaded"
406 loggedIn = self._login_by_cookie()
408 loggedIn = self._login_by_settings()
411 raise RuntimeError("Login Failed")
413 def _login_by_cookie(self):
415 @note Thread agnostic
418 if self._credentials == ("", ""):
419 # Disallow logging in by cookie alone, without credentials
423 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
426 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
428 # If the cookies are bad, scratch them completely
429 self._phoneBackends[self._defaultBackendId].logout()
433 def _login_by_settings(self):
435 @note Thread agnostic
437 if self._credentials == ("", ""):
438 # Don't bother with the settings if they are blank
441 username, password = self._credentials
442 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
444 self._credentials = username, password
445 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
448 def _login_by_user(self):
450 @note This must be run outside of the UI lock
452 loggedIn, (username, password) = False, self._credentials
453 tmpServiceId = self.GV_BACKEND
455 with gtk_toolbox.gtk_lock():
456 credentials = self._credentialsDialog.request_credentials(
457 defaultCredentials = self._credentials
459 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
461 username, password = credentials
462 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
464 with gtk_toolbox.gtk_lock():
465 hildonize.show_busy_banner_end(banner)
468 serviceId = tmpServiceId
469 self._credentials = username, password
470 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
472 # Hint to the user that they are not logged in
473 serviceId = self.NULL_BACKEND
474 self._notebook.set_current_page(self.ACCOUNT_TAB)
476 return loggedIn, serviceId
478 def _select_action(self, action, number, message):
479 self.refresh_session()
481 self._on_dial_clicked(number)
482 elif action == "sms":
483 self._on_sms_clicked(number, message)
485 assert False, "Unknown action: %s" % action
487 def _change_loggedin_status(self, newStatus):
488 oldStatus = self._selectedBackendId
489 if oldStatus == newStatus:
492 self._dialpads[oldStatus].disable()
493 self._accountViews[oldStatus].disable()
494 self._recentViews[oldStatus].disable()
495 self._messagesViews[oldStatus].disable()
496 self._contactsViews[oldStatus].disable()
498 self._dialpads[newStatus].enable()
499 self._accountViews[newStatus].enable()
500 self._recentViews[newStatus].enable()
501 self._messagesViews[newStatus].enable()
502 self._contactsViews[newStatus].enable()
504 self._selectedBackendId = newStatus
506 self._accountViews[self._selectedBackendId].update()
507 self._refresh_active_tab()
509 def load_settings(self, config):
514 if not PROFILE_STARTUP:
515 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
517 self._defaultBackendId = self.NULL_BACKEND
519 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
520 for i in xrange(len(self._credentials))
523 base64.b64decode(blob)
526 self._credentials = tuple(creds)
528 if self._alarmHandler is not None:
529 self._alarmHandler.load_settings(config, "alarm")
530 except ConfigParser.NoOptionError, e:
531 _moduleLogger.exception(
532 "Settings file %s is missing section %s" % (
533 constants._user_settings_,
537 except ConfigParser.NoSectionError, e:
538 _moduleLogger.exception(
539 "Settings file %s is missing section %s" % (
540 constants._user_settings_,
545 for backendId, view in itertools.chain(
546 self._dialpads.iteritems(),
547 self._accountViews.iteritems(),
548 self._messagesViews.iteritems(),
549 self._recentViews.iteritems(),
550 self._contactsViews.iteritems(),
552 sectionName = "%s - %s" % (backendId, view.name())
554 view.load_settings(config, sectionName)
555 except ConfigParser.NoOptionError, e:
556 _moduleLogger.exception(
557 "Settings file %s is missing section %s" % (
558 constants._user_settings_,
562 except ConfigParser.NoSectionError, e:
563 _moduleLogger.exception(
564 "Settings file %s is missing section %s" % (
565 constants._user_settings_,
571 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
572 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
573 hildonize.window_to_landscape(self._window)
574 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
575 hildonize.window_to_portrait(self._window)
576 except ConfigParser.NoOptionError, e:
577 _moduleLogger.exception(
578 "Settings file %s is missing section %s" % (
579 constants._user_settings_,
583 except ConfigParser.NoSectionError, e:
584 _moduleLogger.exception(
585 "Settings file %s is missing section %s" % (
586 constants._user_settings_,
591 def save_settings(self, config):
593 @note Thread Agnostic
595 # Because we now only support GVoice, if there are user credentials,
596 # always assume its using the GVoice backend
597 if self._credentials[0] and self._credentials[1]:
598 backend = self.GV_BACKEND
600 backend = self.NULL_BACKEND
602 config.add_section(constants.__pretty_app_name__)
603 config.set(constants.__pretty_app_name__, "active", str(backend))
604 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
605 for i, value in enumerate(self._credentials):
606 blob = base64.b64encode(value)
607 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
608 config.add_section("alarm")
609 if self._alarmHandler is not None:
610 self._alarmHandler.save_settings(config, "alarm")
612 for backendId, view in itertools.chain(
613 self._dialpads.iteritems(),
614 self._accountViews.iteritems(),
615 self._messagesViews.iteritems(),
616 self._recentViews.iteritems(),
617 self._contactsViews.iteritems(),
619 sectionName = "%s - %s" % (backendId, view.name())
620 config.add_section(sectionName)
621 view.save_settings(config, sectionName)
623 def _save_settings(self):
625 @note Thread Agnostic
627 config = ConfigParser.SafeConfigParser()
628 self.save_settings(config)
629 with open(constants._user_settings_, "wb") as configFile:
630 config.write(configFile)
632 def _refresh_active_tab(self):
633 pageIndex = self._notebook.get_current_page()
634 if pageIndex == self.CONTACTS_TAB:
635 self._contactsViews[self._selectedBackendId].update(force=True)
636 elif pageIndex == self.RECENT_TAB:
637 self._recentViews[self._selectedBackendId].update(force=True)
638 elif pageIndex == self.MESSAGES_TAB:
639 self._messagesViews[self._selectedBackendId].update(force=True)
641 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
642 if self._ledHandler is not None:
643 self._ledHandler.off()
645 def _on_close(self, *args, **kwds):
647 if self._osso is not None:
651 self._save_settings()
655 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
657 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
658 For system_inactivity, we have no background tasks to pause
660 @note Hildon specific
664 for backendId in self.BACKENDS:
665 self._phoneBackends[backendId].clear_caches()
666 self._contactsViews[self._selectedBackendId].clear_caches()
669 if save_unsaved_data or shutdown:
670 self._save_settings()
672 self._errorDisplay.push_exception()
674 def _on_connection_change(self, connection, event, magicIdentifier):
676 @note Hildon specific
681 status = event.get_status()
682 error = event.get_error()
683 iap_id = event.get_iap_id()
684 bearer = event.get_bearer_type()
686 if status == conic.STATUS_CONNECTED:
688 self._spawn_attempt_login()
689 elif status == conic.STATUS_DISCONNECTED:
691 self._defaultBackendId = self._selectedBackendId
692 self._change_loggedin_status(self.NULL_BACKEND)
694 self._errorDisplay.push_exception()
696 def _on_window_state_change(self, widget, event, *args):
698 @note Hildon specific
701 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
702 self._isFullScreen = True
704 self._isFullScreen = False
706 self._errorDisplay.push_exception()
708 def _on_key_press(self, widget, event, *args):
710 @note Hildon specific
712 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
715 event.keyval == gtk.keysyms.F6 or
716 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
718 if self._isFullScreen:
719 self._window.unfullscreen()
721 self._window.fullscreen()
723 self._errorDisplay.push_exception()
725 def _on_clearcookies_clicked(self, *args):
727 self._phoneBackends[self._selectedBackendId].logout()
728 self._accountViews[self._selectedBackendId].clear()
729 self._recentViews[self._selectedBackendId].clear()
730 self._messagesViews[self._selectedBackendId].clear()
731 self._contactsViews[self._selectedBackendId].clear()
732 self._change_loggedin_status(self.NULL_BACKEND)
734 self._spawn_attempt_login(True)
736 self._errorDisplay.push_exception()
738 def _on_notebook_switch_page(self, notebook, page, pageIndex):
740 self._reset_tab_refresh()
742 didRecentUpdate = False
743 didMessagesUpdate = False
745 if pageIndex == self.RECENT_TAB:
746 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
747 elif pageIndex == self.MESSAGES_TAB:
748 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
749 elif pageIndex == self.CONTACTS_TAB:
750 self._contactsViews[self._selectedBackendId].update()
751 elif pageIndex == self.ACCOUNT_TAB:
752 self._accountViews[self._selectedBackendId].update()
754 if didRecentUpdate or didMessagesUpdate:
755 if self._ledHandler is not None:
756 self._ledHandler.off()
758 self._errorDisplay.push_exception()
760 def _set_tab_refresh(self, *args):
762 pageIndex = self._notebook.get_current_page()
763 child = self._notebook.get_nth_page(pageIndex)
764 self._notebook.get_tab_label(child).set_text("Refresh?")
766 self._errorDisplay.push_exception()
769 def _reset_tab_refresh(self, *args):
771 pageIndex = self._notebook.get_current_page()
772 child = self._notebook.get_nth_page(pageIndex)
773 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
775 self._errorDisplay.push_exception()
778 def _on_tab_refresh(self, *args):
780 self._refresh_active_tab()
781 self._reset_tab_refresh()
783 self._errorDisplay.push_exception()
786 def _on_sms_clicked(self, number, message):
788 assert number, "No number specified"
789 assert message, "Empty message"
791 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
794 self._errorDisplay.push_exception()
798 self._errorDisplay.push_message(
799 "Backend link with GoogleVoice is not working, please try again"
805 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
806 hildonize.show_information_banner(self._window, "Sending to %s" % number)
809 self._errorDisplay.push_exception()
812 self._dialpads[self._selectedBackendId].clear()
814 self._errorDisplay.push_exception()
816 def _on_dial_clicked(self, number):
818 assert number, "No number to call"
820 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
823 self._errorDisplay.push_exception()
827 self._errorDisplay.push_message(
828 "Backend link with GoogleVoice is not working, please try again"
834 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
835 self._phoneBackends[self._selectedBackendId].dial(number)
836 hildonize.show_information_banner(self._window, "Calling %s" % number)
839 self._errorDisplay.push_exception()
842 self._dialpads[self._selectedBackendId].clear()
844 self._errorDisplay.push_exception()
846 def _on_menu_refresh(self, *args):
848 self._refresh_active_tab()
850 self._errorDisplay.push_exception()
852 def _on_paste(self, *args):
854 contents = self._clipboard.wait_for_text()
855 if contents is not None:
856 self._dialpads[self._selectedBackendId].set_number(contents)
858 self._errorDisplay.push_exception()
860 def _on_about_activate(self, *args):
862 dlg = gtk.AboutDialog()
863 dlg.set_name(constants.__pretty_app_name__)
864 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
865 dlg.set_copyright("Copyright 2008 - LGPL")
866 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
867 dlg.set_website("http://gc-dialer.garage.maemo.org/")
868 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
872 self._errorDisplay.push_exception()
878 failureCount, testCount = doctest.testmod()
880 print "Tests Successful"
887 _lock_file = os.path.join(constants._data_path_, ".lock")
888 #with gtk_toolbox.flock(_lock_file, 0):
889 gtk.gdk.threads_init()
891 if hildonize.IS_HILDON_SUPPORTED:
892 gtk.set_application_name(constants.__pretty_app_name__)
893 handle = Dialcentral()
894 if not PROFILE_STARTUP:
898 class DummyOptions(object):
904 if __name__ == "__main__":
905 logging.basicConfig(level=logging.DEBUG)
907 if len(sys.argv) > 1:
913 if optparse is not None:
914 parser = optparse.OptionParser()
915 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
916 (commandOptions, commandArgs) = parser.parse_args()
918 commandOptions = DummyOptions()
921 if commandOptions.test: