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")
45 def getmtime_nothrow(path):
47 return os.path.getmtime(path)
52 def display_error_message(msg):
53 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
55 def close(dialog, response):
57 error_dialog.connect("response", close)
61 class Dialcentral(object):
64 os.path.join(os.path.dirname(__file__), "dialcentral.glade"),
65 os.path.join(os.path.dirname(__file__), "../lib/dialcentral.glade"),
66 '/usr/lib/dialcentral/dialcentral.glade',
77 BACKENDS = (NULL_BACKEND, GV_BACKEND)
80 self._initDone = False
81 self._connection = None
83 self._clipboard = gtk.clipboard_get()
85 self._credentials = ("", "")
86 self._selectedBackendId = self.NULL_BACKEND
87 self._defaultBackendId = self.GV_BACKEND
88 self._phoneBackends = None
90 self._accountViews = None
91 self._messagesViews = None
92 self._recentViews = None
93 self._contactsViews = None
94 self._alarmHandler = None
95 self._ledHandler = None
96 self._originalCurrentLabels = []
98 for path in self._glade_files:
99 if os.path.isfile(path):
100 self._widgetTree = gtk.glade.XML(path)
103 display_error_message("Cannot find dialcentral.glade")
107 self._window = self._widgetTree.get_widget("mainWindow")
108 self._notebook = self._widgetTree.get_widget("notebook")
109 self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
110 self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
112 self._isFullScreen = False
113 self._app = hildonize.get_app_class()()
114 self._window = hildonize.hildonize_window(self._app, self._window)
115 hildonize.hildonize_text_entry(self._widgetTree.get_widget("usernameentry"))
116 hildonize.hildonize_password_entry(self._widgetTree.get_widget("passwordentry"))
118 for scrollingWidgetName in (
119 'recent_scrolledwindow',
120 'message_scrolledwindow',
121 'contacts_scrolledwindow',
122 "smsMessages_scrolledwindow",
124 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
125 assert scrollingWidget is not None, scrollingWidgetName
126 hildonize.hildonize_scrollwindow(scrollingWidget)
127 for scrollingWidgetName in (
128 "smsMessage_scrolledEntry",
130 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
131 assert scrollingWidget is not None, scrollingWidgetName
132 hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
136 "addressbookSelectButton",
140 "callbackSelectButton",
141 "minutesEntryButton",
143 "phoneTypeSelection",
145 button = self._widgetTree.get_widget(buttonName)
146 assert button is not None, buttonName
147 hildonize.set_button_thumb_selectable(button)
149 replacementButtons = [gtk.Button("Test")]
150 menu = hildonize.hildonize_menu(
152 self._widgetTree.get_widget("dialpad_menubar"),
156 self._window.connect("key-press-event", self._on_key_press)
157 self._window.connect("window-state-event", self._on_window_state_change)
158 if not hildonize.IS_HILDON_SUPPORTED:
159 _moduleLogger.warning("No hildonization support")
161 hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
163 self._window.connect("destroy", self._on_close)
164 self._window.set_default_size(800, 300)
165 self._window.show_all()
167 self._loginSink = gtk_toolbox.threaded_stage(
170 gtk_toolbox.null_sink(),
174 backgroundSetup = threading.Thread(target=self._idle_setup)
175 backgroundSetup.setDaemon(True)
176 backgroundSetup.start()
178 def _idle_setup(self):
180 If something can be done after the UI loads, push it here so it's not blocking the UI
182 # Barebones UI handlers
187 self._phoneBackends = {self.NULL_BACKEND: null_backend.NullDialer()}
188 with gtk_toolbox.gtk_lock():
189 self._dialpads = {self.NULL_BACKEND: null_views.Dialpad(self._widgetTree)}
190 self._accountViews = {self.NULL_BACKEND: null_views.AccountInfo(self._widgetTree)}
191 self._recentViews = {self.NULL_BACKEND: null_views.RecentCallsView(self._widgetTree)}
192 self._messagesViews = {self.NULL_BACKEND: null_views.MessagesView(self._widgetTree)}
193 self._contactsViews = {self.NULL_BACKEND: null_views.ContactsView(self._widgetTree)}
195 self._dialpads[self._selectedBackendId].enable()
196 self._accountViews[self._selectedBackendId].enable()
197 self._recentViews[self._selectedBackendId].enable()
198 self._messagesViews[self._selectedBackendId].enable()
199 self._contactsViews[self._selectedBackendId].enable()
201 with gtk_toolbox.gtk_lock():
202 self._errorDisplay.push_exception()
204 # Setup maemo specifics
208 except (ImportError, OSError):
212 self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
213 device = osso.DeviceState(self._osso)
214 device.set_device_state_callback(self._on_device_state_change, 0)
216 _moduleLogger.warning("No device state support")
220 self._alarmHandler = alarm_handler.AlarmHandler()
221 except (ImportError, OSError):
224 with gtk_toolbox.gtk_lock():
225 self._errorDisplay.push_exception()
227 _moduleLogger.warning("No notification support")
228 if hildonize.IS_HILDON_SUPPORTED:
231 self._ledHandler = led_handler.LedHandler()
233 _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
234 self._ledHandler = None
236 self._ledHandler = None
240 except (ImportError, OSError):
242 self._connection = None
243 if conic is not None:
244 self._connection = conic.Connection()
245 self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
246 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
248 _moduleLogger.warning("No connection support")
250 with gtk_toolbox.gtk_lock():
251 self._errorDisplay.push_exception()
253 # Setup costly backends
260 os.makedirs(constants._data_path_)
264 gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
266 self._phoneBackends.update({
267 self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
269 with gtk_toolbox.gtk_lock():
270 unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
271 self._dialpads.update({
272 self.GV_BACKEND: unifiedDialpad,
274 self._accountViews.update({
275 self.GV_BACKEND: gv_views.AccountInfo(
276 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
279 self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
280 self._recentViews.update({
281 self.GV_BACKEND: gv_views.RecentCallsView(
282 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
285 self._messagesViews.update({
286 self.GV_BACKEND: gv_views.MessagesView(
287 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
290 self._contactsViews.update({
291 self.GV_BACKEND: gv_views.ContactsView(
292 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
296 fsContactsPath = os.path.join(constants._data_path_, "contacts")
297 fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
299 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
300 self._recentViews[self.GV_BACKEND].number_selected = self._select_action
301 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
302 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
305 self._phoneBackends[self.GV_BACKEND],
308 mergedBook = gv_views.MergedAddressBook(addressBooks, gv_views.MergedAddressBook.advanced_lastname_sorter)
309 self._contactsViews[self.GV_BACKEND].append(mergedBook)
310 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
311 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
314 "on_paste": self._on_paste,
315 "on_refresh": self._on_menu_refresh,
316 "on_clearcookies_clicked": self._on_clearcookies_clicked,
317 "on_about_activate": self._on_about_activate,
319 if hildonize.GTK_MENU_USED:
320 self._widgetTree.signal_autoconnect(callbackMapping)
321 self._notebook.connect("switch-page", self._on_notebook_switch_page)
322 self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
324 with gtk_toolbox.gtk_lock():
325 self._originalCurrentLabels = [
326 self._notebook.get_tab_label(self._notebook.get_nth_page(pageIndex)).get_text()
327 for pageIndex in xrange(self._notebook.get_n_pages())
329 self._notebookTapHandler = gtk_toolbox.TapOrHold(self._notebook)
330 self._notebookTapHandler.enable()
331 self._notebookTapHandler.on_tap = self._reset_tab_refresh
332 self._notebookTapHandler.on_hold = self._on_tab_refresh
333 self._notebookTapHandler.on_holding = self._set_tab_refresh
334 self._notebookTapHandler.on_cancel = self._reset_tab_refresh
336 self._initDone = True
338 config = ConfigParser.SafeConfigParser()
339 config.read(constants._user_settings_)
340 with gtk_toolbox.gtk_lock():
341 self.load_settings(config)
343 with gtk_toolbox.gtk_lock():
344 self._errorDisplay.push_exception()
346 self._spawn_attempt_login(2)
348 def _spawn_attempt_login(self, *args):
349 self._loginSink.send(args)
351 def _attempt_login(self, numOfAttempts = 10, force = False):
353 @note This must be run outside of the UI lock
356 assert 0 <= numOfAttempts, "That was pointless having 0 or less login attempts"
357 assert self._initDone, "Attempting login before app is fully loaded"
359 serviceId = self.NULL_BACKEND
362 with gtk_toolbox.gtk_lock():
363 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
365 self.refresh_session()
366 serviceId = self._defaultBackendId
369 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
371 with gtk_toolbox.gtk_lock():
372 hildonize.show_busy_banner_end(banner)
375 loggedIn, serviceId = self._login_by_user(numOfAttempts)
377 with gtk_toolbox.gtk_lock():
378 self._change_loggedin_status(serviceId)
380 hildonize.show_information_banner(self._window, "Logged In")
382 hildonize.show_information_banner(self._window, "Login Failed")
384 with gtk_toolbox.gtk_lock():
385 self._errorDisplay.push_exception()
387 def refresh_session(self):
389 @note Thread agnostic
391 assert self._initDone, "Attempting login before app is fully loaded"
395 loggedIn = self._login_by_cookie()
397 loggedIn = self._login_by_settings()
400 raise RuntimeError("Login Failed")
402 def _login_by_cookie(self):
404 @note Thread agnostic
406 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
408 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
411 def _login_by_settings(self):
413 @note Thread agnostic
415 username, password = self._credentials
417 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
419 # Retry in case the redirect failed
420 # luckily is_authed does everything we need for a retry
421 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed(True)
424 _moduleLogger.info("Redirection failed on initial login attempt, auto-corrected for this")
426 self._credentials = username, password
427 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
430 def _login_by_user(self, numOfAttempts):
432 @note This must be run outside of the UI lock
434 loggedIn, (username, password) = False, self._credentials
435 tmpServiceId = self.GV_BACKEND
436 for attemptCount in xrange(numOfAttempts):
440 with gtk_toolbox.gtk_lock():
441 credentials = self._credentialsDialog.request_credentials(
442 defaultCredentials = self._credentials
444 if not self._phoneBackends[tmpServiceId].get_callback_number():
445 # subtle reminder to the users to configure things
446 self._notebook.set_current_page(self.ACCOUNT_TAB)
447 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
450 username, password = credentials
452 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
454 # Retry in case the redirect failed
455 # luckily is_authed does everything we need for a retry
456 loggedIn = self._phoneBackends[tmpServiceId].is_authed(True)
459 _moduleLogger.info("Redirection failed on initial login attempt, auto-corrected for this")
461 with gtk_toolbox.gtk_lock():
462 hildonize.show_busy_banner_end(banner)
465 serviceId = tmpServiceId
466 self._credentials = username, password
467 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
469 serviceId = self.NULL_BACKEND
470 self._notebook.set_current_page(self.ACCOUNT_TAB)
472 return loggedIn, serviceId
474 def _select_action(self, action, number, message):
475 self.refresh_session()
477 self._on_dial_clicked(number)
478 elif action == "sms":
479 self._on_sms_clicked(number, message)
481 assert False, "Unknown action: %s" % action
483 def _change_loggedin_status(self, newStatus):
484 oldStatus = self._selectedBackendId
485 if oldStatus == newStatus:
488 self._dialpads[oldStatus].disable()
489 self._accountViews[oldStatus].disable()
490 self._recentViews[oldStatus].disable()
491 self._messagesViews[oldStatus].disable()
492 self._contactsViews[oldStatus].disable()
494 self._dialpads[newStatus].enable()
495 self._accountViews[newStatus].enable()
496 self._recentViews[newStatus].enable()
497 self._messagesViews[newStatus].enable()
498 self._contactsViews[newStatus].enable()
500 if self._phoneBackends[self._selectedBackendId].get_callback_number() is None:
501 self._phoneBackends[self._selectedBackendId].set_sane_callback()
503 self._selectedBackendId = newStatus
505 self._accountViews[self._selectedBackendId].update()
506 self._refresh_active_tab()
508 def load_settings(self, config):
513 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
515 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
516 for i in xrange(len(self._credentials))
519 base64.b64decode(blob)
522 self._credentials = tuple(creds)
524 if self._alarmHandler is not None:
525 self._alarmHandler.load_settings(config, "alarm")
526 except ConfigParser.NoOptionError, e:
527 _moduleLogger.exception(
528 "Settings file %s is missing section %s" % (
529 constants._user_settings_,
533 except ConfigParser.NoSectionError, e:
534 _moduleLogger.exception(
535 "Settings file %s is missing section %s" % (
536 constants._user_settings_,
541 for backendId, view in itertools.chain(
542 self._dialpads.iteritems(),
543 self._accountViews.iteritems(),
544 self._messagesViews.iteritems(),
545 self._recentViews.iteritems(),
546 self._contactsViews.iteritems(),
548 sectionName = "%s - %s" % (backendId, view.name())
550 view.load_settings(config, sectionName)
551 except ConfigParser.NoOptionError, e:
552 _moduleLogger.exception(
553 "Settings file %s is missing section %s" % (
554 constants._user_settings_,
558 except ConfigParser.NoSectionError, e:
559 _moduleLogger.exception(
560 "Settings file %s is missing section %s" % (
561 constants._user_settings_,
567 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
568 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
569 hildonize.window_to_landscape(self._window)
570 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
571 hildonize.window_to_portrait(self._window)
572 except ConfigParser.NoOptionError, e:
573 _moduleLogger.exception(
574 "Settings file %s is missing section %s" % (
575 constants._user_settings_,
579 except ConfigParser.NoSectionError, e:
580 _moduleLogger.exception(
581 "Settings file %s is missing section %s" % (
582 constants._user_settings_,
587 def save_settings(self, config):
589 @note Thread Agnostic
591 config.add_section(constants.__pretty_app_name__)
592 config.set(constants.__pretty_app_name__, "active", str(self._selectedBackendId))
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(2)
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(2, 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()
886 class DummyOptions(object):
892 if __name__ == "__main__":
893 logging.basicConfig(level=logging.DEBUG)
895 if len(sys.argv) > 1:
901 if optparse is not None:
902 parser = optparse.OptionParser()
903 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
904 (commandOptions, commandArgs) = parser.parse_args()
906 commandOptions = DummyOptions()
909 if commandOptions.test: