Added "Add child" context menu option, more fixes on tree view manipulation...
[straw.git] / straw / model / __init__.py
blob31b53a58e24fea4d7948428e0d68c706eba600ac
1 from bisect import insort
2 from gobject import GObject
3 from straw.error import debug
4 import gobject
5 import straw
7 class AsyncGObject(gobject.GObject):
8 def __init__(self):
9 gobject.GObject.__init__(self)
11 def emit(self, *args):
12 gobject.idle_add(gobject.GObject.emit, self, *args)
14 class Node(gobject.GObject):
15 """
16 Represents a base of single node in the model tree. This class is meant
17 for subclassing only, Node instances should not be used.
18 """
20 persistent_properties = [ "id", "parent_id", "type", "norder" ]
21 persistent_table = "nodes"
22 primary_key = "id"
24 __gproperties__ = {
25 "norder": (int, "parent_id", "norder", 0, 999999, 0, gobject.PARAM_READWRITE),
26 "parent": (gobject.TYPE_PYOBJECT, "parent", "parent", gobject.PARAM_READWRITE),
27 "unread-count": (int, "unread-count", "unread-count", 0, 999999, 0, gobject.PARAM_READWRITE)
30 __gsignals__ = {
31 "parent-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
32 "norder-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
33 "unread-count-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
36 def __init__(self):
37 GObject.__init__(self)
38 self.id = None
39 self.parent_id = None
40 self.unread_count = 0
41 self.norder = 0
42 self.children = []
43 self.handler_ids_unread_changed = {}
45 def __cmp__(self, other):
46 if other == None:
47 return -1
48 elif self.norder == other.norder:
49 return 0
50 elif self.norder < other.norder:
51 return -1
52 else:
53 return 1
55 def do_get_property(self, property):
56 if property.name == "norder":
57 return self.norder
58 elif property.name == "parent_id":
59 return self.parent_id
60 elif property.name == "unread-count":
61 return self.unread_count
63 def do_set_property(self, property, value):
64 if property.name == "unread-count":
65 if value == None:
66 value = 0
68 if self.unread_count != value:
69 delta = value - self.unread_count
70 old_value = self.unread_count
71 self.unread_count = value
72 self.emit("unread-count-changed", old_value, delta)
73 elif property.name == "norder":
74 if self.norder != value:
75 old_value = self.norder
76 self.norder = value
77 self.emit("norder-changed", old_value)
78 elif property.name == "parent":
79 if self.parent.id != value.id:
80 old_value = self.parent
81 self.parent = value
82 self.parent_id = value.id
83 self.emit("parent-changed", old_value)
85 def add_child(self, node, norder = None, allow_append = True, emit_changes = True):
86 debug("adding %d to %d with node.norder = %d, norder = %s" % (node.id, self.id, node.norder, str(norder)))
87 if allow_append:
88 if norder == None or norder >= len(self.children):
89 norder = len(self.children)
90 else:
91 if node.parent == self:
92 if norder > len(self.children):
93 norder = len(self.children)#raise AttributeError
95 if norder == None:
96 norder = len(self.children) - 1
97 else:
98 if norder == None:
99 norder = len(self.children)
100 elif norder > len(self.children):
101 norder = len(self.children)
103 if norder < len(self.children):
104 for child in self.children[norder:]:
105 child.props.norder += 1
107 debug("calculated norder = %d" % (norder))
109 node.props.norder = norder
111 self.append_child(node, emit_changes = emit_changes)
113 def append_child(self, node, emit_changes = True):
114 debug("appending %d to %d with norder = %d" % (node.id, self.id, node.norder))
115 insort(self.children, node)
116 self.handler_ids_unread_changed[node.id] = node.connect("unread-count-changed", self.on_unread_count_changed)
118 if emit_changes:
119 self.props.unread_count += node.unread_count
121 def remove_child(self, node, emit_changes = True):
122 debug("removing %d from %d with norder = %d" % (node.id, self.id, node.norder))
123 for child in self.children[(node.norder + 1):]:
124 child.props.norder -= 1
126 self.children.remove(node)
128 node.disconnect(self.handler_ids_unread_changed[node.id])
130 if emit_changes:
131 self.props.unread_count -= node.unread_count
133 def move(self, new_parent, new_norder = None):
134 debug("moving %d to %d (new_norder = %d)" % (self.id, new_parent.id, new_norder))
135 old_parent = self.parent
136 self.parent.remove_child(self, emit_changes = False)
137 new_parent.add_child(self, norder = new_norder, allow_append = False, emit_changes = False)
138 self.props.parent = new_parent
139 debug("changing unread count (old_parent %d: %d -> %d)" % (old_parent.id, old_parent.unread_count, old_parent.unread_count - self.unread_count))
140 debug("changing unread count (new_parent %d: %d -> %d)" % (new_parent.id, new_parent.unread_count, new_parent.unread_count + self.unread_count))
141 old_parent.props.unread_count -= self.unread_count
142 self.parent.props.unread_count += self.unread_count
144 def get_by_path(self, path):
145 print "sss %d | %s | %s" % (self.id, str(path), str(len(self.children)))
146 if len(path) == 0:
147 return self
148 elif path[0] < len(self.children):
149 return self.children[path[0]].get_by_path(path[1:])
150 else:
151 return None
153 def on_unread_count_changed(self, obj, old_value, delta):
154 pass
156 class Category(Node):
158 Represents a category - a node that can have child nodes.
161 persistent_properties = [ "id", "name" ]
162 persistent_table = "categories"
163 primary_key = None
164 inheritance = True
166 def __init__(self):
167 GObject.__init__(self)
168 Node.__gobject_init__(self)
169 Node.__init__(self)
171 self.id = None
172 self.name = ""
173 self.type = "C"
175 def do_get_property(self, property):
176 if property.name == "title":
177 return self.title
178 else:
179 return Node.do_get_property(self, property)
181 def do_set_property(self, property, value):
182 if property.name == "title":
183 self.title = value
184 else:
185 Node.do_set_property(self, property, value)
187 def on_unread_count_changed(self, obj, old_value, delta):
188 self.props.unread_count += delta
190 class Feed(Node):
192 Represents a feed - a node that can contain items. Feeds cannot have
193 child nodes.
196 persistent_properties = [ "id", "title", "location", "link" ]
197 persistent_table = "feeds"
198 primary_key = None
199 inheritance = True
201 __gproperties__ = {
202 "title": (str, "title", "title", "", gobject.PARAM_READWRITE),
203 "location": (str, "location", "location", "", gobject.PARAM_READWRITE),
204 "status": (int, "status", "status", 0, 9999, 0, gobject.PARAM_READWRITE),
207 def __init__(self):
208 GObject.__init__(self)
209 Node.__gobject_init__(self)
210 Node.__init__(self)
212 self.id = None
213 self.type = "F"
214 self.items = []
216 def add_child(self, node, norder = None, allow_append = True):
217 debug("tried to add child to a feed %d!" % self.id)
219 def do_get_property(self, property):
220 if property.name == "title":
221 return self.title
222 elif property.name == "location":
223 return self.title
224 elif property.name == "status":
225 return self.status
226 else:
227 return Node.do_get_property(self, property)
229 def do_set_property(self, property, value):
230 if property.name == "title":
231 self.title = value
232 elif property.name == "location":
233 self.location = value
234 elif property.name == "status":
235 self.status = value
236 else:
237 Node.do_set_property(self, property, value)
239 def on_unread_count_changed(self, obj, old_value, delta):
240 pass
242 def add_item(self, item):
243 self.items.append(item)
245 def on_is_read_changed(self, obj, is_read):
246 if is_read:
247 self.props.unread_count -= 1
248 else:
249 self.props.unread_count += 1
251 class Item(GObject):
252 persistent_properties = [ "id", "title", "feed_id", "is_read", "link", "pub_date", "description" ]
253 persistent_table = "items"
254 primary_key = "id"
256 __gproperties__ = {
257 "title": (str, "title", "title", "", gobject.PARAM_READWRITE),
258 "location": (str, "location", "location", "", gobject.PARAM_READWRITE),
259 "is-read": (gobject.TYPE_BOOLEAN, "is-read", "is-read", False, gobject.PARAM_READWRITE)
262 __gsignals__ = {
263 "is-read-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
266 def __init__(self):
267 GObject.__init__(self)
268 self.id = None
269 self.title = ""
270 self.feed_id = None
271 self.is_read = False
273 self.link = "link"
274 self.pub_date = None
275 self.description = "description"
276 self.source = None
277 self.guid = "guid"
278 self.guidislink = False
280 self.publication_name = "publication_name"
281 self.publication_volume = "publication_volume"
282 self.publication_number = "publication_number"
283 self.publication_section = "publication_section"
284 self.publication_starting_page = "publication_starting_page"
286 self.fm_license = "fm_license"
287 self.fm_changes = "fm_changes"
289 self.creator = "creator"
291 self.contributors = []
292 self.enclosures = []
293 self.license_urls = []
295 def do_get_property(self, property):
296 if property.name == "title":
297 return self.title
298 elif property.name == "is-read":
299 return self.is_read
300 else:
301 raise AttributeError, "unknown property %s" % property.name
303 def do_set_property(self, property, value):
304 if property.name == "title":
305 self.title = value
306 elif property.name == "is-read":
307 old_value = bool(self.is_read)
308 self.is_read = value
309 if old_value != value:
310 self.emit("is-read-changed", int(value))
311 else:
312 raise AttributeError, "unknown property %s" % property.name