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 self._selectedBackendId = newStatus
500 self._accountViews[self._selectedBackendId].update()
501 self._refresh_active_tab()
503 def load_settings(self, config):
508 if not PROFILE_STARTUP:
509 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
511 self._defaultBackendId = self.NULL_BACKEND
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 # Because we now only support GVoice, if there are user credentials,
590 # always assume its using the GVoice backend
591 if self._credentials[0] and self._credentials[1]:
592 backend = self.GV_BACKEND
594 backend = self.NULL_BACKEND
596 config.add_section(constants.__pretty_app_name__)
597 config.set(constants.__pretty_app_name__, "active", str(backend))
598 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
599 for i, value in enumerate(self._credentials):
600 blob = base64.b64encode(value)
601 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
602 config.add_section("alarm")
603 if self._alarmHandler is not None:
604 self._alarmHandler.save_settings(config, "alarm")
606 for backendId, view in itertools.chain(
607 self._dialpads.iteritems(),
608 self._accountViews.iteritems(),
609 self._messagesViews.iteritems(),
610 self._recentViews.iteritems(),
611 self._contactsViews.iteritems(),
613 sectionName = "%s - %s" % (backendId, view.name())
614 config.add_section(sectionName)
615 view.save_settings(config, sectionName)
617 def _save_settings(self):
619 @note Thread Agnostic
621 config = ConfigParser.SafeConfigParser()
622 self.save_settings(config)
623 with open(constants._user_settings_, "wb") as configFile:
624 config.write(configFile)
626 def _refresh_active_tab(self):
627 pageIndex = self._notebook.get_current_page()
628 if pageIndex == self.CONTACTS_TAB:
629 self._contactsViews[self._selectedBackendId].update(force=True)
630 elif pageIndex == self.RECENT_TAB:
631 self._recentViews[self._selectedBackendId].update(force=True)
632 elif pageIndex == self.MESSAGES_TAB:
633 self._messagesViews[self._selectedBackendId].update(force=True)
635 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
636 if self._ledHandler is not None:
637 self._ledHandler.off()
639 def _on_close(self, *args, **kwds):
641 if self._osso is not None:
645 self._save_settings()
649 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
651 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
652 For system_inactivity, we have no background tasks to pause
654 @note Hildon specific
658 for backendId in self.BACKENDS:
659 self._phoneBackends[backendId].clear_caches()
660 self._contactsViews[self._selectedBackendId].clear_caches()
663 if save_unsaved_data or shutdown:
664 self._save_settings()
666 self._errorDisplay.push_exception()
668 def _on_connection_change(self, connection, event, magicIdentifier):
670 @note Hildon specific
675 status = event.get_status()
676 error = event.get_error()
677 iap_id = event.get_iap_id()
678 bearer = event.get_bearer_type()
680 if status == conic.STATUS_CONNECTED:
682 self._spawn_attempt_login()
683 elif status == conic.STATUS_DISCONNECTED:
685 self._defaultBackendId = self._selectedBackendId
686 self._change_loggedin_status(self.NULL_BACKEND)
688 self._errorDisplay.push_exception()
690 def _on_window_state_change(self, widget, event, *args):
692 @note Hildon specific
695 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
696 self._isFullScreen = True
698 self._isFullScreen = False
700 self._errorDisplay.push_exception()
702 def _on_key_press(self, widget, event, *args):
704 @note Hildon specific
706 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
709 event.keyval == gtk.keysyms.F6 or
710 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
712 if self._isFullScreen:
713 self._window.unfullscreen()
715 self._window.fullscreen()
717 self._errorDisplay.push_exception()
719 def _on_clearcookies_clicked(self, *args):
721 self._phoneBackends[self._selectedBackendId].logout()
722 self._accountViews[self._selectedBackendId].clear()
723 self._recentViews[self._selectedBackendId].clear()
724 self._messagesViews[self._selectedBackendId].clear()
725 self._contactsViews[self._selectedBackendId].clear()
726 self._change_loggedin_status(self.NULL_BACKEND)
728 self._spawn_attempt_login(True)
730 self._errorDisplay.push_exception()
732 def _on_notebook_switch_page(self, notebook, page, pageIndex):
734 self._reset_tab_refresh()
736 didRecentUpdate = False
737 didMessagesUpdate = False
739 if pageIndex == self.RECENT_TAB:
740 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
741 elif pageIndex == self.MESSAGES_TAB:
742 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
743 elif pageIndex == self.CONTACTS_TAB:
744 self._contactsViews[self._selectedBackendId].update()
745 elif pageIndex == self.ACCOUNT_TAB:
746 self._accountViews[self._selectedBackendId].update()
748 if didRecentUpdate or didMessagesUpdate:
749 if self._ledHandler is not None:
750 self._ledHandler.off()
752 self._errorDisplay.push_exception()
754 def _set_tab_refresh(self, *args):
756 pageIndex = self._notebook.get_current_page()
757 child = self._notebook.get_nth_page(pageIndex)
758 self._notebook.get_tab_label(child).set_text("Refresh?")
760 self._errorDisplay.push_exception()
763 def _reset_tab_refresh(self, *args):
765 pageIndex = self._notebook.get_current_page()
766 child = self._notebook.get_nth_page(pageIndex)
767 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
769 self._errorDisplay.push_exception()
772 def _on_tab_refresh(self, *args):
774 self._refresh_active_tab()
775 self._reset_tab_refresh()
777 self._errorDisplay.push_exception()
780 def _on_sms_clicked(self, number, message):
782 assert number, "No number specified"
783 assert message, "Empty message"
785 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
788 self._errorDisplay.push_exception()
792 self._errorDisplay.push_message(
793 "Backend link with GoogleVoice is not working, please try again"
799 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
800 hildonize.show_information_banner(self._window, "Sending to %s" % number)
803 self._errorDisplay.push_exception()
806 self._dialpads[self._selectedBackendId].clear()
808 self._errorDisplay.push_exception()
810 def _on_dial_clicked(self, number):
812 assert number, "No number to call"
814 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
817 self._errorDisplay.push_exception()
821 self._errorDisplay.push_message(
822 "Backend link with GoogleVoice is not working, please try again"
828 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
829 self._phoneBackends[self._selectedBackendId].dial(number)
830 hildonize.show_information_banner(self._window, "Calling %s" % number)
833 self._errorDisplay.push_exception()
836 self._dialpads[self._selectedBackendId].clear()
838 self._errorDisplay.push_exception()
840 def _on_menu_refresh(self, *args):
842 self._refresh_active_tab()
844 self._errorDisplay.push_exception()
846 def _on_paste(self, *args):
848 contents = self._clipboard.wait_for_text()
849 if contents is not None:
850 self._dialpads[self._selectedBackendId].set_number(contents)
852 self._errorDisplay.push_exception()
854 def _on_about_activate(self, *args):
856 dlg = gtk.AboutDialog()
857 dlg.set_name(constants.__pretty_app_name__)
858 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
859 dlg.set_copyright("Copyright 2008 - LGPL")
860 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
861 dlg.set_website("http://gc-dialer.garage.maemo.org/")
862 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
866 self._errorDisplay.push_exception()
872 failureCount, testCount = doctest.testmod()
874 print "Tests Successful"
881 _lock_file = os.path.join(constants._data_path_, ".lock")
882 #with gtk_toolbox.flock(_lock_file, 0):
883 gtk.gdk.threads_init()
885 if hildonize.IS_HILDON_SUPPORTED:
886 gtk.set_application_name(constants.__pretty_app_name__)
887 handle = Dialcentral()
888 if not PROFILE_STARTUP:
892 class DummyOptions(object):
898 if __name__ == "__main__":
899 logging.basicConfig(level=logging.DEBUG)
901 if len(sys.argv) > 1:
907 if optparse is not None:
908 parser = optparse.OptionParser()
909 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
910 (commandOptions, commandArgs) = parser.parse_args()
912 commandOptions = DummyOptions()
915 if commandOptions.test: