#!/usr/bin/python2.5
-# GC Dialer - Front end for Google's Grand Central service.
+# DialCentral - Front end for Google's Grand Central service.
# Copyright (C) 2008 Mark Bergman bergman AT merctech DOT com
#
# This library is free software; you can redistribute it and/or
"""
-Grandcentral Dialer
+DialCentral: A phone dialer using GrandCentral
"""
import sys
import gobject
import gtk
-gtk.gdk.threads_init()
import gtk.glade
try:
hildon = None
-"""
-This changes the default, system wide, socket timeout so that a hung server will not completly
-hork the application
-"""
-import socket
-socket.setdefaulttimeout(5)
-
def make_ugly(prettynumber):
"""
except StopIteration:
del a[:]
return False
-
+
decorated_func.__name__ = func.__name__
decorated_func.__doc__ = func.__doc__
decorated_func.__dict__.update(func.__dict__)
@returns Iterable of (Address Book Factory, Book Id, Book Name)
"""
yield self, "", "None"
-
+
def open_addressbook(self, bookId):
return self
@staticmethod
- def factory_short_name():
+ def contact_source_short_name(contactId):
return ""
@staticmethod
return []
+class MergedAddressBook(object):
+ """
+ Merger of all addressbooks
+ """
+
+ def __init__(self, addressbooks, sorter = None):
+ self.__addressbooks = addressbooks
+ self.__sort_contacts = sorter if sorter is not None else self.null_sorter
+
+ def get_addressbooks(self):
+ """
+ @returns Iterable of (Address Book Factory, Book Id, Book Name)
+ """
+ yield self, "", ""
+
+ def open_addressbook(self, bookId):
+ return self
+
+ def contact_source_short_name(self, contactId):
+ bookIndex, originalId = contactId.split("-", 1)
+ return self.__addressbooks[int(bookIndex)].contact_source_short_name(originalId)
+
+ @staticmethod
+ def factory_name():
+ return "All Contacts"
+
+ def get_contacts(self):
+ """
+ @returns Iterable of (contact id, contact name)
+ """
+ contacts = (
+ ("-".join([str(bookIndex), contactId]), contactName)
+ for (bookIndex, addressbook) in enumerate(self.__addressbooks)
+ for (contactId, contactName) in addressbook.get_contacts()
+ )
+ sortedContacts = self.__sort_contacts(contacts)
+ return sortedContacts
+
+ def get_contact_details(self, contactId):
+ """
+ @returns Iterable of (Phone Type, Phone Number)
+ """
+ bookIndex, originalId = contactId.split("-", 1)
+ return self.__addressbooks[int(bookIndex)].get_contact_details(originalId)
+
+ @staticmethod
+ def null_sorter(contacts):
+ return contacts
+
+ @staticmethod
+ def basic_lastname_sorter(contacts):
+ contactsWithKey = [
+ (contactName.rsplit(" ", 1)[-1], (contactId, contactName))
+ for (contactId, contactName) in contacts
+ ]
+ contactsWithKey.sort()
+ return (contactData for (lastName, contactData) in contactsWithKey)
+
+
class PhoneTypeSelector(object):
def __init__(self, widgetTree, gcBackend):
self._typeviewselection.unselect_all()
self._dialog.hide()
return phoneNumber
-
+
def _on_phonetype_select(self, *args):
self._dialog.response(gtk.RESPONSE_OK)
__app_magic__ = 0xdeadbeef
_glade_files = [
- './gc_dialer.glade',
- '../lib/gc_dialer.glade',
'/usr/lib/dialcentral/gc_dialer.glade',
+ os.path.join(os.path.dirname(__file__), "gc_dialer.glade"),
+ os.path.join(os.path.dirname(__file__), "../lib/gc_dialer.glade"),
]
def __init__(self):
self._contactsviewselection = None
self._clearall_id = None
-
+
for path in Dialpad._glade_files:
if os.path.isfile(path):
self._widgetTree = gtk.glade.XML(path)
self._widgetTree.get_widget("callbackcombo").get_child().set_property('hildon-input-mode', (1 << 4))
self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
+ hildon.hildon_helper_set_thumb_scrollbar(self._widgetTree.get_widget('contacts_scrolledwindow'), True)
+ hildon.hildon_helper_set_thumb_scrollbar(self._widgetTree.get_widget('recent_scrolledwindow'), True)
gtkMenu = self._widgetTree.get_widget("dialpad_menubar")
menu = gtk.Menu()
"""
If something can be done after the UI loads, push it here so it's not blocking the UI
"""
-
- from gc_backend import GCDialer
- from evo_backend import EvolutionAddressBook
- self._gcBackend = GCDialer()
+ import gc_backend
+ import evo_backend
+ import gmail_backend
+ import maemo_backend
+
+ self._gcBackend = gc_backend.GCDialer()
try:
import osso
warnings.warn("No Internet Connectivity API ", UserWarning, 2)
- self._addressBookFactories = [
+ addressBooks = [
self._gcBackend,
+ evo_backend.EvolutionAddressBook(),
DummyAddressBook(),
- EvolutionAddressBook(),
]
+ mergedBook = MergedAddressBook(addressBooks, MergedAddressBook.basic_lastname_sorter)
+ self._addressBookFactories = list(addressBooks)
+ self._addressBookFactories.insert(0, mergedBook)
self._addressBook = None
self.open_addressbook(*self.get_addressbooks().next()[0][0:2])
-
+
gtk.gdk.threads_enter()
self._booksList = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
gtk.gdk.threads_leave()
for (factoryId, bookId), (factoryName, bookName) in self.get_addressbooks():
if factoryName and bookName:
- entryName = "%s: %s" % (factoryName, bookName)
+ entryName = "%s: %s" % (factoryName, bookName)
elif factoryName:
entryName = factoryName
elif bookName:
combobox = self._widgetTree.get_widget("addressbook_combo")
combobox.set_model(self._booksList)
cell = gtk.CellRendererText()
- combobox.pack_start(cell, True)
- combobox.add_attribute(cell, 'text', 2)
+ combobox.pack_start(cell, True)
+ combobox.add_attribute(cell, 'text', 2)
combobox.set_active(0)
gtk.gdk.threads_leave()
self._init_contacts_view()
gtk.gdk.threads_leave()
- """
- This is where the blocking can start
- """
+ #This is where the blocking can start
if self._gcBackend.is_authed():
gtk.gdk.threads_enter()
self.set_account_number(self._gcBackend.get_account_number())
# Add the column to the treeview
column = gtk.TreeViewColumn("Contact")
+ #displayContactSource = False
+ displayContactSource = True
+ if displayContactSource:
+ textrenderer = gtk.CellRendererText()
+ column.pack_start(textrenderer, expand=False)
+ column.add_attribute(textrenderer, 'text', 0)
+
textrenderer = gtk.CellRendererText()
column.pack_start(textrenderer, expand=True)
column.add_attribute(textrenderer, 'text', 1)
combobox.get_child().set_text(make_pretty(self._gcBackend.get_callback_number()))
def _idly_populate_recentview(self):
+ self._recenttime = time.time()
self._recentmodel.clear()
for personsName, phoneNumber, date, action in self._gcBackend.get_recent():
self._recentmodel.append(item)
gtk.gdk.threads_leave()
- self._recenttime = time.time()
return False
- @make_idler
def _idly_populate_contactsview(self):
+ #@todo Add a lock so only one code path can be in here at a time
+ self._contactstime = time.time()
self._contactsmodel.clear()
# completely disable updating the treeview while we populate the data
contactsview.freeze_child_notify()
contactsview.set_model(None)
- contactType = (self._addressBook.factory_short_name(),)
- for contactId, contactName in self._addressBook.get_contacts():
+ addressBook = self._addressBook
+ for contactId, contactName in addressBook.get_contacts():
+ contactType = (addressBook.contact_source_short_name(contactId),)
self._contactsmodel.append(contactType + (contactName, "", contactId) + ("",))
- yield
# restart the treeview data rendering
contactsview.set_model(self._contactsmodel)
contactsview.thaw_child_notify()
-
- self._contactstime = time.time()
+ return False
def attempt_login(self, numOfAttempts = 1):
"""
if self._gcBackend.is_authed():
return True
-
+
for x in xrange(numOfAttempts):
gtk.gdk.threads_enter()
- dialog = self._widgetTree.get_widget("login_dialog")
+ dialog = self._widgetTree.get_widget("login_dialog")
dialog.set_transient_for(self._window)
dialog.set_default_response(0)
dialog.run()
self._gcBackend.set_sane_callback()
self.populate_callback_combo()
self.set_account_number(self._gcBackend.get_account_number())
- gtk.gdk.threads_leave()
- return True
+ gtk.gdk.threads_leave()
+ return True
+
+ return False
def display_error_message(self, msg):
error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
for i, factory in enumerate(self._addressBookFactories):
for bookFactory, bookId, bookName in factory.get_addressbooks():
yield (i, bookId), (factory.factory_name(), bookName)
-
+
def open_addressbook(self, bookFactoryId, bookId):
self._addressBook = self._addressBookFactories[bookFactoryId].open_addressbook(bookId)
self._contactstime = 0
- gobject.idle_add(self._idly_populate_contactsview)
+ threading.Thread(target=self._idly_populate_contactsview).start()
def set_number(self, number):
"""
if status == conic.STATUS_CONNECTED:
self._window.set_sensitive(True)
self._deviceIsOnline = True
- threading.Thread(target=self.attempt_login,args=[2]).start()
+ threading.Thread(target=self.attempt_login, args=[2]).start()
elif status == conic.STATUS_DISCONNECTED:
self._window.set_sensitive(False)
self._deviceIsOnline = False
self._isFullScreen = True
else:
self._isFullScreen = False
-
+
def _on_key_press(self, widget, event, *args):
"""
@note Hildon specific
self.set_account_number("")
# re-run the inital grandcentral setup
- threading.Thread(target=self.attempt_login,args=[2]).start()
+ threading.Thread(target=self.attempt_login, args=[2]).start()
#gobject.idle_add(self._idly_populate_callback_combo)
def _on_callbackentry_changed(self, *args):
self._contactsviewselection.unselect_all()
def _on_notebook_switch_page(self, notebook, page, page_num):
- if page_num == 1 and 300 < (time.time() - self._contactstime):
- threading.Thread(target=self._idly_populate_contactsview).start()
- elif page_num == 2 and 300 < (time.time() - self._recenttime):
- threading.Thread(target=self._idly_populate_recentview).start()
- #elif page_num == 3 and self._callbackNeedsSetup:
- # gobject.idle_add(self._idly_populate_callback_combo)
+ if page_num == 1:
+ if 300 < (time.time() - self._contactstime):
+ threading.Thread(target=self._idly_populate_contactsview).start()
+ elif page_num == 3:
+ if 300 < (time.time() - self._recenttime):
+ threading.Thread(target=self._idly_populate_recentview).start()
+ #elif page_num == 2:
+ # self._callbackNeedsSetup::
+ # gobject.idle_add(self._idly_populate_callback_combo)
tabTitle = self._notebook.get_tab_label(self._notebook.get_nth_page(page_num)).get_text()
if hildon is not None:
def _on_paste(self, *args):
contents = self._clipboard.wait_for_text()
+ phoneNumber = make_ugly(contents)
self.set_number(phoneNumber)
def _on_clear_number(self, *args):
self.set_number("")
def _on_digit_clicked(self, widget):
- self.set_number(self._phonenumber + widget.get_name()[5])
+ self.set_number(self._phonenumber + widget.get_name()[-1])
def _on_backspace(self, widget):
self.set_number(self._phonenumber[:-1])
dlg.set_copyright("Copyright 2008 - LGPL")
dlg.set_comments("Dialer is designed to interface with your Google Grandcentral account. This application is not affiliated with Google or Grandcentral in any way")
dlg.set_website("http://gc-dialer.garage.maemo.org/")
- dlg.set_authors(["<z2n@merctech.com>","Eric Warnke <ericew@gmail.com>","Ed Page <edpage@byu.net>"])
+ dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
dlg.run()
dlg.destroy()
-
+
def run_doctest():
import doctest