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):
439 with gtk_toolbox.gtk_lock():
440 credentials = self._credentialsDialog.request_credentials(
441 defaultCredentials = self._credentials
443 if not self._phoneBackends[tmpServiceId].get_callback_number():
444 # subtle reminder to the users to configure things
445 self._notebook.set_current_page(self.ACCOUNT_TAB)
446 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
448 username, password = credentials
450 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
452 # Retry in case the redirect failed
453 # luckily is_authed does everything we need for a retry
454 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed(True)
457 _moduleLogger.info("Redirection failed on initial login attempt, auto-corrected for this")
459 with gtk_toolbox.gtk_lock():
460 hildonize.show_busy_banner_end(banner)
463 serviceId = tmpServiceId
464 self._credentials = username, password
465 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
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 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
513 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
514 for i in xrange(len(self._credentials))
517 base64.b64decode(blob)
520 self._credentials = tuple(creds)
522 if self._alarmHandler is not None:
523 self._alarmHandler.load_settings(config, "alarm")
524 except ConfigParser.NoOptionError, e:
525 _moduleLogger.exception(
526 "Settings file %s is missing section %s" % (
527 constants._user_settings_,
531 except ConfigParser.NoSectionError, e:
532 _moduleLogger.exception(
533 "Settings file %s is missing section %s" % (
534 constants._user_settings_,
539 for backendId, view in itertools.chain(
540 self._dialpads.iteritems(),
541 self._accountViews.iteritems(),
542 self._messagesViews.iteritems(),
543 self._recentViews.iteritems(),
544 self._contactsViews.iteritems(),
546 sectionName = "%s - %s" % (backendId, view.name())
548 view.load_settings(config, sectionName)
549 except ConfigParser.NoOptionError, e:
550 _moduleLogger.exception(
551 "Settings file %s is missing section %s" % (
552 constants._user_settings_,
556 except ConfigParser.NoSectionError, e:
557 _moduleLogger.exception(
558 "Settings file %s is missing section %s" % (
559 constants._user_settings_,
565 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
566 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
567 hildonize.window_to_landscape(self._window)
568 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
569 hildonize.window_to_portrait(self._window)
570 except ConfigParser.NoOptionError, e:
571 _moduleLogger.exception(
572 "Settings file %s is missing section %s" % (
573 constants._user_settings_,
577 except ConfigParser.NoSectionError, e:
578 _moduleLogger.exception(
579 "Settings file %s is missing section %s" % (
580 constants._user_settings_,
585 def save_settings(self, config):
587 @note Thread Agnostic
589 config.add_section(constants.__pretty_app_name__)
590 config.set(constants.__pretty_app_name__, "active", str(self._selectedBackendId))
591 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
592 for i, value in enumerate(self._credentials):
593 blob = base64.b64encode(value)
594 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
595 config.add_section("alarm")
596 if self._alarmHandler is not None:
597 self._alarmHandler.save_settings(config, "alarm")
599 for backendId, view in itertools.chain(
600 self._dialpads.iteritems(),
601 self._accountViews.iteritems(),
602 self._messagesViews.iteritems(),
603 self._recentViews.iteritems(),
604 self._contactsViews.iteritems(),
606 sectionName = "%s - %s" % (backendId, view.name())
607 config.add_section(sectionName)
608 view.save_settings(config, sectionName)
610 def _save_settings(self):
612 @note Thread Agnostic
614 config = ConfigParser.SafeConfigParser()
615 self.save_settings(config)
616 with open(constants._user_settings_, "wb") as configFile:
617 config.write(configFile)
619 def _refresh_active_tab(self):
620 pageIndex = self._notebook.get_current_page()
621 if pageIndex == self.CONTACTS_TAB:
622 self._contactsViews[self._selectedBackendId].update(force=True)
623 elif pageIndex == self.RECENT_TAB:
624 self._recentViews[self._selectedBackendId].update(force=True)
625 elif pageIndex == self.MESSAGES_TAB:
626 self._messagesViews[self._selectedBackendId].update(force=True)
628 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
629 if self._ledHandler is not None:
630 self._ledHandler.off()
632 def _on_close(self, *args, **kwds):
634 if self._osso is not None:
638 self._save_settings()
642 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
644 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
645 For system_inactivity, we have no background tasks to pause
647 @note Hildon specific
651 for backendId in self.BACKENDS:
652 self._phoneBackends[backendId].clear_caches()
653 self._contactsViews[self._selectedBackendId].clear_caches()
656 if save_unsaved_data or shutdown:
657 self._save_settings()
659 self._errorDisplay.push_exception()
661 def _on_connection_change(self, connection, event, magicIdentifier):
663 @note Hildon specific
668 status = event.get_status()
669 error = event.get_error()
670 iap_id = event.get_iap_id()
671 bearer = event.get_bearer_type()
673 if status == conic.STATUS_CONNECTED:
675 self._spawn_attempt_login(2)
676 elif status == conic.STATUS_DISCONNECTED:
678 self._defaultBackendId = self._selectedBackendId
679 self._change_loggedin_status(self.NULL_BACKEND)
681 self._errorDisplay.push_exception()
683 def _on_window_state_change(self, widget, event, *args):
685 @note Hildon specific
688 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
689 self._isFullScreen = True
691 self._isFullScreen = False
693 self._errorDisplay.push_exception()
695 def _on_key_press(self, widget, event, *args):
697 @note Hildon specific
699 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
702 event.keyval == gtk.keysyms.F6 or
703 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
705 if self._isFullScreen:
706 self._window.unfullscreen()
708 self._window.fullscreen()
710 self._errorDisplay.push_exception()
712 def _on_clearcookies_clicked(self, *args):
714 self._phoneBackends[self._selectedBackendId].logout()
715 self._accountViews[self._selectedBackendId].clear()
716 self._recentViews[self._selectedBackendId].clear()
717 self._messagesViews[self._selectedBackendId].clear()
718 self._contactsViews[self._selectedBackendId].clear()
719 self._change_loggedin_status(self.NULL_BACKEND)
721 self._spawn_attempt_login(2, True)
723 self._errorDisplay.push_exception()
725 def _on_notebook_switch_page(self, notebook, page, pageIndex):
727 self._reset_tab_refresh()
729 didRecentUpdate = False
730 didMessagesUpdate = False
732 if pageIndex == self.RECENT_TAB:
733 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
734 elif pageIndex == self.MESSAGES_TAB:
735 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
736 elif pageIndex == self.CONTACTS_TAB:
737 self._contactsViews[self._selectedBackendId].update()
738 elif pageIndex == self.ACCOUNT_TAB:
739 self._accountViews[self._selectedBackendId].update()
741 if didRecentUpdate or didMessagesUpdate:
742 if self._ledHandler is not None:
743 self._ledHandler.off()
745 self._errorDisplay.push_exception()
747 def _set_tab_refresh(self, *args):
749 pageIndex = self._notebook.get_current_page()
750 child = self._notebook.get_nth_page(pageIndex)
751 self._notebook.get_tab_label(child).set_text("Refresh?")
753 self._errorDisplay.push_exception()
756 def _reset_tab_refresh(self, *args):
758 pageIndex = self._notebook.get_current_page()
759 child = self._notebook.get_nth_page(pageIndex)
760 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
762 self._errorDisplay.push_exception()
765 def _on_tab_refresh(self, *args):
767 self._refresh_active_tab()
768 self._reset_tab_refresh()
770 self._errorDisplay.push_exception()
773 def _on_sms_clicked(self, number, message):
775 assert number, "No number specified"
776 assert message, "Empty message"
778 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
781 self._errorDisplay.push_exception()
785 self._errorDisplay.push_message(
786 "Backend link with GoogleVoice is not working, please try again"
792 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
793 hildonize.show_information_banner(self._window, "Sending to %s" % number)
796 self._errorDisplay.push_exception()
799 self._dialpads[self._selectedBackendId].clear()
801 self._errorDisplay.push_exception()
803 def _on_dial_clicked(self, number):
805 assert number, "No number to call"
807 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
810 self._errorDisplay.push_exception()
814 self._errorDisplay.push_message(
815 "Backend link with GoogleVoice is not working, please try again"
821 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
822 self._phoneBackends[self._selectedBackendId].dial(number)
823 hildonize.show_information_banner(self._window, "Calling %s" % number)
826 self._errorDisplay.push_exception()
829 self._dialpads[self._selectedBackendId].clear()
831 self._errorDisplay.push_exception()
833 def _on_menu_refresh(self, *args):
835 self._refresh_active_tab()
837 self._errorDisplay.push_exception()
839 def _on_paste(self, *args):
841 contents = self._clipboard.wait_for_text()
842 if contents is not None:
843 self._dialpads[self._selectedBackendId].set_number(contents)
845 self._errorDisplay.push_exception()
847 def _on_about_activate(self, *args):
849 dlg = gtk.AboutDialog()
850 dlg.set_name(constants.__pretty_app_name__)
851 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
852 dlg.set_copyright("Copyright 2008 - LGPL")
853 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
854 dlg.set_website("http://gc-dialer.garage.maemo.org/")
855 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
859 self._errorDisplay.push_exception()
865 failureCount, testCount = doctest.testmod()
867 print "Tests Successful"
874 _lock_file = os.path.join(constants._data_path_, ".lock")
875 #with gtk_toolbox.flock(_lock_file, 0):
876 gtk.gdk.threads_init()
878 if hildonize.IS_HILDON_SUPPORTED:
879 gtk.set_application_name(constants.__pretty_app_name__)
880 handle = Dialcentral()
884 class DummyOptions(object):
890 if __name__ == "__main__":
891 logging.basicConfig(level=logging.DEBUG)
893 if len(sys.argv) > 1:
899 if optparse is not None:
900 parser = optparse.OptionParser()
901 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
902 (commandOptions, commandArgs) = parser.parse_args()
904 commandOptions = DummyOptions()
907 if commandOptions.test: