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 self._initDone = True
347 config = ConfigParser.SafeConfigParser()
348 config.read(constants._user_settings_)
349 with gtk_toolbox.gtk_lock():
350 self.load_settings(config)
352 with gtk_toolbox.gtk_lock():
353 self._errorDisplay.push_exception()
355 self._spawn_attempt_login()
357 def _spawn_attempt_login(self, *args):
358 self._loginSink.send(args)
360 def _attempt_login(self, force = False):
362 @note This must be run outside of the UI lock
365 assert self._initDone, "Attempting login before app is fully loaded"
367 serviceId = self.NULL_BACKEND
369 if not force and self._defaultBackendId != self.NULL_BACKEND:
370 with gtk_toolbox.gtk_lock():
371 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
373 self.refresh_session()
374 serviceId = self._defaultBackendId
377 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
379 with gtk_toolbox.gtk_lock():
380 hildonize.show_busy_banner_end(banner)
383 loggedIn, serviceId = self._login_by_user()
385 with gtk_toolbox.gtk_lock():
386 self._change_loggedin_status(serviceId)
388 hildonize.show_information_banner(self._window, "Logged In")
390 hildonize.show_information_banner(self._window, "Login Failed")
391 if not self._phoneBackends[self._defaultBackendId].get_callback_number():
392 # subtle reminder to the users to configure things
393 self._notebook.set_current_page(self.ACCOUNT_TAB)
396 with gtk_toolbox.gtk_lock():
397 self._errorDisplay.push_exception()
399 def refresh_session(self):
401 @note Thread agnostic
403 assert self._initDone, "Attempting login before app is fully loaded"
407 loggedIn = self._login_by_cookie()
409 loggedIn = self._login_by_settings()
412 raise RuntimeError("Login Failed")
414 def _login_by_cookie(self):
416 @note Thread agnostic
418 if self._credentials == ("", ""):
419 # Disallow logging in by cookie alone, without credentials
422 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
424 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
427 def _login_by_settings(self):
429 @note Thread agnostic
431 if self._credentials == ("", ""):
432 # Don't bother with the settings if they are blank
435 username, password = self._credentials
436 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
438 self._credentials = username, password
439 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
442 def _login_by_user(self):
444 @note This must be run outside of the UI lock
446 loggedIn, (username, password) = False, self._credentials
447 tmpServiceId = self.GV_BACKEND
449 with gtk_toolbox.gtk_lock():
450 credentials = self._credentialsDialog.request_credentials(
451 defaultCredentials = self._credentials
453 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
455 username, password = credentials
456 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
458 with gtk_toolbox.gtk_lock():
459 hildonize.show_busy_banner_end(banner)
462 serviceId = tmpServiceId
463 self._credentials = username, password
464 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
466 # Hint to the user that they are not logged in
467 serviceId = self.NULL_BACKEND
468 self._notebook.set_current_page(self.ACCOUNT_TAB)
470 return loggedIn, serviceId
472 def _select_action(self, action, number, message):
473 self.refresh_session()
475 self._on_dial_clicked(number)
476 elif action == "sms":
477 self._on_sms_clicked(number, message)
479 assert False, "Unknown action: %s" % action
481 def _change_loggedin_status(self, newStatus):
482 oldStatus = self._selectedBackendId
483 if oldStatus == newStatus:
486 self._dialpads[oldStatus].disable()
487 self._accountViews[oldStatus].disable()
488 self._recentViews[oldStatus].disable()
489 self._messagesViews[oldStatus].disable()
490 self._contactsViews[oldStatus].disable()
492 self._dialpads[newStatus].enable()
493 self._accountViews[newStatus].enable()
494 self._recentViews[newStatus].enable()
495 self._messagesViews[newStatus].enable()
496 self._contactsViews[newStatus].enable()
498 if self._phoneBackends[self._selectedBackendId].get_callback_number() is None:
499 self._phoneBackends[self._selectedBackendId].set_sane_callback()
501 self._selectedBackendId = newStatus
503 self._accountViews[self._selectedBackendId].update()
504 self._refresh_active_tab()
506 def load_settings(self, config):
511 if not PROFILE_STARTUP:
512 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
514 self._defaultBackendId = self.NULL_BACKEND
516 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
517 for i in xrange(len(self._credentials))
520 base64.b64decode(blob)
523 self._credentials = tuple(creds)
525 if self._alarmHandler is not None:
526 self._alarmHandler.load_settings(config, "alarm")
527 except ConfigParser.NoOptionError, e:
528 _moduleLogger.exception(
529 "Settings file %s is missing section %s" % (
530 constants._user_settings_,
534 except ConfigParser.NoSectionError, e:
535 _moduleLogger.exception(
536 "Settings file %s is missing section %s" % (
537 constants._user_settings_,
542 for backendId, view in itertools.chain(
543 self._dialpads.iteritems(),
544 self._accountViews.iteritems(),
545 self._messagesViews.iteritems(),
546 self._recentViews.iteritems(),
547 self._contactsViews.iteritems(),
549 sectionName = "%s - %s" % (backendId, view.name())
551 view.load_settings(config, sectionName)
552 except ConfigParser.NoOptionError, e:
553 _moduleLogger.exception(
554 "Settings file %s is missing section %s" % (
555 constants._user_settings_,
559 except ConfigParser.NoSectionError, e:
560 _moduleLogger.exception(
561 "Settings file %s is missing section %s" % (
562 constants._user_settings_,
568 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
569 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
570 hildonize.window_to_landscape(self._window)
571 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
572 hildonize.window_to_portrait(self._window)
573 except ConfigParser.NoOptionError, e:
574 _moduleLogger.exception(
575 "Settings file %s is missing section %s" % (
576 constants._user_settings_,
580 except ConfigParser.NoSectionError, e:
581 _moduleLogger.exception(
582 "Settings file %s is missing section %s" % (
583 constants._user_settings_,
588 def save_settings(self, config):
590 @note Thread Agnostic
592 # Because we now only support GVoice, if there are user credentials,
593 # always assume its using the GVoice backend
594 if self._credentials[0] and self._credentials[1]:
595 backend = self.GV_BACKEND
597 backend = self.NULL_BACKEND
599 config.add_section(constants.__pretty_app_name__)
600 config.set(constants.__pretty_app_name__, "active", str(backend))
601 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
602 for i, value in enumerate(self._credentials):
603 blob = base64.b64encode(value)
604 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
605 config.add_section("alarm")
606 if self._alarmHandler is not None:
607 self._alarmHandler.save_settings(config, "alarm")
609 for backendId, view in itertools.chain(
610 self._dialpads.iteritems(),
611 self._accountViews.iteritems(),
612 self._messagesViews.iteritems(),
613 self._recentViews.iteritems(),
614 self._contactsViews.iteritems(),
616 sectionName = "%s - %s" % (backendId, view.name())
617 config.add_section(sectionName)
618 view.save_settings(config, sectionName)
620 def _save_settings(self):
622 @note Thread Agnostic
624 config = ConfigParser.SafeConfigParser()
625 self.save_settings(config)
626 with open(constants._user_settings_, "wb") as configFile:
627 config.write(configFile)
629 def _refresh_active_tab(self):
630 pageIndex = self._notebook.get_current_page()
631 if pageIndex == self.CONTACTS_TAB:
632 self._contactsViews[self._selectedBackendId].update(force=True)
633 elif pageIndex == self.RECENT_TAB:
634 self._recentViews[self._selectedBackendId].update(force=True)
635 elif pageIndex == self.MESSAGES_TAB:
636 self._messagesViews[self._selectedBackendId].update(force=True)
638 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
639 if self._ledHandler is not None:
640 self._ledHandler.off()
642 def _on_close(self, *args, **kwds):
644 if self._osso is not None:
648 self._save_settings()
652 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
654 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
655 For system_inactivity, we have no background tasks to pause
657 @note Hildon specific
661 for backendId in self.BACKENDS:
662 self._phoneBackends[backendId].clear_caches()
663 self._contactsViews[self._selectedBackendId].clear_caches()
666 if save_unsaved_data or shutdown:
667 self._save_settings()
669 self._errorDisplay.push_exception()
671 def _on_connection_change(self, connection, event, magicIdentifier):
673 @note Hildon specific
678 status = event.get_status()
679 error = event.get_error()
680 iap_id = event.get_iap_id()
681 bearer = event.get_bearer_type()
683 if status == conic.STATUS_CONNECTED:
685 self._spawn_attempt_login()
686 elif status == conic.STATUS_DISCONNECTED:
688 self._defaultBackendId = self._selectedBackendId
689 self._change_loggedin_status(self.NULL_BACKEND)
691 self._errorDisplay.push_exception()
693 def _on_window_state_change(self, widget, event, *args):
695 @note Hildon specific
698 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
699 self._isFullScreen = True
701 self._isFullScreen = False
703 self._errorDisplay.push_exception()
705 def _on_key_press(self, widget, event, *args):
707 @note Hildon specific
709 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
712 event.keyval == gtk.keysyms.F6 or
713 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
715 if self._isFullScreen:
716 self._window.unfullscreen()
718 self._window.fullscreen()
720 self._errorDisplay.push_exception()
722 def _on_clearcookies_clicked(self, *args):
724 self._phoneBackends[self._selectedBackendId].logout()
725 self._accountViews[self._selectedBackendId].clear()
726 self._recentViews[self._selectedBackendId].clear()
727 self._messagesViews[self._selectedBackendId].clear()
728 self._contactsViews[self._selectedBackendId].clear()
729 self._change_loggedin_status(self.NULL_BACKEND)
731 self._spawn_attempt_login(True)
733 self._errorDisplay.push_exception()
735 def _on_notebook_switch_page(self, notebook, page, pageIndex):
737 self._reset_tab_refresh()
739 didRecentUpdate = False
740 didMessagesUpdate = False
742 if pageIndex == self.RECENT_TAB:
743 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
744 elif pageIndex == self.MESSAGES_TAB:
745 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
746 elif pageIndex == self.CONTACTS_TAB:
747 self._contactsViews[self._selectedBackendId].update()
748 elif pageIndex == self.ACCOUNT_TAB:
749 self._accountViews[self._selectedBackendId].update()
751 if didRecentUpdate or didMessagesUpdate:
752 if self._ledHandler is not None:
753 self._ledHandler.off()
755 self._errorDisplay.push_exception()
757 def _set_tab_refresh(self, *args):
759 pageIndex = self._notebook.get_current_page()
760 child = self._notebook.get_nth_page(pageIndex)
761 self._notebook.get_tab_label(child).set_text("Refresh?")
763 self._errorDisplay.push_exception()
766 def _reset_tab_refresh(self, *args):
768 pageIndex = self._notebook.get_current_page()
769 child = self._notebook.get_nth_page(pageIndex)
770 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
772 self._errorDisplay.push_exception()
775 def _on_tab_refresh(self, *args):
777 self._refresh_active_tab()
778 self._reset_tab_refresh()
780 self._errorDisplay.push_exception()
783 def _on_sms_clicked(self, number, message):
785 assert number, "No number specified"
786 assert message, "Empty message"
788 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
791 self._errorDisplay.push_exception()
795 self._errorDisplay.push_message(
796 "Backend link with GoogleVoice is not working, please try again"
802 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
803 hildonize.show_information_banner(self._window, "Sending to %s" % number)
806 self._errorDisplay.push_exception()
809 self._dialpads[self._selectedBackendId].clear()
811 self._errorDisplay.push_exception()
813 def _on_dial_clicked(self, number):
815 assert number, "No number to call"
817 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
820 self._errorDisplay.push_exception()
824 self._errorDisplay.push_message(
825 "Backend link with GoogleVoice is not working, please try again"
831 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
832 self._phoneBackends[self._selectedBackendId].dial(number)
833 hildonize.show_information_banner(self._window, "Calling %s" % number)
836 self._errorDisplay.push_exception()
839 self._dialpads[self._selectedBackendId].clear()
841 self._errorDisplay.push_exception()
843 def _on_menu_refresh(self, *args):
845 self._refresh_active_tab()
847 self._errorDisplay.push_exception()
849 def _on_paste(self, *args):
851 contents = self._clipboard.wait_for_text()
852 if contents is not None:
853 self._dialpads[self._selectedBackendId].set_number(contents)
855 self._errorDisplay.push_exception()
857 def _on_about_activate(self, *args):
859 dlg = gtk.AboutDialog()
860 dlg.set_name(constants.__pretty_app_name__)
861 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
862 dlg.set_copyright("Copyright 2008 - LGPL")
863 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
864 dlg.set_website("http://gc-dialer.garage.maemo.org/")
865 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
869 self._errorDisplay.push_exception()
875 failureCount, testCount = doctest.testmod()
877 print "Tests Successful"
884 _lock_file = os.path.join(constants._data_path_, ".lock")
885 #with gtk_toolbox.flock(_lock_file, 0):
886 gtk.gdk.threads_init()
888 if hildonize.IS_HILDON_SUPPORTED:
889 gtk.set_application_name(constants.__pretty_app_name__)
890 handle = Dialcentral()
891 if not PROFILE_STARTUP:
895 class DummyOptions(object):
901 if __name__ == "__main__":
902 logging.basicConfig(level=logging.DEBUG)
904 if len(sys.argv) > 1:
910 if optparse is not None:
911 parser = optparse.OptionParser()
912 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
913 (commandOptions, commandArgs) = parser.parse_args()
915 commandOptions = DummyOptions()
918 if commandOptions.test: