Logo Search packages:      
Sourcecode: jppy version File versions  Download package

gtk_models.py

import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade
import gobject
import os
import sys
import jppy
import types
import mx.DateTime
import ConfigParser

import queryLanguage

try:
    use_pyuca = jppy.config.get('core','unicode-collation').upper() == "YES"
except ConfigParser.NoOptionError, e:
    use_pyuca = False
    print "No unicode collator (disabled via configuration)"
collator = None
if use_pyuca:
    try:
        from pyuca import Collator
        print "Loading collation keys..."
        keysfilename = os.path.join("@@python_module_prefix@@",
                                    "gui","allkeys.txt")
        print "Attempting to load keys from %s" % keysfilename
        collator = Collator(keysfilename)
        print "loaded."
    except Exception,e :
        print "Failed to load collator, falling back to plain sort()"
        print e


pystring = types.StringType
gstring  = gobject.TYPE_STRING

pyenum   = types.IntType
genum    = gobject.TYPE_UINT

pyint   = types.IntType
gint    = gobject.TYPE_UINT

pybool   = types.BooleanType
gbool    = gobject.TYPE_BOOLEAN

pydate   = type(mx.DateTime.now())

gpyobject = gobject.TYPE_PYOBJECT

pylist = types.ListType

class jppyModel(gtk.GenericTreeModel):
    filter = {}

    def __init__(self):
      gtk.GenericTreeModel.__init__(self)

        self.records = None
        self.search  = ""
        self.queryfilter = None
        self.categories  = []

        self.columns = []

        n = 1
        for column in self._columns:
            self.columns.append((n,) + column)
            n += 1

        self.querygrammer = queryLanguage.makeGrammer([n[0] for n in self._columns])
        
        if hasattr(self,'_labels'):
            self.labels = list(self._labels)
        else:
            self._labels = {}
            self.labels = []

    def collate(self, records):
        if collator:
            def func(record):
                string = record[self.sort_key[0]]
                return collator.sort_key(string)
            return sorted(records, key=func)
        else:
            records.sort()
            return records

    def shouldListViewShowOneLine(self):
        return False

    def getColumnForNamesList(self,attribute_index):
        try:
            return 1 + len(self.columns) + self.labels.index(attribute_index)
        except:
            return None

    def getListOfNamesForColumn(self,column,node):
        if self._labels.has_key(column):
            return self._labels[column]
        else:
            return None

    def getNameOfColumn(self,column):
        return self._columns[column-1][0]
        
    def getStringNameForValue(self,column,index):
        if self.labels.has_key(column):
            try:
                return self.labels[column][index]
            except TypeError:
                return None
            
    def setSearchTerm(self,str):
        self.search = str
        self.queryfilter = None        

    def setQueryFilterTerm(self,str):
        self.queryfilter = queryLanguage.parseQuery(str, self.querygrammer)
        self.search = ""

    def setCategoryFilter(self,cats):
        self.categories = cats

    def setFilter(self, key, value):
        if self.filter.has_key(key):
            self.filter[key] = value
        else:
            raise NotImplementedError("No such filter key %s" % key)

    def save_to_disk(self):
        if self.records:
            for contact in self.records:
                if contact.unsaved_changes:
                    print "Saved changes to %s" % contact
                    self._database.save(contact)

    def new_record(self):
        return self.newrecord()

    def get_new_records(self):
        self.records = self._database.records(search=self.search,
                                              categories=self.categories)
        if self.queryfilter:
            self.records = queryLanguage.runQuery(self.queryfilter, self.records)
            if not isinstance(self.records, types.ListType):
                print "WARNING: %s" % self.records
                self.records = []
        self.records = self.collate(self.records)
        
    def available_categories(self):
        return self._database.getCategories()

    def available_categories_indexed(self):
        cats = zip(self.available_categories(),range(16))
        cats.sort()
        try:
            cats.remove(("Unfiled",0))
            cats.insert(0,("Unfiled",0))
        except ValueError:
            pass
        return filter(lambda r: r[0], cats)
    
    def rescan_database(self,save=1):
        print "Scanning database"
        if save:
            self.save_to_disk()
            
        if self.records:
            before = len(self.records)
        else:
            before = 0
        print self.search, self.categories

        self.get_new_records()

        after = len(self.records)

        if after < before:
            # we changed and deleted rows
            for n in range(0,after):
                path = (n,)
                self.row_changed(path,self.get_iter(path))

            for n in range(after, before):
                self.row_deleted((after,))
        elif after == before:
            # we only changed rows
            for n in range(0,after):
                path = (n,)
                self.row_changed(path,self.get_iter(path))
        else:
            # we changed and added rows
            for n in range(0,before-1):
                path = (n,)
                self.row_changed(path,self.get_iter(path))

            for n in range(before, after):
                path = (n,)                
                self.row_inserted(path,self.get_iter(path))

    def record_count(self):
        return len(self.records)

    def get_gobject_column_type(self, index):
        if index == 0:
            return gobject.TYPE_PYOBJECT
        elif index > len(self.columns):
            return gobject.TYPE_PYOBJECT
        else:
            t =  self.columns[index-1][4]
            return t

    def get_python_column_type(self, index):
      "returns the type of a column in the model"
        if index == 0:
            return types.ObjectType
        elif index > len(self.columns):
            return types.ListType
        else:
            t =  self.columns[index-1][5]
            #print "getting pytype of %d, giving %s" % (index, t)
            return t

    def convert_type(self, value, typewanted):
        #print "Converting %s (%s) to %s" % (repr(value), type(value), typewanted)
        if isinstance(value, typewanted):
            return value
        elif value == None:
            return None
        elif issubclass(typewanted, types.StringType):
            return str(value)
        elif issubclass(typewanted, types.IntType):
            return int(value)
        else:
            raise NotImplementedError("Cannot convert type %s to %s" % (type(value), typewanted))

    # normal API
    def remove(self, iter):
        path = self.get_path(iter)
        record = self.records[path[0]]
        print "DELETING %s:%s" % (path, record)
        self._database.delete(record)
        self.get_new_records()
        self.row_deleted(path)

    def append(self):
        if self.records:
            self.save_to_disk()
        else:
            self.records = []

        self.records.append(self.new_record())
        path = (len(self.records)-1,)
        iter = self.get_iter(path)
        self.row_inserted(path,iter)
        return iter
        
    def on_get_flags(self):
      "returns the GtkTreeModelFlags for this particular type of model"
      return gtk.TREE_MODEL_LIST_ONLY

    def on_get_n_columns(self):
      "returns the number of columns in the model"
      return len(self.columns) + len(self.labels)

    def on_get_column_type(self, index):
      "returns the type of a column in the model"
        return self.get_gobject_column_type(index)

    def on_get_path(self, node):
      "returns the tree path for a particular node."
      #return (self.records.index(node),)
        return (node[0],)        

    def on_get_iter(self, path):
        "returns the node corresponding to the given path."
        if not self.records:
            self.rescan_database()
        try:
            return (path[0],self.records[path[0]])
        except IndexError:
            return None

    def on_get_value(self, node, column):
      "returns the value stored in a particular column for the node"
        #print "get_value", node, column
        if column == 0:
            return node[1]
        elif column > len(self.columns):
            return self.getListOfNamesForColumn(self.labels[column-len(self.columns)-1],node)
        else:
            attribute = self.getNameOfColumn(column)
            if attribute in node[1].keys():
                return node[1][attribute]
            else:
                return getattr(node[1],attribute)

    def set_value(self, iter, column, value):
        pytype = self.get_python_column_type(column)
        if pytype == types.IntType and value == "":
            print "Damn GTK SpinButtons..."
            return
        print "set_value", repr(value), pytype, column, self.getNameOfColumn(column)
        value = self.convert_type(value, pytype)
        if column == 0:
            self.records[self.get_path(iter)[0]] = value
            self.row_changed(self.get_path(iter),iter)            
        else:
            contact = self.records[self.get_path(iter)[0]]
            attribute = self.getNameOfColumn(column)
            if attribute in contact.keys():
                if not contact[attribute] == value:
                    print "%s is not %s, updating contact attribute %s" % (
                        repr(contact[attribute]), repr(value), attribute)
                    contact[attribute] = value
                    print "Changed to %s" % contact[attribute]
            elif hasattr(contact, attribute):
                if not getattr(contact,attribute) == value:
                    setattr(contact, attribute, value)
            self.row_changed(self.get_path(iter),iter)

    def findAttributeNumberByName(self, attributename):
        for n, attribute, label, widgetname, gtype, pytype in self.columns:
            if attribute == attributename:
                return n
        return None

    def on_iter_next(self, node):
      "returns the next node at this level of the tree"
        try:
            pos = node[0]+1
            return (pos,self.records[pos])
        except IndexError:
            return None
        
    def on_iter_children(self, node):
      "returns the first child of this node"
      return None

    def on_iter_has_child(self, node):
      "returns true if this node has children"
      return 0

    def on_iter_n_children(self, node):
      "returns the number of children of this node"
        if not node:
            return 1
        return 0

    def on_iter_nth_child(self, node, n):
      "returns the nth child of this node"
        if not node:
            return (n,self.records[n])
        return None

    def on_iter_parent(self, node):
      "returns the parent of this node"
        return None

class ContactsModel(jppyModel):
    sort_key = ("lastname",)
    def __init__(self):
        # jppy attribute name, human label, glade widget name
        # order is from contact_as_list_item() in pytype_contacts.c
        
        # hmm.. this is quite nasty.

        self._database = jppy.addressBook()
        self.newrecord = jppy.Contact

        im    = self._database.getIMLabels()
        types = self._database.getAddressTypeLabels()        
        addr  = self._database.getAddressLabels()
        phone = self._database.getPhoneLabels()
        cats  = self._database.getCategories()

        self._labels = {5: types,
                        11: types,
                        17: types,
                        32: phone,
                        34: phone,
                        36: phone,
                        38: phone,
                        40: phone,
                        42: phone,
                        44: phone,
                        46: im,
                        48: im,
                        54: None, ## handled by our special getListOfNamesForColumn
                        56: cats}
        
        self._columns = [("lastname","Last Name","LastNameEntry",gstring,pystring),
                         ("firstname","First Name","FirstNameEntry",gstring,pystring),
                         ("company","Company","CompanyEntry",gstring,pystring),
                         ("title","Title","TitleEntry",gstring,pystring),

                         ("typeaddr1","Addr Label 1","AddrLabel1",genum,pyenum),
                         ("address1","Address 1","Address1",gstring,pystring),
                         ("city1","City 1","City1",gstring,pystring),
                         ("state1","State 1","State1",gstring,pystring),
                         ("zip1","Zip 1","Zip1",gstring,pystring),
                         ("country1","Country 1","Country1",gstring,pystring),

                         ("typeaddr2","Addr Label 2","AddrLabel2",genum,pyenum),
                         ("address2","Address 2","Address2",gstring,pystring),
                         ("city2","City 2","City2",gstring,pystring),
                         ("state2","State 2","State2",gstring,pystring),
                         ("zip2","Zip 2","Zip2",gstring,pystring),
                         ("country2","Country 2","Country2",gstring,pystring),

                         ("typeaddr3","Addr Label 3","AddrLabel3",genum,pyenum),
                         ("address3","Address 3","Address3",gstring,pystring),
                         ("city3","City 3","City3",gstring,pystring),
                         ("state3","State 3","State3",gstring,pystring),
                         ("zip3","Zip 3","Zip3",gstring,pystring),
                         ("country3","Country 3","Country3",gstring,pystring),
                         
                         ("custom1","Custom 1","Custom1",gstring,pystring),
                         ("custom2","Custom 2","Custom2",gstring,pystring),
                         ("custom3","Custom 3","Custom3",gstring,pystring),
                         ("custom4","Custom 4","Custom4",gstring,pystring),
                         ("custom5","Custom 5","Custom5",gstring,pystring),
                         ("custom6","Custom 6","Custom6",gstring,pystring),
                         ("custom7","Custom 7","Custom7",gstring,pystring),
                         ("custom8","Custom 8","Custom8",gstring,pystring),
                         ("custom9","Custom 9","Custom9",gstring,pystring),

                         ("type1","Label 1","PhoneLabel1",genum,pyenum),
                         ("phone1","Phone 1","Phone1",gstring,pystring),
                         ("type2","Label 2","PhoneLabel2",genum,pyenum),
                         ("phone2","Phone 2","Phone2",gstring,pystring),
                         ("type3","Label 3","PhoneLabel3",genum,pyenum),
                         ("phone3","Phone 3","Phone3",gstring,pystring),
                         ("type4","Label 4","PhoneLabel4",genum,pyenum),
                         ("phone4","Phone 4","Phone4",gstring,pystring),
                         ("type5","Label 5","PhoneLabel5",genum,pyenum),
                         ("phone5","Phone 5","Phone5",gstring,pystring),
                         ("type6","Label 6","PhoneLabel6",genum,pyenum),
                         ("phone6","Phone 6","Phone6",gstring,pystring),
                         ("type7","Label 7","PhoneLabel7",genum,pyenum),
                         ("phone7","Phone 7","Phone7",gstring,pystring),
                         
                         ("typeim1","IM Label 1","ImLabel1",genum,pyenum),
                         ("im1","IM 1","Im1",gstring,pystring),
                         ("typeim2","IM Label 2","ImLabel2",genum,pyenum),
                         ("im2","IM 2","Im2",gstring,pystring),                         
                         
                         ("website","Website","Website",gstring,pystring),
                         ("note","Note","ContactsNote",gstring,pystring),

                         ("birthday","Birthday","birthdayDateEdit",gpyobject,pydate),
                         ("reminder","Reminder","birthdayRemindSpinbutton",gpyobject,pyint),

                         ("showphone","Default Phone Label","Showphone",genum,pyenum),
                         ("currentphone","Default Phone","Currentphone",gstring,pystring),

                         ("category","Category","ContactsCategoryOptionMenu",genum,pyenum),
                         ("secret","Private","ContactsPrivateCheckButton",gbool,pybool),
                         ]
        
      jppyModel.__init__(self)

    def getListOfNamesForColumn(self,column,node):
        if column == 54:
            _labels = self._database.getPhoneLabels()
            labels = []
            for phone, label in node[1]['phones_with_labels']:
                labels.append(_labels[label])
            return labels
        else:
            return jppyModel.getListOfNamesForColumn(self, column, node)


