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")
392 with gtk_toolbox.gtk_lock():
393 self._errorDisplay.push_exception()
395 def refresh_session(self):
397 @note Thread agnostic
399 assert self._initDone, "Attempting login before app is fully loaded"
403 loggedIn = self._login_by_cookie()
405 loggedIn = self._login_by_settings()
408 raise RuntimeError("Login Failed")
410 def _login_by_cookie(self):
412 @note Thread agnostic
414 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
416 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
419 def _login_by_settings(self):
421 @note Thread agnostic
423 username, password = self._credentials
424 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
426 self._credentials = username, password
427 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
430 def _login_by_user(self):
432 @note This must be run outside of the UI lock
434 loggedIn, (username, password) = False, self._credentials
435 tmpServiceId = self.GV_BACKEND
437 with gtk_toolbox.gtk_lock():
438 credentials = self._credentialsDialog.request_credentials(
439 defaultCredentials = self._credentials
441 if not self._phoneBackends[tmpServiceId].get_callback_number():
442 # subtle reminder to the users to configure things
443 self._notebook.set_current_page(self.ACCOUNT_TAB)
445 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
447 username, password = credentials
448 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
450 with gtk_toolbox.gtk_lock():
451 hildonize.show_busy_banner_end(banner)
454 serviceId = tmpServiceId
455 self._credentials = username, password
456 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
458 # Hint to the user that they are not logged in
459 serviceId = self.NULL_BACKEND
460 self._notebook.set_current_page(self.ACCOUNT_TAB)
462 return loggedIn, serviceId
464 def _select_action(self, action, number, message):
465 self.refresh_session()
467 self._on_dial_clicked(number)
468 elif action == "sms":
469 self._on_sms_clicked(number, message)
471 assert False, "Unknown action: %s" % action
473 def _change_loggedin_status(self, newStatus):
474 oldStatus = self._selectedBackendId
475 if oldStatus == newStatus:
478 self._dialpads[oldStatus].disable()
479 self._accountViews[oldStatus].disable()
480 self._recentViews[oldStatus].disable()
481 self._messagesViews[oldStatus].disable()
482 self._contactsViews[oldStatus].disable()
484 self._dialpads[newStatus].enable()
485 self._accountViews[newStatus].enable()
486 self._recentViews[newStatus].enable()
487 self._messagesViews[newStatus].enable()
488 self._contactsViews[newStatus].enable()
490 if self._phoneBackends[self._selectedBackendId].get_callback_number() is None:
491 self._phoneBackends[self._selectedBackendId].set_sane_callback()
493 self._selectedBackendId = newStatus
495 self._accountViews[self._selectedBackendId].update()
496 self._refresh_active_tab()
498 def load_settings(self, config):
503 if not PROFILE_STARTUP:
504 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
506 self._defaultBackendId = self.NULL_BACKEND
508 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
509 for i in xrange(len(self._credentials))
512 base64.b64decode(blob)
515 self._credentials = tuple(creds)
517 if self._alarmHandler is not None:
518 self._alarmHandler.load_settings(config, "alarm")
519 except ConfigParser.NoOptionError, e:
520 _moduleLogger.exception(
521 "Settings file %s is missing section %s" % (
522 constants._user_settings_,
526 except ConfigParser.NoSectionError, e:
527 _moduleLogger.exception(
528 "Settings file %s is missing section %s" % (
529 constants._user_settings_,
534 for backendId, view in itertools.chain(
535 self._dialpads.iteritems(),
536 self._accountViews.iteritems(),
537 self._messagesViews.iteritems(),
538 self._recentViews.iteritems(),
539 self._contactsViews.iteritems(),
541 sectionName = "%s - %s" % (backendId, view.name())
543 view.load_settings(config, sectionName)
544 except ConfigParser.NoOptionError, e:
545 _moduleLogger.exception(
546 "Settings file %s is missing section %s" % (
547 constants._user_settings_,
551 except ConfigParser.NoSectionError, e:
552 _moduleLogger.exception(
553 "Settings file %s is missing section %s" % (
554 constants._user_settings_,
560 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
561 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
562 hildonize.window_to_landscape(self._window)
563 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
564 hildonize.window_to_portrait(self._window)
565 except ConfigParser.NoOptionError, e:
566 _moduleLogger.exception(
567 "Settings file %s is missing section %s" % (
568 constants._user_settings_,
572 except ConfigParser.NoSectionError, e:
573 _moduleLogger.exception(
574 "Settings file %s is missing section %s" % (
575 constants._user_settings_,
580 def save_settings(self, config):
582 @note Thread Agnostic
584 # Because we now only support GVoice, if there are user credentials,
585 # always assume its using the GVoice backend
586 if self._credentials[0] and self._credentials[1]:
587 backend = self.GV_BACKEND
589 backend = self.NULL_BACKEND
591 config.add_section(constants.__pretty_app_name__)
592 config.set(constants.__pretty_app_name__, "active", str(backend))
593 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
594 for i, value in enumerate(self._credentials):
595 blob = base64.b64encode(value)
596 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
597 config.add_section("alarm")
598 if self._alarmHandler is not None:
599 self._alarmHandler.save_settings(config, "alarm")
601 for backendId, view in itertools.chain(
602 self._dialpads.iteritems(),
603 self._accountViews.iteritems(),
604 self._messagesViews.iteritems(),
605 self._recentViews.iteritems(),
606 self._contactsViews.iteritems(),
608 sectionName = "%s - %s" % (backendId, view.name())
609 config.add_section(sectionName)
610 view.save_settings(config, sectionName)
612 def _save_settings(self):
614 @note Thread Agnostic
616 config = ConfigParser.SafeConfigParser()
617 self.save_settings(config)
618 with open(constants._user_settings_, "wb") as configFile:
619 config.write(configFile)
621 def _refresh_active_tab(self):
622 pageIndex = self._notebook.get_current_page()
623 if pageIndex == self.CONTACTS_TAB:
624 self._contactsViews[self._selectedBackendId].update(force=True)
625 elif pageIndex == self.RECENT_TAB:
626 self._recentViews[self._selectedBackendId].update(force=True)
627 elif pageIndex == self.MESSAGES_TAB:
628 self._messagesViews[self._selectedBackendId].update(force=True)
630 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
631 if self._ledHandler is not None:
632 self._ledHandler.off()
634 def _on_close(self, *args, **kwds):
636 if self._osso is not None:
640 self._save_settings()
644 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
646 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
647 For system_inactivity, we have no background tasks to pause
649 @note Hildon specific
653 for backendId in self.BACKENDS:
654 self._phoneBackends[backendId].clear_caches()
655 self._contactsViews[self._selectedBackendId].clear_caches()
658 if save_unsaved_data or shutdown:
659 self._save_settings()
661 self._errorDisplay.push_exception()
663 def _on_connection_change(self, connection, event, magicIdentifier):
665 @note Hildon specific
670 status = event.get_status()
671 error = event.get_error()
672 iap_id = event.get_iap_id()
673 bearer = event.get_bearer_type()
675 if status == conic.STATUS_CONNECTED:
677 self._spawn_attempt_login()
678 elif status == conic.STATUS_DISCONNECTED:
680 self._defaultBackendId = self._selectedBackendId
681 self._change_loggedin_status(self.NULL_BACKEND)
683 self._errorDisplay.push_exception()
685 def _on_window_state_change(self, widget, event, *args):
687 @note Hildon specific
690 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
691 self._isFullScreen = True
693 self._isFullScreen = False
695 self._errorDisplay.push_exception()
697 def _on_key_press(self, widget, event, *args):
699 @note Hildon specific
701 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
704 event.keyval == gtk.keysyms.F6 or
705 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
707 if self._isFullScreen:
708 self._window.unfullscreen()
710 self._window.fullscreen()
712 self._errorDisplay.push_exception()
714 def _on_clearcookies_clicked(self, *args):
716 self._phoneBackends[self._selectedBackendId].logout()
717 self._accountViews[self._selectedBackendId].clear()
718 self._recentViews[self._selectedBackendId].clear()
719 self._messagesViews[self._selectedBackendId].clear()
720 self._contactsViews[self._selectedBackendId].clear()
721 self._change_loggedin_status(self.NULL_BACKEND)
723 self._spawn_attempt_login(True)
725 self._errorDisplay.push_exception()
727 def _on_notebook_switch_page(self, notebook, page, pageIndex):
729 self._reset_tab_refresh()
731 didRecentUpdate = False
732 didMessagesUpdate = False
734 if pageIndex == self.RECENT_TAB:
735 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
736 elif pageIndex == self.MESSAGES_TAB:
737 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
738 elif pageIndex == self.CONTACTS_TAB:
739 self._contactsViews[self._selectedBackendId].update()
740 elif pageIndex == self.ACCOUNT_TAB:
741 self._accountViews[self._selectedBackendId].update()
743 if didRecentUpdate or didMessagesUpdate:
744 if self._ledHandler is not None:
745 self._ledHandler.off()
747 self._errorDisplay.push_exception()
749 def _set_tab_refresh(self, *args):
751 pageIndex = self._notebook.get_current_page()
752 child = self._notebook.get_nth_page(pageIndex)
753 self._notebook.get_tab_label(child).set_text("Refresh?")
755 self._errorDisplay.push_exception()
758 def _reset_tab_refresh(self, *args):
760 pageIndex = self._notebook.get_current_page()
761 child = self._notebook.get_nth_page(pageIndex)
762 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
764 self._errorDisplay.push_exception()
767 def _on_tab_refresh(self, *args):
769 self._refresh_active_tab()
770 self._reset_tab_refresh()
772 self._errorDisplay.push_exception()
775 def _on_sms_clicked(self, number, message):
777 assert number, "No number specified"
778 assert message, "Empty message"
780 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
783 self._errorDisplay.push_exception()
787 self._errorDisplay.push_message(
788 "Backend link with GoogleVoice is not working, please try again"
794 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
795 hildonize.show_information_banner(self._window, "Sending to %s" % number)
798 self._errorDisplay.push_exception()
801 self._dialpads[self._selectedBackendId].clear()
803 self._errorDisplay.push_exception()
805 def _on_dial_clicked(self, number):
807 assert number, "No number to call"
809 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
812 self._errorDisplay.push_exception()
816 self._errorDisplay.push_message(
817 "Backend link with GoogleVoice is not working, please try again"
823 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
824 self._phoneBackends[self._selectedBackendId].dial(number)
825 hildonize.show_information_banner(self._window, "Calling %s" % number)
828 self._errorDisplay.push_exception()
831 self._dialpads[self._selectedBackendId].clear()
833 self._errorDisplay.push_exception()
835 def _on_menu_refresh(self, *args):
837 self._refresh_active_tab()
839 self._errorDisplay.push_exception()
841 def _on_paste(self, *args):
843 contents = self._clipboard.wait_for_text()
844 if contents is not None:
845 self._dialpads[self._selectedBackendId].set_number(contents)
847 self._errorDisplay.push_exception()
849 def _on_about_activate(self, *args):
851 dlg = gtk.AboutDialog()
852 dlg.set_name(constants.__pretty_app_name__)
853 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
854 dlg.set_copyright("Copyright 2008 - LGPL")
855 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
856 dlg.set_website("http://gc-dialer.garage.maemo.org/")
857 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
861 self._errorDisplay.push_exception()
867 failureCount, testCount = doctest.testmod()
869 print "Tests Successful"
876 _lock_file = os.path.join(constants._data_path_, ".lock")
877 #with gtk_toolbox.flock(_lock_file, 0):
878 gtk.gdk.threads_init()
880 if hildonize.IS_HILDON_SUPPORTED:
881 gtk.set_application_name(constants.__pretty_app_name__)
882 handle = Dialcentral()
883 if not PROFILE_STARTUP:
887 class DummyOptions(object):
893 if __name__ == "__main__":
894 logging.basicConfig(level=logging.DEBUG)
896 if len(sys.argv) > 1:
902 if optparse is not None:
903 parser = optparse.OptionParser()
904 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
905 (commandOptions, commandArgs) = parser.parse_args()
907 commandOptions = DummyOptions()
910 if commandOptions.test: