Example #1
0
    def get_tasks_tree(self):
        """This create a liblarch tree suitable for tasks,
        including default filters
        For tags, filter are dynamically created at Tag insertion.
        """
        tasktree = Tree()
        f_dic = {
            "workview": [self.workview],
            "active": [self.active],
            "closed": [self.closed, {"flat": True}],
            "notag": [self.notag],
            "workable": [self.is_workable],
            "started": [self.is_started],
            "workdue": [self.workdue],
            "workstarted": [self.workstarted],
            "worktostart": [self.worktostart],
            "worklate": [self.worklate],
            "no_disabled_tag": [self.no_disabled_tag],
        }

        for f in f_dic:
            filt = f_dic[f]
            if len(filt) > 1:
                param = filt[1]
            else:
                param = None
            tasktree.add_filter(f, filt[0], param)
        self.tasktree = tasktree
        return tasktree
Example #2
0
    def get_tasks_tree(self):
        '''This create a liblarch tree suitable for tasks,
        including default filters
        For tags, filter are dynamically created at Tag insertion.
        '''
        tasktree = Tree()
        f_dic = {
            'workview': [self.workview],
            'active': [self.active],
            'closed': [self.closed, {'flat': True}],
            'notag': [self.notag],
            'workable': [self.is_workable],
            'started': [self.is_started],
            'workdue': [self.workdue],
            'workstarted': [self.workstarted],
            'worktostart': [self.worktostart],
            'worklate': [self.worklate],
            'no_disabled_tag': [self.no_disabled_tag],
        }

        for f in f_dic:
            filt = f_dic[f]
            if len(filt) > 1:
                param = filt[1]
            else:
                param = None
            tasktree.add_filter(f, filt[0], param)
        self.tasktree = tasktree
        return tasktree
Example #3
0
    def get_tasks_tree(self):
        '''This create a liblarch tree suitable for tasks,
        including default filters
        For tags, filter are dynamically created at Tag insertion.
        '''
        tasktree = Tree()
        f_dic = {
            'workview': [self.workview],
            'active': [self.active],
            'closed': [self.closed, {
                'flat': True
            }],
            'notag': [self.notag],
            'workable': [self.is_workable],
            'started': [self.is_started],
            'workdue': [self.workdue],
            'workstarted': [self.workstarted],
            'worktostart': [self.worktostart],
            'worklate': [self.worklate],
            'no_disabled_tag': [self.no_disabled_tag],
        }

        for f in f_dic:
            filt = f_dic[f]
            if len(filt) > 1:
                param = filt[1]
            else:
                param = None
            tasktree.add_filter(f, filt[0], param)
        self.tasktree = tasktree
        return tasktree
Example #4
0
    def _build_tree_view(self):
        self.tree = Tree()
        self.tree.add_filter("even",self.even_filter)
        self.tree.add_filter("odd",self.odd_filter)
        self.tree.add_filter("flat",self.flat_filter,{"flat": True})
        self.tree.add_filter("leaf",self.leaf_filter)
        self.view_tree = self.tree.get_viewtree()
        self.mod_counter = 0
        
        self.view_tree.register_cllbck('node-added-inview',self._update_title)
        self.view_tree.register_cllbck('node-modified-inview',self._modified_count)
        self.view_tree.register_cllbck('node-deleted-inview',self._update_title)

        desc = {}

        col_name = 'label'
        col = {}
        col['title'] = "Title"
        col['value'] = [str, self.task_label_column]
        col['expandable'] = True
        col['resizable'] = True
        col['sorting'] = 'label'
        col['order'] = 0
        desc[col_name] = col

        tree_view = TreeView(self.view_tree, desc)

        # Polish TreeView
        def on_row_activate(sender,a,b):
            print(
                "Selected nodes are: {0!s}".format(
                    tree_view.get_selected_nodes()
                )
            )

        tree_view.set_dnd_name('liblarch-demo/liblarch_widget')
        tree_view.set_multiple_selection(True)

        tree_view.set_property("enable-tree-lines", True)
        tree_view.connect('row-activated', on_row_activate)

        return tree_view
Example #5
0
    def get_tags_tree(self, req):
        '''This create a liblarch tree suitable for tags,
        including the all_tags_tag and notag_tag.
        '''
        tagtree = Tree()

        # Build the "all tasks tag"
        alltag = tag.Tag(tag.ALLTASKS_TAG, req=req)
        alltag.set_attribute("special", "all")
        alltag.set_attribute("label", "<span weight='bold'>%s</span>"
                             % _("All tasks"))
        alltag.set_attribute("icon", "gtg-tags-all")
        alltag.set_attribute("order", 0)
        tagtree.add_node(alltag)
        p = {}
        self.tasktree.add_filter(tag.ALLTASKS_TAG,
                                 self.alltag, parameters=p)
        # Build the "without tag tag"
        notag_tag = tag.Tag(tag.NOTAG_TAG, req=req)
        notag_tag.set_attribute("special", "notag")
        notag_tag.set_attribute("label", "<span weight='bold'>%s</span>"
                                % _("Tasks with no tags"))
        notag_tag.set_attribute("icon", "gtg-tags-none")
        notag_tag.set_attribute("order", 2)
        tagtree.add_node(notag_tag)
        p = {}
        self.tasktree.add_filter(tag.NOTAG_TAG,
                                 self.notag, parameters=p)

        # Build the search tag
        search_tag = tag.Tag(tag.SEARCH_TAG, req=req)
        search_tag.set_attribute("special", "search")
        search_tag.set_attribute("label",
                                 "<span weight='bold'>%s</span>" % _("Search"))
        search_tag.set_attribute("icon", "search")
        search_tag.set_attribute("order", 1)
        tagtree.add_node(search_tag)
        p = {}
        self.tasktree.add_filter(tag.SEARCH_TAG,
                                 search_filter, parameters=p)

        # Build the separator
        sep_tag = tag.Tag(tag.SEP_TAG, req=req)
        sep_tag.set_attribute("special", "sep")
        sep_tag.set_attribute("order", 3)
        tagtree.add_node(sep_tag)

        # Filters
        tagtree.add_filter('activetag', self.actively_used_tag)
        tagtree.add_filter('usedtag', self.used_tag)

        activeview = tagtree.get_viewtree(name='activetags', refresh=False)
        activeview.apply_filter('activetag')

        # This view doesn't seem to be used. So it's not useful to build it now
#        usedview = tagtree.get_viewtree(name='usedtags',refresh=False)
#        usedview.apply_filter('usedtag')

        self.tagtree = tagtree
        self.tagtree_loaded = True
        return tagtree