class TasksModel(jppyModel):
    filter = {'hide_completed': True}
    sort_key = ("description",)
        
    def __init__(self):
        # jpilot attribute name, human label, glade widget name
        # order is from todo_as_list_item() in pytype_todos.c
        self._database = jppy.taskList()

        self.newrecord = jppy.Todo
        cats = self._database.getCategories()

        self._labels = {6: cats}

        self._columns = [("description","Description","TasksDescription",gstring,pystring),
                         ("due","Due","TasksDateDue",gpyobject,pydate),
                         ("complete","Done","TasksComplete",gbool, pybool),
                         ("priority","Priority","TasksPriority",genum, pyenum),
                         ("note","Note","TasksNote",gstring,pystring),                         

                         ("category","Category","TasksCategoryOptionMenu",genum,pyenum),
                         ("secret","Private","ContactsPrivateCheckButton",gbool, pybool)
                         ]

        jppyModel.__init__(self)
    
    def get_new_records(self):
        print self.filter
        self.records = []
        self._records = self._database.records(search=self.search,
                                               categories=self.categories)
        for record in self._records:
            if self.filter['hide_completed'] and record['complete']:
                continue
            self.records.append(record)

        if self.queryfilter:
            self.records = queryLanguage.runQuery(self.queryfilter, self.records)
            if not isinstance(self.records, types.ListType):
                print "WARNING: %s" % self.records
                self.records = []
        self.records = self.collate(self.records)

class MemosModel(jppyModel):
    sort_key = ("text",)
    def __init__(self):
        self._database = jppy.memoList()
        self.newrecord = jppy.Memo        
        cats = self._database.getCategories()
        self._labels = {2: cats}
        
        
        # jpilot attribute name, human label, glade widget name
        self._columns = [("text","Memo","Description",gstring,pystring),
                         ("category","Category","CategoryOptionMenu",genum,pyenum),
                         ("secret","Private","PrivateCheckButton",gbool, pybool)]

        jppyModel.__init__(self)
        
    def shouldListViewShowOneLine(self):
        return True

class Memos32Model(jppyModel):
    sort_key = ("text",)
    def __init__(self):
        self._database = jppy.memo32List()
        self.newrecord = jppy.Memo        
        cats = self._database.getCategories()
        self._labels = {2: cats}
        
        
        # jpilot attribute name, human label, glade widget name
        self._columns = [("text","Memo","Description",gstring,pystring),
                         ("category","Category","CategoryOptionMenu",genum,pyenum),
                         ("secret","Private","PrivateCheckButton",gbool, pybool)]

        jppyModel.__init__(self)
        
    def shouldListViewShowOneLine(self):
        return True

Generated by  Doxygen 1.6.0   Back to index