~saurabhanandiit/gtg/exportFixed

« back to all changes in this revision

Viewing changes to GTG/tools/liblarch/__init__.py

  • Committer: Izidor Matušov
  • Date: 2012-02-29 10:06:41 UTC
  • mfrom: (1098 gtg)
  • mto: This revision was merged to the branch mainline in revision 1103.
  • Revision ID: izidor.matusov@gmail.com-20120229100641-q1uns4yqz1fem2z4
Merged with the current trunk & solved problems with offset

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
# -----------------------------------------------------------------------------
3
 
# Getting Things Gnome! - a personal organizer for the GNOME desktop
4
 
# Copyright (c) 2008-2011- Lionel Dricot & Bertrand Rousseau
5
 
#
6
 
# This program is free software: you can redistribute it and/or modify it under
7
 
# the terms of the GNU General Public License as published by the Free Software
8
 
# Foundation, either version 3 of the License, or (at your option) any later
9
 
# version.
10
 
#
11
 
# This program is distributed in the hope that it will be useful, but WITHOUT
12
 
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 
# details.
15
 
#
16
 
# You should have received a copy of the GNU General Public License along with
17
 
# this program.  If not, see <http://www.gnu.org/licenses/>.
18
 
 
19
 
import functools
20
 
 
21
 
from GTG.tools.liblarch.tree import MainTree
22
 
from GTG.tools.liblarch.filteredtree import FilteredTree
23
 
from GTG.tools.liblarch.filters_bank import FiltersBank
24
 
 
25
 
class Tree:
26
 
    """ A thin wrapper to MainTree that adds filtering capabilities.
27
 
    It also provides a few methods to operate complex operation on the
28
 
    MainTree (e.g, move_node) """ 
29
 
 
30
 
    def __init__(self):
31
 
        """ Creates MainTree which wraps and a main view without filters """
32
 
        self.__tree = MainTree()
33
 
        self.__fbank = FiltersBank(self.__tree)
34
 
        self.views = {}
35
 
        self.views['main'] = ViewTree(self, self.__tree, self.__fbank, static=True)
36
 
 
37
 
##### HANDLE NODES ############################################################
38
 
 
39
 
    def get_node(self, node_id):
40
 
        """ Returns the object of node.
41
 
        If the node does not exists, a ValueError is raised. """
42
 
        return self.__tree.get_node(node_id)
43
 
 
44
 
    def has_node(self, node_id):
45
 
        """ Does the node exists in this tree? """
46
 
        return self.__tree.has_node(node_id)
47
 
 
48
 
    def add_node(self, node, parent_id=None, high_priority=False):
49
 
        """ Add a node to tree. If parent_id is set, put the node as a child of
50
 
        this node, otherwise put it as a child of the root node."""
51
 
        self.__tree.add_node(node, parent_id, high_priority)
52
 
 
53
 
    def del_node(self, node_id, recursive=False):
54
 
        """ Remove node from tree and return whether it was successful or not """
55
 
        return self.__tree.remove_node(node_id, recursive)
56
 
 
57
 
    def refresh_node(self, node_id):
58
 
        """ Send a request for updating the node """
59
 
        self.__tree.modify_node(node_id)
60
 
 
61
 
    def refresh_all(self):
62
 
        """ Refresh all nodes """
63
 
        self.__tree.refresh_all()
64
 
 
65
 
    def move_node(self, node_id, new_parent_id=None):
66
 
        """ Move the node to a new parent (dismissing all other parents)
67
 
        use pid None to move it to the root """
68
 
        if self.has_node(node_id):
69
 
            node = self.get_node(node_id)
70
 
            node.set_parent(new_parent_id)
71
 
            toreturn = True
72
 
        else:
73
 
            toreturn = False
74
 
 
75
 
        return toreturn
76
 
 
77
 
 
78
 
    def add_parent(self, node_id, new_parent_id=None):
79
 
        """ Add the node to a new parent. Return whether operation was
80
 
        successful or not. If the node does not exists, return False """
81
 
 
82
 
        if self.has_node(node_id):
83
 
            node = self.get_node(node_id)
84
 
            return node.add_parent(new_parent_id)
85
 
        else:
86
 
            return False
87
 
 
88
 
##### VIEWS ###################################################################
89
 
    def get_main_view(self):
90
 
        """ Return the special view "main" which is without any filters on it."""
91
 
        return self.views['main']
92
 
 
93
 
    def get_viewtree(self, name=None, refresh=True):
94
 
        """ Returns a viewtree by the name:
95
 
          * a viewtree with that name exists => return it
96
 
          * a viewtree with that name does not exist => create a new one and return it
97
 
          * name is None => create an anonymous tree (do not remember it)
98
 
 
99
 
        If refresh is False, the view is not initialized. This is useful as
100
 
        an optimization if you plan to apply a filter.
101
 
        """
102
 
 
103
 
        if name is not None and self.views.has_key(name):
104
 
            view_tree = self.views[name]
105
 
        else:
106
 
            view_tree = ViewTree(self,self.__tree,self.__fbank,refresh=refresh)
107
 
            if name is not None:
108
 
                self.views[name] = view_tree
109
 
        return view_tree
110
 
 
111
 
##### FILTERS ##################################################################
112
 
    def list_filters(self):
113
 
        """ Return a list of all available filters by name """
114
 
        return self.__fbank.list_filters()
115
 
 
116
 
    def add_filter(self, filter_name, filter_func, parameters=None):
117
 
        """ Adds a filter to the filter bank.
118
 
 
119
 
        @filter_name : name to give to the filter
120
 
        @filter_func : the function that will filter the nodes
121
 
        @parameters : some default parameters fot that filter
122
 
        Return True if the filter was added
123
 
        Return False if the filter_name was already in the bank
124
 
        """
125
 
        return self.__fbank.add_filter(filter_name, filter_func, parameters)
126
 
 
127
 
    def remove_filter(self,filter_name):
128
 
        """ Remove a filter from the bank. Only custom filters that were 
129
 
        added here can be removed. Return False if the filter was not removed.
130
 
        """
131
 
        return self.__fbank.remove_filter(filter_name)
132
 
 
133
 
# There should be two classes: for static and for dynamic mode
134
 
# There are many conditions, and also we would prevent unallowed modes
135
 
class ViewTree:
136
 
    def __init__(self, maininterface, maintree, filters_bank,\
137
 
                                             refresh = True, static = False):
138
 
        """A ViewTree is the interface that should be used to display Tree(s).
139
 
 
140
 
           In static mode, FilteredTree layer is not created. (There is no need)
141
 
 
142
 
           We connect to MainTree or FilteredTree to get informed about changes.
143
 
           If FilteredTree is used, it is connected to MainTree to handle changes
144
 
           and then send id to ViewTree if it applies.
145
 
 
146
 
           @param maintree: a Tree object, cointaining all the nodes
147
 
           @param filters_bank: a FiltersBank object. Filters can be added
148
 
                                dinamically to that.
149
 
           @param refresh: if True, this ViewTree is automatically refreshed
150
 
                           after applying a filter.
151
 
           @param static: if True, this is the view of the complete maintree.
152
 
                           Filters cannot be added to such a view.
153
 
        """
154
 
        self.maininterface = maininterface
155
 
        self.__maintree = maintree
156
 
        self.__cllbcks = {}
157
 
        self.__fbank = filters_bank
158
 
        self.static = static
159
 
 
160
 
        if self.static:
161
 
            self._tree = self.__maintree
162
 
            self.__ft = None
163
 
            self.__maintree.register_callback('node-added', \
164
 
                        functools.partial(self.__emit, 'node-added'))
165
 
            self.__maintree.register_callback('node-deleted', \
166
 
                        functools.partial(self.__emit, 'node-deleted'))
167
 
            self.__maintree.register_callback('node-modified', \
168
 
                        functools.partial(self.__emit, 'node-modified'))
169
 
        else:
170
 
            self.__ft = FilteredTree(maintree, filters_bank, refresh = refresh)
171
 
            self._tree = self.__ft
172
 
            self.__ft.set_callback('added', \
173
 
                        functools.partial(self.__emit, 'node-added-inview'))
174
 
            self.__ft.set_callback('deleted', \
175
 
                        functools.partial(self.__emit, 'node-deleted-inview'))
176
 
            self.__ft.set_callback('modified', \
177
 
                        functools.partial(self.__emit, 'node-modified-inview'))
178
 
            self.__ft.set_callback('reordered', \
179
 
                        functools.partial(self.__emit, 'node-children-reordered'))
180
 
                        
181
 
    def queue_action(self, node_id,func,param=None):
182
 
        self.__ft.set_callback('runonce',func,node_id=node_id,param=param)
183
 
                        
184
 
    def get_basetree(self):
185
 
        """ Return Tree object """
186
 
        return self.maininterface
187
 
 
188
 
    def register_cllbck(self, event, func):
189
 
        """ Store function and return unique key which can be used to
190
 
        unregister the callback later """
191
 
 
192
 
        if not self.__cllbcks.has_key(event):
193
 
            self.__cllbcks[event] = {}
194
 
 
195
 
        callbacks = self.__cllbcks[event]
196
 
        key = 0
197
 
        while callbacks.has_key(key):
198
 
            key += 1
199
 
 
200
 
        callbacks[key] = func
201
 
        return key
202
 
 
203
 
    def deregister_cllbck(self, event, key):
204
 
        """ Remove the callback identifed by key (from register_cllbck) """
205
 
        try:
206
 
            del self.__cllbcks[event][key]
207
 
        except KeyError:
208
 
            pass
209
 
        
210
 
    def __emit(self, event, node_id, path=None, neworder=None):
211
 
        """ Handle a new event from MainTree or FilteredTree
212
 
        by passing it to other objects, e.g. TreeWidget """
213
 
        callbacks = dict(self.__cllbcks.get(event, {}))
214
 
 
215
 
        for func in callbacks.itervalues():
216
 
            if neworder:
217
 
                func(node_id, path, neworder)
218
 
            else:
219
 
                func(node_id,path)
220
 
 
221
 
    def get_node(self, node_id):
222
 
        """ Get a node from MainTree """
223
 
        return self.__maintree.get_node(node_id)
224
 
        
225
 
    #FIXME WTF?
226
 
    def __get_static_node(self,node_id):
227
 
        toreturn = None
228
 
        if self.static:
229
 
            if not node_id or node_id == 'root':
230
 
                toreturn = self.__maintree.get_root()
231
 
            else:
232
 
                toreturn = self.__maintree.get_node(node_id)
233
 
        else:
234
 
            raise Exception("You should not get a static node"+\
235
 
                            " in a viewtree")
236
 
        return toreturn
237
 
 
238
 
    #FIXME WTF?
239
 
    def get_root(self):
240
 
        return self.__maintree.get_root()
241
 
 
242
 
    #FIXME WTF?
243
 
    def refresh_all(self):
244
 
        self.__maintree.refresh_all()
245
 
 
246
 
    def get_current_state(self):
247
 
        """ Request current state to be send by signals/callbacks.
248
 
 
249
 
        This allow LibLarch widget to connect on fly (e.g. after FilteredTree
250
 
        is up and has some nodes). """
251
 
 
252
 
        if self.static:
253
 
            self.__maintree.refresh_all()
254
 
        else:
255
 
            self.__ft.get_current_state()
256
 
 
257
 
    def print_tree(self, string=None):
258
 
        """ Print the shown tree, i.e. MainTree or FilteredTree """
259
 
        return self._tree.print_tree(string)
260
 
 
261
 
    def get_all_nodes(self):
262
 
        """ Return list of node_id of displayed nodes """
263
 
        return self._tree.get_all_nodes()
264
 
 
265
 
    def get_n_nodes(self, withfilters=[], include_transparent=True):
266
 
        """ Returns quantity of displayed nodes in this tree
267
 
 
268
 
        @withfilters => Additional filters are applied before counting,
269
 
        i.e. the currently applied filters are also taken into account
270
 
 
271
 
        @inclde_transparent => if it is False, filters which don't have
272
 
        the transparent parameters are skipped, not takend into account
273
 
        """
274
 
 
275
 
        if not self.__ft:
276
 
            self.__ft = FilteredTree(self.__maintree, self.__fbank, refresh = True)
277
 
        return self.__ft.get_n_nodes(withfilters=withfilters,\
278
 
                                    include_transparent=include_transparent)
279
 
 
280
 
    # FIXME WTF? do wee need it?
281
 
    def get_node_for_path(self, path):
282
 
        """ Convert path to node_id.
283
 
 
284
 
        I am not sure what this is for... """
285
 
        return self._tree.get_node_for_path(path)
286
 
 
287
 
    # FIXME WTF? do wee need it?
288
 
    def get_paths_for_node(self, node_id=None):
289
 
        """ If node_id is none, return root path
290
 
 
291
 
        *Almost* reverse function to get_node_for_path
292
 
        (1 node can have many paths, 1:M)
293
 
        """
294
 
        return self._tree.get_paths_for_node(node_id)
295
 
 
296
 
    # FIXME WTF? do wee need it?
297
 
    # FIXME change pid => parent_id
298
 
    def next_node(self, node_id, pid=None):
299
 
        """ Return the next node to node_id.
300
 
 
301
 
        @parent_id => identify which instance of node_id to work.
302
 
        If None, random instance is used """
303
 
 
304
 
        return self._tree.next_node(node_id, pid)
305
 
        
306
 
    def node_has_child(self, node_id):
307
 
        """ FIXME desc """
308
 
        # FIXME use the same interface
309
 
 
310
 
        if self.static:
311
 
            toreturn = self.__maintree.get_node(node_id).has_child()
312
 
        else:
313
 
            toreturn = self.__ft.node_has_child(node_id)
314
 
        return toreturn
315
 
 
316
 
    def node_all_children(self, node_id=None):
317
 
        """ FIXME desc """
318
 
        # FIXME use the same interface
319
 
        # FIXME name? returns really all children? recursive?
320
 
        if self.static:
321
 
            if not node_id or self.__maintree.has_node(node_id):
322
 
                toreturn = self.__maintree.get_node(node_id).get_children()
323
 
            else:
324
 
                toreturn = []
325
 
        else:
326
 
            toreturn = self._tree.node_all_children(node_id)
327
 
        return toreturn
328
 
 
329
 
    def node_n_children(self, node_id=None, recursive=False):
330
 
        """ Return quantity of children of node_id.
331
 
        If node_id is None, use the root node. 
332
 
        Every instance of node has the same children"""
333
 
        if not self.__ft:
334
 
            self.__ft = FilteredTree(self.__maintree, self.__fbank, refresh = True)
335
 
        return self.__ft.node_n_children(node_id,recursive)
336
 
 
337
 
    def node_nth_child(self, node_id, n):
338
 
        """ Return nth child of the node. """
339
 
        # FIXME the same interface
340
 
 
341
 
        toreturn = None
342
 
        if self.static:
343
 
            # FIXME  Shouldnt be solved in MainTree?
344
 
            if not node_id or node_id == 'root':
345
 
                node = self.__maintree.get_root()
346
 
            else:
347
 
                node = self.__maintree.get_node(node_id)
348
 
 
349
 
            if node and node.get_n_children() > n:
350
 
                toreturn = node.get_nth_child(n)
351
 
            else:
352
 
                raise ValueError("node %s has less than %s nodes" %(node_id, n))
353
 
        else:
354
 
            # FIXME  Shouldnt be solved in FilteredTree?
355
 
            realn = self.__ft.node_n_children(node_id)
356
 
            if realn <= n:
357
 
                raise ValueError("viewtree has %s nodes, no node %s" %(realn, n))
358
 
            toreturn = self.__ft.node_nth_child(node_id, n)
359
 
        return toreturn
360
 
        
361
 
    def node_has_parent(self, node_id):
362
 
        """ Has node parents? Is it child of root? """
363
 
        return len(self.node_parents(node_id)) > 0
364
 
 
365
 
    def node_parents(self, node_id):
366
 
        """ Returns displayed parents of the given node, or [] if there is no 
367
 
        parent (such as if the node is a child of the virtual root),
368
 
        or if the parent is not displayable.
369
 
        Doesn't check wheter node node_id is displayed or not. (we only care about
370
 
        parents)
371
 
        """
372
 
        # FIXME the same interface
373
 
        if self.static:
374
 
            toreturn = self.__maintree.get_node(node_id).get_parents()
375
 
        else:
376
 
            toreturn = self.__ft.node_parents(node_id)
377
 
        return toreturn
378
 
 
379
 
    def is_displayed(self, node_id):
380
 
        """ Is the node displayed? """
381
 
# FIXME is node 
382
 
        if self.static:
383
 
            return self.__maintree.has_node(node_id)
384
 
        else:
385
 
            return self.__ft.is_displayed(node_id)
386
 
 
387
 
####### FILTERS ###############################################################
388
 
    def list_applied_filters(self):
389
 
        return self.__ft.list_applied_filters()
390
 
        
391
 
    def apply_filter(self, filter_name, parameters=None, \
392
 
                     reset=False, refresh=True):
393
 
        """ Applies a new filter to the tree.
394
 
 
395
 
        @param filter_name: The name of an already registered filter to apply
396
 
        @param parameters: Optional parameters to pass to the filter
397
 
        @param reset : optional boolean. Should we remove other filters?
398
 
        @param refresh : should we refresh after applying this filter ?
399
 
        """
400
 
        if self.static:
401
 
            raise Exception("WARNING: filters cannot be applied" + \
402
 
                            "to a static tree\n")
403
 
 
404
 
        self.__ft.apply_filter(filter_name, parameters, reset, refresh)
405
 
 
406
 
    def unapply_filter(self,filter_name,refresh=True):
407
 
        """ Removes a filter from the tree.
408
 
 
409
 
        @param filter_name: The name of filter to remove
410
 
        """
411
 
        if self.static:
412
 
            raise Exception("WARNING: filters cannot be unapplied" +\
413
 
                            "from a static tree\n")
414
 
        
415
 
        self.__ft.unapply_filter(filter_name, refresh)
416
 
 
417
 
    def reset_filters(self, refresh=True, transparent_only=False):
418
 
        """ Remove all filters currently set on the tree. """
419
 
        if self.static:
420
 
            raise Exception("WARNING: filters cannot be reset" +\
421
 
                            "on a static tree\n")
422
 
        else:
423
 
             self.__ft.reset_filters(refresh, transparent_only)