Example #6
0
    def get_tags_tree(self, req):
        '''This create a liblarch tree suitable for tags,
        including the all_tags_tag and notag_tag.
        '''
        tagtree = Tree()

        ### building the initial tags
        # Build the "all tasks tag"
        alltag = Tag(CoreConfig.ALLTASKS_TAG, req=req)
        alltag.set_attribute("special", "all")
        alltag.set_attribute("label",
                             "<span weight='bold'>%s</span>" % _("All tasks"))
        alltag.set_attribute("icon", "gtg-tags-all")
        alltag.set_attribute("order", 0)
        tagtree.add_node(alltag)
        p = {}
        self.tasktree.add_filter(CoreConfig.ALLTASKS_TAG,
                                 self.alltag,
                                 parameters=p)
        # Build the "without tag tag"
        notag_tag = Tag(CoreConfig.NOTAG_TAG, req=req)
        notag_tag.set_attribute("special", "notag")
        notag_tag.set_attribute(
            "label", "<span weight='bold'>%s</span>" % _("Tasks with no tags"))
        notag_tag.set_attribute("icon", "gtg-tags-none")
        notag_tag.set_attribute("order", 2)
        tagtree.add_node(notag_tag)
        p = {}
        self.tasktree.add_filter(CoreConfig.NOTAG_TAG,
                                 self.notag,
                                 parameters=p)

        # Build the search tag
        search_tag = Tag(CoreConfig.SEARCH_TAG, req=req)
        search_tag.set_attribute("special", "search")
        search_tag.set_attribute("label",
                                 "<span weight='bold'>%s</span>" % _("Search"))
        search_tag.set_attribute("icon", "search")
        search_tag.set_attribute("order", 1)
        tagtree.add_node(search_tag)
        p = {}
        self.tasktree.add_filter(CoreConfig.SEARCH_TAG,
                                 search_filter,
                                 parameters=p)

        # Build the separator
        sep_tag = Tag(CoreConfig.SEP_TAG, req=req)
        sep_tag.set_attribute("special", "sep")
        sep_tag.set_attribute("order", 3)
        tagtree.add_node(sep_tag)

        #### Filters
        tagtree.add_filter('activetag', self.actively_used_tag)
        tagtree.add_filter('usedtag', self.used_tag)

        activeview = tagtree.get_viewtree(name='activetags', refresh=False)
        activeview.apply_filter('activetag')

        # This view doesn't seem to be used. So it's not useful to build it now
        #        usedview = tagtree.get_viewtree(name='usedtags',refresh=False)
        #        usedview.apply_filter('usedtag')

        self.tagtree = tagtree
        self.tagtree_loaded = True
        return tagtree
Example #7
0
    def get_tags_tree(self, req):
        """This create a liblarch tree suitable for tags,
        including the all_tags_tag and notag_tag.
        """
        tagtree = Tree()

        # Build the "all tasks tag"
        alltag = tag.Tag(tag.ALLTASKS_TAG, req=req)
        alltag.set_attribute("special", "all")
        alltag.set_attribute("label", "%s" % _("All tasks"))
        alltag.set_attribute("icon", "emblem-documents-symbolic")
        alltag.set_attribute("order", 0)
        tagtree.add_node(alltag)
        p = {}
        self.tasktree.add_filter(tag.ALLTASKS_TAG, self.alltag, parameters=p)
        # Build the "without tag tag"
        notag_tag = tag.Tag(tag.NOTAG_TAG, req=req)
        notag_tag.set_attribute("special", "notag")
        notag_tag.set_attribute("label", "%s" % _("Tasks with no tags"))
        notag_tag.set_attribute("icon", "task-past-due-symbolic")
        notag_tag.set_attribute("order", 2)
        tagtree.add_node(notag_tag)
        p = {}
        self.tasktree.add_filter(tag.NOTAG_TAG, self.notag, parameters=p)

        # Build the search tag
        search_tag = tag.Tag(tag.SEARCH_TAG, req=req)
        search_tag.set_attribute("special", "search")
        search_tag.set_attribute("label", _("Saved searches"))
        search_tag.set_attribute("icon", "system-search-symbolic")
        search_tag.set_attribute("order", 1)
        tagtree.add_node(search_tag)
        p = {}
        self.tasktree.add_filter(tag.SEARCH_TAG, search_filter, parameters=p)

        # Build the separator
        sep_tag = tag.Tag(tag.SEP_TAG, req=req)
        sep_tag.set_attribute("special", "sep")
        sep_tag.set_attribute("order", 3)
        tagtree.add_node(sep_tag)

        # Filters
        tagtree.add_filter('activetag', self.actively_used_tag)
        tagtree.add_filter('usedtag', self.used_tag)

        activeview = tagtree.get_viewtree(name='activetags', refresh=False)
        activeview.apply_filter('activetag')

        # This view doesn't seem to be used. So it's not useful to build it now
        #        usedview = tagtree.get_viewtree(name='usedtags',refresh=False)
        #        usedview.apply_filter('usedtag')

        self.tagtree = tagtree
        self.tagtree_loaded = True
        return tagtree
Example #8
0
class LiblarchDemo:
    """ Shows a simple GUI demo of liblarch usage
    with several functions for adding tasks """

    def _build_tree_view(self):
        self.tree = Tree()
        self.tree.add_filter("even",self.even_filter)
        self.tree.add_filter("odd",self.odd_filter)
        self.tree.add_filter("flat",self.flat_filter,{"flat": True})
        self.tree.add_filter("leaf",self.leaf_filter)
        self.view_tree = self.tree.get_viewtree()
        self.mod_counter = 0
        
        self.view_tree.register_cllbck('node-added-inview',self._update_title)
        self.view_tree.register_cllbck('node-modified-inview',self._modified_count)
        self.view_tree.register_cllbck('node-deleted-inview',self._update_title)

        desc = {}

        col_name = 'label'
        col = {}
        col['title'] = "Title"
        col['value'] = [str, self.task_label_column]
        col['expandable'] = True
        col['resizable'] = True
        col['sorting'] = 'label'
        col['order'] = 0
        desc[col_name] = col

        tree_view = TreeView(self.view_tree, desc)

        # Polish TreeView
        def on_row_activate(sender,a,b):
            print("Selected nodes are: %s" %str(tree_view.get_selected_nodes()))

        tree_view.set_dnd_name('liblarch-demo/liblarch_widget')
        tree_view.set_multiple_selection(True)

        tree_view.set_property("enable-tree-lines", True)
        tree_view.connect('row-activated', on_row_activate)

        return tree_view
        
    def even_filter(self,node):
        if node.get_id().isdigit():
            return int(node.get_id())%2 == 0
        else:
            return False
    
    def odd_filter(self,node):
        return not self.even_filter(node)
        
    def flat_filter(self,node,parameters=None):
        return True
        
    def leaf_filter(self,node):
        return not node.has_child()
        
    def _modified_count(self,nid,path):
#        print "Node %s has been modified" %nid
        self.mod_counter += 1
        
    def _update_title(self,sender,nid):
        count = self.view_tree.get_n_nodes()
        if count == LOAD_MANY_TASKS_COUNT and self.start_time > 0:
            stop_time = time() - self.start_time
            print("Time to load %s tasks: %s" %(LOAD_MANY_TASKS_COUNT,stop_time))
#        if count > 0:
            mean = self.mod_counter * 1.0 / count
            print("%s modified signals were received (%s per task)" %(self.mod_counter, mean))     
        self.window.set_title('Liblarch demo: %s nodes' %count)
        

    def __init__(self):
        self.window = Gtk.Window()
        self.window.set_size_request(640, 480)
        self.window.set_position(Gtk.WindowPosition.CENTER)
        self.window.set_border_width(10)
        self.window.set_title('Liblarch demo')
        self.window.connect('destroy', self.finish)

        self.liblarch_widget = self._build_tree_view()
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.add_with_viewport(self.liblarch_widget)
        
        self.start_time = 0

        # Buttons 
        action_panel = Gtk.Box()
        action_panel.set_spacing(5)

        button_desc = [('_Add a Task', self.add_task), 
            ('_Delete a Task', self.delete_task),
            ('_Print Tree', self.print_tree),
            ('_Print FT', self.print_ft),
            ('_Load many Tasks', self.many_tasks),
            ('_Quit', self.finish)]

        for name, callback in button_desc:
            button = Gtk.Button(name)
            button.connect('clicked', callback)
            action_panel.pack_start(button, True, True, 0)
            
        filter_panel= Gtk.Box()
        filter_panel.set_spacing(5)
            
        for name in self.tree.list_filters():
            button = Gtk.ToggleButton("%s filter"%name)
            button.connect('toggled',self.apply_filter,name)
            filter_panel.pack_start(button, True, True, 0)

        # Use cases
        usecases_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        usecase_box = None
        usecase_order = 0
        usecase_order_max = 3

        button_desc = [('_Tree high 3', self.tree_high_3),
            ('Tree high 3 backwards', self.tree_high_3_backwards),
            ('Load from file', self.load_from_file),
            ('Delete DFXBCAE', self.delete_magic),
            ('Delete backwards', self.delete_backwards),
            ('Delete randomly', self.delete_random),
            ('Change task', self.change_task),
            ('_Backend use case', self.backends),
            ]

        for name, callback in button_desc:
            if usecase_order <= 0:
                if usecase_box is not None:
                    usecases_vbox.pack_start(usecase_box, expand=False, fill=True, padding=0)
                usecase_box = Gtk.Box()
                usecase_box.set_spacing(5)

            button = Gtk.Button(name)
            button.connect('clicked', callback)
            usecase_box.pack_start(button, True, True, 0)

            usecase_order = (usecase_order + 1) % usecase_order_max

        usecases_vbox.pack_start(usecase_box, expand=False, fill=True, padding=0)
        usecase_panel = Gtk.Expander()
        usecase_panel.set_label('Use cases')
        usecase_panel.set_expanded(True)
        usecase_panel.add(usecases_vbox)

        # Show it
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(action_panel, False, True, 10)
        vbox.pack_start(filter_panel, False, True, 10)
        vbox.pack_start(scrolled_window, True, True, 0)
        vbox.pack_start(usecase_panel, False, True, 10)

        self.window.add(vbox)
        self.window.show_all()

        self.should_finish = threading.Event()

    def task_label_column(self, node):
        newlabel = node.get_label()
        return newlabel

    def print_tree(self, widget=None):
        print() 
        print("=" * 20, "Tree", "=" * 20)
        self.tree.get_main_view().print_tree()
        print("=" * 46)
        print()

    def print_ft(self, widget=None):
        print() 
        self.view_tree.print_tree()
        print()
    
    @save_backup
    def add_task(self, widget):
        """ Add a new task. If a task is selected,
        the new task is added as its child """
        selected = self.liblarch_widget.get_selected_nodes()

        t_id = random_id()
        t_title = random_task_title(t_id)
        task = TaskNode(t_id, t_title,self.view_tree)

        if len(selected) == 1:
            # Adding a subchild
            parent = selected[0]
            self.tree.add_node(task, parent_id = parent)
            print('Added sub-task "%s" (%s) for %s' % (t_title, t_id, parent))
        else:
            # Adding as a new child
            self.tree.add_node(task)
            for parent_id in selected:
                task.add_parent(parent_id)
            print('Added task "%s" (%s)' % (t_title, t_id))
            
    def apply_filter(self,widget,param):
        print("applying filter: %s" %param)
        if param in self.view_tree.list_applied_filters():
            self.view_tree.unapply_filter(param)
        else:
            self.view_tree.apply_filter(param)

    @save_backup
    def tree_high_3(self, widget):
        ''' We add the leaf nodes before the root, in order to test
        if it works fine even in this configuration'''
        print('Adding a tree of height 3')

        selected = self.liblarch_widget.get_selected_nodes()

        if len(selected) == 1:
            parent = selected[0]
        else:
            parent = None
            
        t_id = random_id()
        t_title = random_task_title(t_id)
        roottask = TaskNode(t_id, t_title,self.view_tree)
        local_parent = t_id

        for i in range(2):
            t_id = random_id()
            t_title = random_task_title(t_id)
            task = TaskNode(t_id, t_title,self.view_tree)

            self.tree.add_node(task, parent_id = local_parent)

            # Task becomes a parent for new task
            local_parent = t_id
            
        self.tree.add_node(roottask, parent_id = parent)

    @save_backup
    def tree_high_3_backwards(self, widget):
        print('Adding a tree of height 3 backwards')

        selected = self.liblarch_widget.get_selected_nodes()

        if len(selected) == 1:
            parent = selected[0]
        else:
            parent = None

        tasks = []
        relationships = []
        for i in range(3):
            t_id = random_id()
            t_title = random_task_title(t_id)
            task = TaskNode(t_id, t_title,self.view_tree)

            tasks.append((t_id, task))

            if parent is not None:
                relationships.append((parent, t_id))

            parent = t_id

        # Relationships can come in any order, e.g. reversed
        relationships = reversed(relationships)

        for t_id, task in tasks:
            print("Adding task to tree:", t_id, task)
            self.tree.add_node(task)
            print("="*50)

        for parent, child in relationships:
            print("New relationship: ", parent, "with", child)
            parent_node = self.tree.get_node(parent)
            parent_node.add_child(child)
            print("="*50)

        print()

    @save_backup
    def delete_task(self, widget, order='normal'):
        print('Deleting a task')
        selected = self.liblarch_widget.get_selected_nodes()

        print('Order: %s' % order)

        if   order == 'normal':
            ordered_nodes = selected
        elif order == 'backward':
            ordered_nodes = reversed(selected)
        elif order == 'random':
            ordered_nodes = selected
            shuffle(ordered_nodes)
            # Replace iterator for a list => 
            # we want to see the order in logs and the performance is not important
            ordered_nodes = [node for node in ordered_nodes]
        elif order == 'magic-combination':
            # testing a special case from examples/test_suite
            ordered_nodes = ['D', 'F', 'X', 'B', 'C', 'A', 'E']
        else:
            Log.error('Unknown order, skipping...')
            return

        print("Tasks should be removed in this order:", ordered_nodes)

        for node_id in ordered_nodes:
            self.tree.del_node(node_id)
            print('Removed node %s' % node_id)

        self.print_tree(None)

    def delete_backwards(self, widget):
        """ Delete task backward """
        self.delete_task(widget, order='backward')

    def delete_random(self, widget):
        """ Delete tasks in random order """
        self.delete_task(widget, order='random')

    def delete_magic(self, widget):
        self.delete_task(widget, order='magic-combination')

    def change_task(self, widget):
        view = self.tree.get_main_view()
        for node_id in self.liblarch_widget.get_selected_nodes():
            node = self.tree.get_node(node_id)
            node.label = "Ahoj"
            node.modified()

    def backends(self, widget):
        print("Backends....")
        Backend('1sec', self.should_finish, 1, self.tree).start()
        Backend('3sec', self.should_finish, 3, self.tree).start()
        Backend('5sec', self.should_finish, 5, self.tree).start()
        widget.set_sensitive(False)


    def many_tasks(self, widget):
        self.start_time = time()
        def _many_tasks():
            tasks_ids = []
            prefix = randint(1, 1000)* 100000
            for i in range(LOAD_MANY_TASKS_COUNT):
                t_id = str(prefix + i)
                t_title = t_id
                task = TaskNode(t_id, t_title,self.view_tree)

                # There is 25 % chance to adding as a sub_task
                if tasks_ids != [] and randint(0, 100) < 90:
                    parent = choice(tasks_ids)
                    self.tree.add_node(task, parent_id = parent)
                else:
                    self.tree.add_node(task)

                tasks_ids.append(t_id)

                # Sleep 0.01 second to create illusion of real tasks
                sleep(SLEEP_BETWEEN_TASKS)

            print("end of _many_tasks thread")
        t = threading.Thread(target=_many_tasks)
        t.start()

    def load_from_file(self, widget):
        dialog = Gtk.FileChooserDialog("Open..", self.window, 
            Gtk.FileChooserAction.OPEN, 
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
        dialog.set_default_response(Gtk.ResponseType.OK)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            file_name = dialog.get_filename()
        else:
            file_name = None
        dialog.destroy()

        if file_name is None:
            return

        log = open(file_name, 'r').read()

        m = re.match('\s*Tree before operation\s+=+\s+Tree\s+=+\s+(.*?)=+', log, re.UNICODE | re.DOTALL)
        if m:
            treelines = m.group(1)
            items = [(len(line) - len(line.lstrip()), line.strip()) for line in treelines.splitlines()]
            # Filter "root" item and decrease level
            items = [(level, name) for level, name in items[1:]]

            # The "root" items should be at level 0, adjust level to that
            min_level = min(level for level, name in items)
            items = [(level-min_level, name) for level, name in items]

            nodes = list(set([name for level, name in items]))

            relationships = []
            parent_level = { -1: None }

            for level, name in items:
                parent = parent_level[level - 1]
                relationships.append((parent, name))

                for key in list(parent_level.keys()):
                    if key > level:
                        del parent_level[key]

                parent_level[level] = name

            print("Nodes to add:", nodes)
            print("Relationships:", "\n".join(str(r) for r in relationships))
            print()

            for node_id in nodes:
                task = TaskNode(node_id, random_task_title(node_id),self.view_tree)
                self.tree.add_node(task)

            for parent, child in relationships:
                parent_node = self.tree.get_node(parent)
                parent_node.add_child(child)
        else:
            print("Not matched")
            print("Log: ", log)

    def finish(self, widget):
        self.should_finish.set()
        Gtk.main_quit()

    def run(self):
        Gtk.main()
Example #9
0
BIG_NUMBER = 2000
STAIRS = 200

from liblarch import Tree
from liblarch.tree import TreeNode
from liblarch_gtk import TreeView
import time, gtk

#This is a dummy treenode that only have one properties: a color
class DummyNode(TreeNode):
    def __init__(self,tid):
        TreeNode.__init__(self, tid)
        self.colors = []
        
        
tree = Tree()
view = tree.get_viewtree()
#view = tree.get_viewtree(refresh = False)
nodes_id = []
################now testing the GTK treeview ##################
#The columns description:
desc = {}
col = {}
col['title'] = "Node name"
render_text = gtk.CellRendererText()
col['renderer'] = ['markup',render_text]
def get_node_name(node):
    return node.get_id()
col['value'] = [str,get_node_name]
desc['titles'] = col
#treeview = TreeView(view,desc)
Example #10
0
class LiblarchDemo(object):
    """
    Shows a simple GUI demo of liblarch usage
    with several functions for adding tasks.
    """

    def _build_tree_view(self):
        self.tree = Tree()
        self.tree.add_filter("even",self.even_filter)
        self.tree.add_filter("odd",self.odd_filter)
        self.tree.add_filter("flat",self.flat_filter,{"flat": True})
        self.tree.add_filter("leaf",self.leaf_filter)
        self.view_tree = self.tree.get_viewtree()
        self.mod_counter = 0
        
        self.view_tree.register_cllbck('node-added-inview',self._update_title)
        self.view_tree.register_cllbck('node-modified-inview',self._modified_count)
        self.view_tree.register_cllbck('node-deleted-inview',self._update_title)

        desc = {}

        col_name = 'label'
        col = {}
        col['title'] = "Title"
        col['value'] = [str, self.task_label_column]
        col['expandable'] = True
        col['resizable'] = True
        col['sorting'] = 'label'
        col['order'] = 0
        desc[col_name] = col

        tree_view = TreeView(self.view_tree, desc)

        # Polish TreeView
        def on_row_activate(sender,a,b):
            print(
                "Selected nodes are: {0!s}".format(
                    tree_view.get_selected_nodes()
                )
            )

        tree_view.set_dnd_name('liblarch-demo/liblarch_widget')
        tree_view.set_multiple_selection(True)

        tree_view.set_property("enable-tree-lines", True)
        tree_view.connect('row-activated', on_row_activate)

        return tree_view

    def even_filter(self,node):
        if node.get_id().isdigit():
            return int(node.get_id())%2 == 0
        else:
            return False

    def odd_filter(self, node):
        return not self.even_filter(node)

    def flat_filter(self, node, parameters=None):
        return True

    def leaf_filter(self, node):
        return not node.has_child()

    def _modified_count(self, nid, path):
#        print("Node {0} has been modified".format(nid))
        self.mod_counter += 1

    def _update_title(self,sender,nid):
        count = self.view_tree.get_n_nodes()
        if count == LOAD_MANY_TASKS_COUNT and self.start_time > 0:
            stop_time = time() - self.start_time
            print(
                "Time to load {0} tasks: {1}".format(
                    LOAD_MANY_TASKS_COUNT,stop_time
                )
            )
#        if count > 0:
            mean = self.mod_counter * 1.0 / count
            print(
                "{0} modified signals were received ({1} per task)".format(
                    self.mod_counter, mean
                )
            )
        self.window.set_title('Liblarch demo: %s nodes' %count)

    def __init__(self):
        self.window = Gtk.Window()
        self.window.set_size_request(640, 480)
        self.window.set_position(Gtk.WindowPosition.CENTER)
        self.window.set_border_width(10)
        self.window.set_title('Liblarch demo')
        self.window.connect('destroy', self.finish)

        self.liblarch_widget = self._build_tree_view()
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.add_with_viewport(self.liblarch_widget)

        self.start_time = 0

        # Buttons
        action_panel = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        action_panel.set_spacing(5)

        button_desc = [
            ('_Add a Task', self.add_task), 
            ('_Delete a Task', self.delete_task),
            ('_Print Tree', self.print_tree),
            ('_Print FT', self.print_ft),
            ('_Load many Tasks', self.many_tasks),
            ('_Quit', self.finish)
        ]

        for name, callback in button_desc:
            button = Gtk.Button(name, use_underline=True)
            button.connect('clicked', callback)
            action_panel.pack_start(button, expand=True, fill=True, padding=0)
            
        filter_panel= Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        filter_panel.set_spacing(5)
            
        for name in self.tree.list_filters():
            button = Gtk.ToggleButton("%s filter"%name)
            button.connect('toggled',self.apply_filter,name)
            filter_panel.pack_start(button, expand=True, fill=True, padding=0)

        # Use cases
        usecases_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        usecase_box = None
        usecase_order = 0
        usecase_order_max = 3

        button_desc = [
            ('_Tree high 3', self.tree_high_3),
            ('Tree high 3 backwards', self.tree_high_3_backwards),
            ('Load from file', self.load_from_file),
            ('Delete DFXBCAE', self.delete_magic),
            ('Delete backwards', self.delete_backwards),
            ('Delete randomly', self.delete_random),
            ('Change task', self.change_task),
            ('_Backend use case', self.backends),
        ]

        for name, callback in button_desc:
            if usecase_order <= 0:
                if usecase_box is not None:
                    usecases_vbox.pack_start(
                        usecase_box,
                        expand=False,
                        fill=True,
                        padding=0
                    )
                usecase_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
                usecase_box.set_spacing(5)

            button = Gtk.Button(name, use_underline=True)
            button.connect('clicked', callback)
            usecase_box.pack_start(button, expand=True, fill=True, padding=0)

            usecase_order = (usecase_order + 1) % usecase_order_max

        usecases_vbox.pack_start(
            usecase_box,
            expand=False,
            fill=True,
            padding=0
        )
#        usecase_panel = Gtk.Expander('Use cases')
        usecase_panel = Gtk.Expander()
        usecase_panel.set_expanded(True)
        usecase_panel.add(usecases_vbox)

        # Show it
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(action_panel, False, True, 10)
        vbox.pack_start(filter_panel, False, True, 10)
        vbox.pack_start(scrolled_window, expand=True, fill=True, padding=0)
        vbox.pack_start(usecase_panel, False, True, 10)

        self.window.add(vbox)
        self.window.show_all()

        self.should_finish = threading.Event()

    def task_label_column(self, node):
        newlabel = node.get_label()
        return newlabel

    def print_tree(self, widget=None):
        print
        print("{0} Tree {0}".format("=" * 20))
        self.tree.get_main_view().print_tree()
        print("=" * 46)
        print

    def print_ft(self, widget=None):
        print
        self.view_tree.print_tree()
        print

    @save_backup
    def add_task(self, widget):
        """
        Add a new task. If a task is selected,
        the new task is added as its child
        """
        selected = self.liblarch_widget.get_selected_nodes()

        t_id = random_id()
        t_title = random_task_title(t_id)
        task = TaskNode(t_id, t_title,self.view_tree)

        if len(selected) == 1:
            # Adding a subchild
            parent = selected[0]
            self.tree.add_node(task, parent_id = parent)
            print(
                'Added sub-task "{0}" ({1}) for {2}'.format(
                    t_title, t_id, parent
                )
            )
        else:
            # Adding as a new child
            self.tree.add_node(task)
            for parent_id in selected:
                task.add_parent(parent_id)
            print('Added task "{0}" ({1})'.format(t_title, t_id))

    def apply_filter(self,widget,param):
        print("applying filter: {0}".format(param))
        if param in self.view_tree.list_applied_filters():
            self.view_tree.unapply_filter(param)
        else:
            self.view_tree.apply_filter(param)

    @save_backup
    def tree_high_3(self, widget):
        """
        We add the leaf nodes before the root, in order to test
        if it works fine even in this configuration
        """
        print('Adding a tree of height 3')

        selected = self.liblarch_widget.get_selected_nodes()

        if len(selected) == 1:
            parent = selected[0]
        else:
            parent = None
            
        t_id = random_id()
        t_title = random_task_title(t_id)
        roottask = TaskNode(t_id, t_title,self.view_tree)
        local_parent = t_id

        for i in range(2):
            t_id = random_id()
            t_title = random_task_title(t_id)
            task = TaskNode(t_id, t_title,self.view_tree)

            self.tree.add_node(task, parent_id = local_parent)

            # Task becomes a parent for new task
            local_parent = t_id
            
        self.tree.add_node(roottask, parent_id = parent)

    @save_backup
    def tree_high_3_backwards(self, widget):
        print('Adding a tree of height 3 backwards')

        selected = self.liblarch_widget.get_selected_nodes()

        if len(selected) == 1:
            parent = selected[0]
        else:
            parent = None

        tasks = []
        relationships = []
        for i in range(3):
            t_id = random_id()
            t_title = random_task_title(t_id)
            task = TaskNode(t_id, t_title,self.view_tree)

            tasks.append((t_id, task))

            if parent is not None:
                relationships.append((parent, t_id))

            parent = t_id

        # Relationships can come in any order, e.g. reversed
        relationships = reversed(relationships)

        for t_id, task in tasks:
            print("Adding task to tree: {0} {1}".format(t_id, task))
            self.tree.add_node(task)
            print("="*50)

        for parent, child in relationships:
            print("New relationship: {0} with {1}".format(parent, child))
            parent_node = self.tree.get_node(parent)
            parent_node.add_child(child)
            print("="*50)

        print

    @save_backup
    def delete_task(self, widget, order="normal"):
        print("Deleting a task")
        selected = self.liblarch_widget.get_selected_nodes()

        print("Order: {0}".format(order))

        if   order == "normal":
            ordered_nodes = selected
        elif order == "backward":
            ordered_nodes = reversed(selected)
        elif order == "random":
            ordered_nodes = selected
            shuffle(ordered_nodes)
            # Replace iterator for a list => 
            # we want to see the order in logs and the performance is not important
            ordered_nodes = [node for node in ordered_nodes]
        elif order == 'magic-combination':
            # testing a special case from examples/test_suite
            ordered_nodes = ['D', 'F', 'X', 'B', 'C', 'A', 'E']
        else:
            Log.error('Unknown order, skipping...')
            return

        print(
            "Tasks should be removed in this order: {0}".format(ordered_nodes)
        )

        for node_id in ordered_nodes:
            self.tree.del_node(node_id)
            print("Removed node {0}".format(node_id))

        self.print_tree(None)

    def delete_backwards(self, widget):
        """
        Delete task backward
        """
        self.delete_task(widget, order='backward')

    def delete_random(self, widget):
        """
        Delete tasks in random order
        """
        self.delete_task(widget, order='random')

    def delete_magic(self, widget):
        self.delete_task(widget, order='magic-combination')

    def change_task(self, widget):
        view = self.tree.get_main_view()
        for node_id in self.liblarch_widget.get_selected_nodes():
            node = self.tree.get_node(node_id)
            node.label = "Ahoj"
            node.modified()

    def backends(self, widget):
        print("Backends....")
        Backend('1sec', self.should_finish, 1, self.tree).start()
        Backend('3sec', self.should_finish, 3, self.tree).start()
        Backend('5sec', self.should_finish, 5, self.tree).start()
        widget.set_sensitive(False)


    def many_tasks(self, widget):
        self.start_time = time()
        def _many_tasks():
            tasks_ids = []
            prefix = randint(1, 1000)* 100000
            for i in range(LOAD_MANY_TASKS_COUNT):
                t_id = str(prefix + i)
                t_title = t_id
                task = TaskNode(t_id, t_title,self.view_tree)

                # There is 25 % chance to adding as a sub_task
                if tasks_ids != [] and randint(0, 100) < 90:
                    parent = choice(tasks_ids)
                    self.tree.add_node(task, parent_id = parent)
                else:
                    self.tree.add_node(task)

                tasks_ids.append(t_id)

                # Sleep 0.01 second to create illusion of real tasks
                sleep(SLEEP_BETWEEN_TASKS)

            print("end of _many_tasks thread")
        t = threading.Thread(target=_many_tasks)
        t.start()

    def load_from_file(self, widget):
        dialog = Gtk.FileChooserDialog("Open..", self.window, 
            Gtk.FileChooserAction.OPEN, 
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
        dialog.set_default_response(Gtk.ResponseType.OK)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            file_name = dialog.get_filename()
        else:
            file_name = None
        dialog.destroy()

        if file_name is None:
            return

        log = open(file_name, 'r').read()

        m = re.match(
            '\s*Tree before operation\s+=+\s+Tree\s+=+\s+(.*?)=+',
            log, re.UNICODE | re.DOTALL
        )
        if m:
            treelines = m.group(1)
            items = [
                (len(line) - len(line.lstrip()), line.strip())
                for line in treelines.splitlines()
            ]
            # Filter "root" item and decrease level
            items = [(level, name) for level, name in items[1:]]

            # The "root" items should be at level 0, adjust level to that
            min_level = min(level for level, name in items)
            items = [(level-min_level, name) for level, name in items]

            nodes = list(set([name for level, name in items]))

            relationships = []
            parent_level = { -1: None }

            for level, name in items:
                parent = parent_level[level - 1]
                relationships.append((parent, name))

                for key in list(parent_level.keys()):
                    if key > level:
                        del parent_level[key]

                parent_level[level] = name

            print("Nodes to add: {0}".format(nodes))
            print(
                "Relationships: {0}".format(
                    "\n".join(str(r) for r in relationships)
                )
            )
            print

            for node_id in nodes:
                task = TaskNode(
                    node_id,
                    random_task_title(node_id),
                    self.view_tree
                )
                self.tree.add_node(task)

            for parent, child in relationships:
                parent_node = self.tree.get_node(parent)
                parent_node.add_child(child)
        else:
            print("Not matched")
            print("Log: {0}".format(log))

    def finish(self, widget):
        self.should_finish.set()
        Gtk.main_quit()

    def run(self):
        Gtk.main()
Example #11
0
    def make_contact_list(self):

        # LIBLARCH TREE CONSTRUCTION
        # First thing, we create a liblarch tree
        self.tree = Tree()
        # Now, we add each contact *and* each team as nodes of that tree.
        # The team will be the parents of the contact nodes.
        for contact in CONTACTS:
            # We create the node and use the XMPP address as the node_id
            node = NodeContact(contact['xmpp'])
            # We add the status and the nickname
            node.set_status(contact['status'])
            node.set_nick(contact['name'])
            # The contact node is added to the tree
            self.tree.add_node(node)
            # Now, we create the team if it was not done before
            for team_name in contact['teams']:
                if not self.tree.has_node(team_name):
                    team_node = NodeTeam(team_name)
                    self.tree.add_node(team_node)
                # now we put the contact under the team
                node.add_parent(team_name)
                # we could also have done
                # team_node.add_child(contact[0])

        # LIBLARCH VIEW and FILTER
        # Ok, now we have our liblarch tree. What we need is a view.
        self.view = self.tree.get_viewtree()
        # We also create a filter that will allow us to hide offline people
        self.tree.add_filter("online", self.is_node_online)
        self.offline = False
        self.tree.add_filter("search", self.search_filter)
        # And we apply this filter by default
        self.view.apply_filter("online")

        # LIBLARCH GTK.TreeView
        # And, now, we build our Gtk.TreeView
        # We will build each column of our TreeView
        columns = {}
        # The first column contain the XMPP address but will be hidden
        # But it is still useful for searching
        col = {}
        col['value'] = [str, lambda node: node.get_id()]
        col['visible'] = False
        col['order'] = 0
        columns['XMPP'] = col
        # The second column is the status
        col = {}
        render_tags = CellRendererTags()
        render_tags.set_property('xalign', 0.0)
        col['renderer'] = ['status', render_tags]
        col['value'] = [GObject.TYPE_PYOBJECT, lambda node: node.get_status()]
        col['expandable'] = False
        col['resizable'] = False
        col['order'] = 1
        columns['status'] = col
        # the third column is the nickname
        col = {}
        col['value'] = [str, lambda node: node.get_label()]
        col['visible'] = True
        col['order'] = 2
        columns['nick'] = col

        return TreeView(self.view, columns)
Example #12
0
class ContactListWindow(object):
    def __init__(self):
        # First we do all the GTK stuff
        # This is not interesting from a liblarch perspective
        self.window = Gtk.Window()
        self.window.set_size_request(300, 600)
        self.window.set_border_width(12)
        self.window.set_title('Liblarch contact-list')
        self.window.connect('destroy', self.quit)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.set_spacing(6)
        # A check button to show/hide offline contacts
        show_offline = Gtk.CheckButton("Show offline contacts")
        show_offline.connect("toggled", self.show_offline_contacts)
        vbox.pack_start(show_offline, expand=False, fill=True, padding=0)
        # The search through contacts
        search = Gtk.Entry()
        search.set_icon_from_icon_name(0, "search")
        search.get_buffer().connect("inserted-text", self.search)
        search.get_buffer().connect("deleted-text", self.search)
        vbox.pack_start(search, expand=False, fill=True, padding=0)
        # The contact list, build with liblarch
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.add_with_viewport(self.make_contact_list())
        scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                   Gtk.PolicyType.AUTOMATIC)
        vbox.pack_start(scrolled_window, True, True, 0)
        # Status
        box = Gtk.ComboBoxText()
        box.append_text("Online")
        box.append_text("Busy")
        box.append_text("Offline")
        box.set_active(0)
        box.connect('changed', self.status_changed)
        vbox.pack_start(box, expand=False, fill=True, padding=0)
        self.window.add(vbox)
        self.window.show_all()

    # This is the interesting part on how we use liblarch
    def make_contact_list(self):

        # LIBLARCH TREE CONSTRUCTION
        # First thing, we create a liblarch tree
        self.tree = Tree()
        # Now, we add each contact *and* each team as nodes of that tree.
        # The team will be the parents of the contact nodes.
        for contact in CONTACTS:
            # We create the node and use the XMPP address as the node_id
            node = NodeContact(contact['xmpp'])
            # We add the status and the nickname
            node.set_status(contact['status'])
            node.set_nick(contact['name'])
            # The contact node is added to the tree
            self.tree.add_node(node)
            # Now, we create the team if it was not done before
            for team_name in contact['teams']:
                if not self.tree.has_node(team_name):
                    team_node = NodeTeam(team_name)
                    self.tree.add_node(team_node)
                # now we put the contact under the team
                node.add_parent(team_name)
                # we could also have done
                # team_node.add_child(contact[0])

        # LIBLARCH VIEW and FILTER
        # Ok, now we have our liblarch tree. What we need is a view.
        self.view = self.tree.get_viewtree()
        # We also create a filter that will allow us to hide offline people
        self.tree.add_filter("online", self.is_node_online)
        self.offline = False
        self.tree.add_filter("search", self.search_filter)
        # And we apply this filter by default
        self.view.apply_filter("online")

        # LIBLARCH GTK.TreeView
        # And, now, we build our Gtk.TreeView
        # We will build each column of our TreeView
        columns = {}
        # The first column contain the XMPP address but will be hidden
        # But it is still useful for searching
        col = {}
        col['value'] = [str, lambda node: node.get_id()]
        col['visible'] = False
        col['order'] = 0
        columns['XMPP'] = col
        # The second column is the status
        col = {}
        render_tags = CellRendererTags()
        render_tags.set_property('xalign', 0.0)
        col['renderer'] = ['status', render_tags]
        col['value'] = [GObject.TYPE_PYOBJECT, lambda node: node.get_status()]
        col['expandable'] = False
        col['resizable'] = False
        col['order'] = 1
        columns['status'] = col
        # the third column is the nickname
        col = {}
        col['value'] = [str, lambda node: node.get_label()]
        col['visible'] = True
        col['order'] = 2
        columns['nick'] = col

        return TreeView(self.view, columns)

    # This is the "online" filter.
    # It returns the contacts that are busy or online
    # and teams that have at least one contact displayed
    def is_node_online(self, node):
        if node.get_type() == "contact":
            # Always show myself
            if node.get_id() == '*****@*****.**':
                return True
            status = node.get_status()
            if status == "online" or status == "busy":
                return True
            else:
                return False
        # For the team, we test each contact of that team
        elif node.get_type() == "team":
            tree = node.get_tree()
            for child_id in node.get_children():
                child = tree.get_node(child_id)
                status = child.get_status()
                if status == "online" or status == "busy":
                    return True
            return False
        return True

    def show_offline_contacts(self, widget):
        # We should remove the filter to show offline contacts
        if widget.get_active():
            self.view.unapply_filter('online')
            self.offline = True
        # else we apply the "online" filter, showing only online/busy people
        else:
            self.view.apply_filter('online')
            self.offline = False

    def status_changed(self, widget):
        new = widget.get_active_text()
        node = self.tree.get_node('*****@*****.**')
        if new == 'Busy':
            node.set_status('busy')
        elif new == 'Offline':
            node.set_status('offline')
        else:
            node.set_status('online')

    def search(self, widget, position, char, nchar=None):
        search_string = widget.get_text()
        if len(search_string) > 0:
            # First, we remove the old filter
            # Note the "refresh=False", because we know we will apply another
            # filter just afterwards
            # We also remove the online filter to search through offline
            # contacts
            if not self.offline:
                self.view.unapply_filter('online', refresh=False)
            self.view.unapply_filter('search', refresh=False)
            self.view.apply_filter('search',
                                   parameters={'search': search_string})
        else:
            if not self.offline:
                self.view.apply_filter('online')
            self.view.unapply_filter('search')

    def search_filter(self, node, parameters=None):
        string = parameters['search']
        if node.get_type() == "contact":
            if string in node.get_id() or string in node.get_nick():
                return True
            else:
                return False
        else:
            return False

    def quit(self, widget):
        Gtk.main_quit()
Example #13
0
 def make_contact_list(self):
 
     ##### LIBLARCH TREE CONSTRUCTION
     #First thing, we create a liblarch tree
     self.tree = Tree()
     #Now, we add each contact *and* each team as nodes of that tree.
     #The team will be the parents of the contact nodes.
     for contact in CONTACTS:
         #We create the node and use the XMPP address as the node_id
         node = NodeContact(contact[0])
         #We add the status and the nickname
         node.set_status(contact[1])
         node.set_nick(contact[2])
         #The contact node is added to the tree
         self.tree.add_node(node)
         #Now, we create the team if it was not done before
         for team_name in contact[3]:
             if not self.tree.has_node(team_name):
                 team_node = NodeTeam(team_name)
                 self.tree.add_node(team_node)
             #now we put the contact under the team
             node.add_parent(team_name)
             #we could also have done
             #team_node.add_child(contact[0])
     
     ###### LIBLARCH VIEW and FILTER
     #Ok, now we have our liblarch tree. What we need is a view.
     self.view = self.tree.get_viewtree()
     #We also create a filter that will allow us to hide offline people
     self.tree.add_filter("online",self.is_node_online)
     self.offline = False
     self.tree.add_filter("search",self.search_filter)
     #And we apply this filter by default
     self.view.apply_filter("online")
     
     ###### LIBLARCH GTK.TreeView
     #And, now, we build our Gtk.TreeView
     #We will build each column of our TreeView
     columns = {}
     #The first column contain the XMPP address but will be hidden
     #But it is still useful for searching
     col = {}
     col['value'] = [str, lambda node: node.get_id()]
     col['visible'] = False
     col['order'] = 0
     columns['XMPP'] = col
     #The second column is the status
     col = {}
     render_tags = CellRendererTags()
     render_tags.set_property('xalign', 0.0)
     col['renderer'] = ['status',render_tags]
     col['value'] = [gobject.TYPE_PYOBJECT,lambda node: node.get_status()]
     col['expandable'] = False
     col['resizable'] = False
     col['order'] = 1
     columns['status'] = col
     #the third column is the nickname
     col = {}
     col['value'] = [str, lambda node: node.get_label()]
     col['visible'] = True
     col['order'] = 2
     columns['nick'] = col
     
     
     return TreeView(self.view,columns)
Example #14
0
class contact_list_window():

    
    def __init__(self):
        # First we do all the GTK stuff
        # This is not interesting from a liblarch perspective
        self.window = gtk.Window()
        self.window.set_size_request(300, 600)
        self.window.set_border_width(12)
        self.window.set_title('Liblarch contact-list')
        self.window.connect('destroy', self.quit)
        vbox = gtk.VBox()
        vbox.set_spacing(6)
        #A check button to show/hide offline contacts
        show_offline = gtk.CheckButton("Show offline contacts")
        show_offline.connect("toggled",self.show_offline_contacts)
        vbox.pack_start(show_offline, False, True)
        #The search through contacts
        search = gtk.Entry()
        search.set_icon_from_icon_name(0, "search")
        search.get_buffer().connect("inserted-text",self.search)
        search.get_buffer().connect("deleted-text",self.search)
        vbox.pack_start(search, False, True)
        #The contact list, build with liblarch
        scrolled_window = gtk.ScrolledWindow()
        scrolled_window.add_with_viewport(self.make_contact_list())
        scrolled_window.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC)
        vbox.pack_start(scrolled_window)
        #Status
        box = gtk.combo_box_new_text()
        box.append_text("Online")
        box.append_text("Busy")
        box.append_text("Offline")
        box.set_active(0)
        box.connect('changed',self.status_changed)
        vbox.pack_start(box, False, True)
        self.window.add(vbox)
        self.window.show_all()
        
    
    #This is the interesting part on how we use liblarch
    def make_contact_list(self):
    
        ##### LIBLARCH TREE CONSTRUCTION
        #First thing, we create a liblarch tree
        self.tree = Tree()
        #Now, we add each contact *and* each team as nodes of that tree.
        #The team will be the parents of the contact nodes.
        for contact in CONTACTS:
            #We create the node and use the XMPP address as the node_id
            node = NodeContact(contact[0])
            #We add the status and the nickname
            node.set_status(contact[1])
            node.set_nick(contact[2])
            #The contact node is added to the tree
            self.tree.add_node(node)
            #Now, we create the team if it was not done before
            for team_name in contact[3]:
                if not self.tree.has_node(team_name):
                    team_node = NodeTeam(team_name)
                    self.tree.add_node(team_node)
                #now we put the contact under the team
                node.add_parent(team_name)
                #we could also have done
                #team_node.add_child(contact[0])
        
        ###### LIBLARCH VIEW and FILTER
        #Ok, now we have our liblarch tree. What we need is a view.
        self.view = self.tree.get_viewtree()
        #We also create a filter that will allow us to hide offline people
        self.tree.add_filter("online",self.is_node_online)
        self.offline = False
        self.tree.add_filter("search",self.search_filter)
        #And we apply this filter by default
        self.view.apply_filter("online")
        
        ###### LIBLARCH GTK.TreeView
        #And, now, we build our Gtk.TreeView
        #We will build each column of our TreeView
        columns = {}
        #The first column contain the XMPP address but will be hidden
        #But it is still useful for searching
        col = {}
        col['value'] = [str, lambda node: node.get_id()]
        col['visible'] = False
        col['order'] = 0
        columns['XMPP'] = col
        #The second column is the status
        col = {}
        render_tags = CellRendererTags()
        render_tags.set_property('xalign', 0.0)
        col['renderer'] = ['status',render_tags]
        col['value'] = [gobject.TYPE_PYOBJECT,lambda node: node.get_status()]
        col['expandable'] = False
        col['resizable'] = False
        col['order'] = 1
        columns['status'] = col
        #the third column is the nickname
        col = {}
        col['value'] = [str, lambda node: node.get_label()]
        col['visible'] = True
        col['order'] = 2
        columns['nick'] = col
        
        
        return TreeView(self.view,columns)
    
    #This is the "online" filter.
    #It returns the contacts that are busy or online
    #and teams that have at least one contact displayed
    def is_node_online(self,node):
        if node.get_type() == "contact":
            #Always show myself
            if node.get_id() == '*****@*****.**': 
                return True
            status = node.get_status()
            if status == "online" or status == "busy":
                return True
            else:
                return False
        #For the team, we test each contact of that team
        elif node.get_type() == "team":
            tree = node.get_tree()
            for child_id in node.get_children():
                child = tree.get_node(child_id)
                status = child.get_status()
                if status == "online" or status == "busy":
                    return True
            return False
        return True
        
    def show_offline_contacts(self,widget):
        #We should remove the filter to show offline contacts
        if widget.get_active():
            self.view.unapply_filter('online')
            self.offline = True
        #else we apply the "online" filter, showing only online/busy people
        else:
            self.view.apply_filter('online')
            self.offline = False
            
    def status_changed(self,widget):
        new = widget.get_active_text()
        node = self.tree.get_node('*****@*****.**')
        if new == 'Busy': node.set_status('busy')
        elif new == 'Offline': node.set_status('offline')
        else: node.set_status('online')
        
    def search(self,widget,position,char,nchar=None):
        search_string = widget.get_text()
        if len(search_string) > 0:
            #First, we remove the old filter
            #note the "refresh=False", because we know we will apply
            #another filter just afterwards
            #We also remove the online filter to search through offline contacts
            if not self.offline:
                self.view.unapply_filter('online',refresh=False)
            self.view.unapply_filter('search',refresh=False)
            self.view.apply_filter('search',parameters={'search':search_string})
        else:
            if not self.offline:
                self.view.apply_filter('online')
            self.view.unapply_filter('search')
        
    def search_filter(self,node,parameters=None):
        string = parameters['search']
        if node.get_type() == "contact":
            if string in node.get_id() or string in node.get_nick():
                return True
            else:
                return False
        else:
            return False
        
    def quit(self, widget):
        gtk.main_quit()