Status: | Merged |
---|---|
Approved by: | Patrick Farrell |
Approved revision: | 447 |
Merged at revision: | 424 |
Proposed branch: | lp:~spud/spud/slice-view |
Merge into: | lp:spud |
Diff against target: |
4544 lines (+2067/-1919) 13 files modified
diamond/diamond/attributewidget.py (+341/-0) diamond/diamond/choice.py (+73/-1) diamond/diamond/commentwidget.py (+146/-0) diamond/diamond/databuttonswidget.py (+47/-0) diamond/diamond/datatype.py (+75/-0) diamond/diamond/datawidget.py (+501/-0) diamond/diamond/descriptionwidget.py (+190/-0) diamond/diamond/interface.py (+200/-1687) diamond/diamond/mixedtree.py (+204/-0) diamond/diamond/schema.py (+1/-1) diamond/diamond/sliceview.py (+117/-0) diamond/diamond/tree.py (+164/-10) diamond/gui/gui.glade (+8/-220) |
To merge this branch: | bzr merge lp:~spud/spud/slice-view |
Related bugs: | |
Related blueprints: |
Diamond: Slice view of FLML file
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Patrick Farrell | Approve | ||
Review via email:
|
Commit message
Description of the change
Adds slice view.
There is one small issue with this merge. The main view will NOT update the view of the currently selected row if that row is changed in the slice view. If this is merged in that should be marked as a bug.
To post a comment you must log in.
lp:~spud/spud/slice-view
updated
- 442. By Fraser Waters
-
Added node paramater to choice _on_set_*
- 443. By Fraser Waters
-
Slice view is bigger and better aligned.
- 444. By Fraser Waters
-
No longer includes inactive nodes.
- 445. By Fraser Waters
-
Main view updates when slice view closed.
- 446. By Fraser Waters
-
Can't slice if nothing to slice on.
- 447. By Fraser Waters
-
If can't slice display message in statusbar.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'diamond/diamond/attributewidget.py' |
2 | --- diamond/diamond/attributewidget.py 1970-01-01 00:00:00 +0000 |
3 | +++ diamond/diamond/attributewidget.py 2011-07-25 13:56:22 +0000 |
4 | @@ -0,0 +1,341 @@ |
5 | +#!/usr/bin/env python |
6 | + |
7 | +# This file is part of Diamond. |
8 | +# |
9 | +# Diamond is free software: you can redistribute it and/or modify |
10 | +# it under the terms of the GNU General Public License as published by |
11 | +# the Free Software Foundation, either version 3 of the License, or |
12 | +# (at your option) any later version. |
13 | +# |
14 | +# Diamond is distributed in the hope that it will be useful, |
15 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | +# GNU General Public License for more details. |
18 | +# |
19 | +# You should have received a copy of the GNU General Public License |
20 | +# along with Diamond. If not, see <http://www.gnu.org/licenses/>. |
21 | + |
22 | +import gobject |
23 | +import gtk |
24 | + |
25 | +import datatype |
26 | +import dialogs |
27 | + |
28 | +class AttributeWidget(gtk.Frame): |
29 | + |
30 | + __gsignals__ = { "on-store" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), |
31 | + "update-name" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())} |
32 | + |
33 | + def __init__(self): |
34 | + gtk.Frame.__init__(self) |
35 | + |
36 | + scrolledWindow = gtk.ScrolledWindow() |
37 | + scrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
38 | + |
39 | + treeview = self.treeview = gtk.TreeView() |
40 | + |
41 | + model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) |
42 | + treeview.set_model(model) |
43 | + treeview.connect("motion-notify-event", self.treeview_mouse_over) |
44 | + |
45 | + key_renderer = gtk.CellRendererText() |
46 | + key_renderer.set_property("editable", False) |
47 | + |
48 | + column1 = gtk.TreeViewColumn("Name", key_renderer, text = 0) |
49 | + column1.set_cell_data_func(key_renderer, self.key_data_func) |
50 | + column1.set_property("min-width", 75) |
51 | + |
52 | + entry_renderer = gtk.CellRendererText() |
53 | + entry_renderer.connect("edited", self.entry_edited) |
54 | + entry_renderer.connect("editing-started", self.entry_edit_start) |
55 | + |
56 | + combo_renderer = gtk.CellRendererCombo() |
57 | + combo_renderer.set_property("text-column", 0) |
58 | + combo_renderer.connect("edited", self.combo_selected) |
59 | + combo_renderer.connect("editing-started", self.combo_edit_start) |
60 | + |
61 | + column2 = gtk.TreeViewColumn("Value", entry_renderer, text = 1) |
62 | + column2.pack_start(combo_renderer) |
63 | + column2.set_attributes(combo_renderer, text = 1) |
64 | + column2.set_cell_data_func(entry_renderer, self.entry_data_func) |
65 | + column2.set_cell_data_func(combo_renderer, self.combo_data_func) |
66 | + column2.set_property("expand", True) |
67 | + column2.set_property("min-width", 75) |
68 | + |
69 | + icon_renderer = gtk.CellRendererPixbuf() |
70 | + |
71 | + column3 = gtk.TreeViewColumn("", icon_renderer) |
72 | + column3.set_cell_data_func(icon_renderer, self.icon_data_func) |
73 | + |
74 | + treeview.append_column(column1) |
75 | + treeview.append_column(column2) |
76 | + treeview.append_column(column3) |
77 | + |
78 | + scrolledWindow.add(treeview) |
79 | + |
80 | + label = gtk.Label() |
81 | + label.set_markup("<b>Attributes</b>") |
82 | + |
83 | + self.set_label_widget(label) |
84 | + self.set_shadow_type(gtk.SHADOW_NONE) |
85 | + self.add(scrolledWindow) |
86 | + |
87 | + def update(self, node): |
88 | + |
89 | + self.treeview.get_model().clear() |
90 | + |
91 | + self.node = node |
92 | + |
93 | + if node is None or not node.attrs.keys(): |
94 | + self.set_property("visible", False) |
95 | + else: |
96 | + self.set_property("visible", True) |
97 | + |
98 | + for key in node.attrs.keys(): |
99 | + model = self.treeview.get_model() |
100 | + cell_model = gtk.ListStore(gobject.TYPE_STRING) |
101 | + |
102 | + iter = model.append() |
103 | + model.set_value(iter, 0, key) |
104 | + model.set_value(iter, 2, cell_model) |
105 | + |
106 | + if isinstance(node.attrs[key][0], tuple): |
107 | + if node.attrs[key][1] is None: |
108 | + if isinstance(node.attrs[key][0][0], tuple): |
109 | + model.set_value(iter, 1, "Select " + datatype.print_type(node.attrs[key][0][1]) + "...") |
110 | + else: |
111 | + model.set_value(iter, 1, "Select...") |
112 | + else: |
113 | + model.set_value(iter, 1, node.attrs[key][1]) |
114 | + |
115 | + if isinstance(node.attrs[key][0][0], tuple): |
116 | + opts = node.attrs[key][0][0] |
117 | + else: |
118 | + opts = node.attrs[key][0] |
119 | + |
120 | + for opt in opts: |
121 | + cell_iter = cell_model.append() |
122 | + cell_model.set_value(cell_iter, 0, opt) |
123 | + |
124 | + self.treeview.get_column(2).set_property("visible", True) |
125 | + elif node.attrs[key][0] is None: |
126 | + model.set_value(iter, 1, "No data") |
127 | + elif node.attrs[key][1] is None: |
128 | + model.set_value(iter, 1, datatype.print_type(node.attrs[key][0])) |
129 | + else: |
130 | + model.set_value(iter, 1, node.attrs[key][1]) |
131 | + |
132 | + self.treeview.queue_resize() |
133 | + |
134 | + return |
135 | + |
136 | + def treeview_mouse_over(self, widget, event): |
137 | + """ |
138 | + Called when the mouse moves over the attributes widget. Sets the |
139 | + appropriate attribute widget tooltip. |
140 | + """ |
141 | + |
142 | + path_info = self.treeview.get_path_at_pos(int(event.x), int(event.y)) |
143 | + if path_info is None: |
144 | + try: |
145 | + self.treeview.set_tooltip_text("") |
146 | + self.treeview.set_property("has-tooltip", False) |
147 | + except: |
148 | + pass |
149 | + return |
150 | + |
151 | + path = path_info[0] |
152 | + col = path_info[1] |
153 | + if col is not self.treeview.get_column(1): |
154 | + try: |
155 | + self.treeview.set_tooltip_text("") |
156 | + self.treeview.set_property("has-tooltip", False) |
157 | + except: |
158 | + pass |
159 | + return |
160 | + |
161 | + iter = self.treeview.get_model().get_iter(path) |
162 | + iter_key = self.treeview.get_model().get_value(iter, 0) |
163 | + |
164 | + return |
165 | + |
166 | + def key_data_func(self, col, cell_renderer, model, iter): |
167 | + """ |
168 | + Attribute name data function. Sets the cell renderer text colours. |
169 | + """ |
170 | + |
171 | + iter_key = model.get_value(iter, 0) |
172 | + |
173 | + if not self.node.active or self.node.attrs[iter_key][0] is None or self.node.attrs[iter_key][0] == "fixed": |
174 | + cell_renderer.set_property("foreground", "grey") |
175 | + elif self.node.attrs[iter_key][1] is None: |
176 | + cell_renderer.set_property("foreground", "blue") |
177 | + else: |
178 | + cell_renderer.set_property("foreground", "black") |
179 | + |
180 | + return |
181 | + |
182 | + def entry_data_func(self, col, cell_renderer, model, iter): |
183 | + """ |
184 | + Attribute text data function. Hides the renderer if a combo box is required, |
185 | + and sets colours and editability otherwise. |
186 | + """ |
187 | + |
188 | + iter_key = model.get_value(iter, 0) |
189 | + |
190 | + if not self.node.active or self.node.attrs[iter_key][0] is None or self.node.attrs[iter_key][0] == "fixed": |
191 | + cell_renderer.set_property("editable", False) |
192 | + cell_renderer.set_property("foreground", "grey") |
193 | + cell_renderer.set_property("visible", True) |
194 | + elif not isinstance(self.node.attrs[iter_key][0], tuple): |
195 | + cell_renderer.set_property("editable", True) |
196 | + cell_renderer.set_property("visible", True) |
197 | + if self.node.attrs[iter_key][1] is None: |
198 | + cell_renderer.set_property("foreground", "blue") |
199 | + else: |
200 | + cell_renderer.set_property("foreground", "black") |
201 | + else: |
202 | + cell_renderer.set_property("editable", False) |
203 | + cell_renderer.set_property("visible", False) |
204 | + |
205 | + return |
206 | + |
207 | + def combo_data_func(self, col, cell_renderer, model, iter): |
208 | + """ |
209 | + Attribute combo box data function. Hides the renderer if a combo box is not |
210 | + required, and sets the combo box options otherwise. Adds an entry if required. |
211 | + """ |
212 | + |
213 | + iter_key = model.get_value(iter, 0) |
214 | + |
215 | + if self.node.active and isinstance(self.node.attrs[iter_key][0], tuple): |
216 | + cell_renderer.set_property("editable", True) |
217 | + cell_renderer.set_property("visible", True) |
218 | + if isinstance(self.node.attrs[iter_key][0][0], tuple): |
219 | + cell_renderer.set_property("has-entry", True) |
220 | + else: |
221 | + cell_renderer.set_property("has-entry", False) |
222 | + if self.node.attrs[iter_key][1] is None: |
223 | + cell_renderer.set_property("foreground", "blue") |
224 | + else: |
225 | + cell_renderer.set_property("foreground", "black") |
226 | + else: |
227 | + cell_renderer.set_property("visible", False) |
228 | + cell_renderer.set_property("editable", False) |
229 | + cell_renderer.set_property("model", model.get_value(iter, 2)) |
230 | + |
231 | + return |
232 | + |
233 | + def icon_data_func(self, col, cell_renderer, model, iter): |
234 | + """ |
235 | + Attribute icon data function. Used to add downward pointing arrows for combo |
236 | + attributes, for consistency with the LHS. |
237 | + """ |
238 | + |
239 | + iter_key = model.get_value(iter, 0) |
240 | + |
241 | + if self.node.active and isinstance(self.node.attrs[iter_key][0], tuple): |
242 | + cell_renderer.set_property("stock-id", gtk.STOCK_GO_DOWN) |
243 | + else: |
244 | + cell_renderer.set_property("stock-id", None) |
245 | + |
246 | + return |
247 | + |
248 | + def entry_edit_start(self, cell_renderer, editable, path): |
249 | + """ |
250 | + Called when editing is started on an attribute text cell. Used to delete the |
251 | + printable_type placeholder. |
252 | + """ |
253 | + |
254 | + iter = self.treeview.get_model().get_iter(path) |
255 | + iter_key = self.treeview.get_model().get_value(iter, 0) |
256 | + |
257 | + if self.node.attrs[iter_key][1] is None: |
258 | + editable.set_text("") |
259 | + |
260 | + return |
261 | + |
262 | + def combo_edit_start(self, cell_renderer, editable, path): |
263 | + """ |
264 | + Called when editing is started on an attribute combo cell. Used to delete the |
265 | + select placeholder for mixed entry / combo attributes. |
266 | + """ |
267 | + |
268 | + iter = self.treeview.get_model().get_iter(path) |
269 | + iter_key = self.treeview.get_model().get_value(iter, 0) |
270 | + |
271 | + if isinstance(self.node.attrs[iter_key][0][0], tuple) and self.node.attrs[iter_key][1] is None: |
272 | + editable.child.set_text("") |
273 | + |
274 | + return |
275 | + |
276 | + def entry_edited(self, cell_renderer, path, new_text): |
277 | + """ |
278 | + Called when editing is finished on an attribute text cell. Updates data in the |
279 | + treestore. |
280 | + """ |
281 | + |
282 | + iter = self.treeview.get_model().get_iter(path) |
283 | + iter_key = self.treeview.get_model().get_value(iter, 0) |
284 | + |
285 | + if self.node.get_attr(iter_key) is None and new_text == "": |
286 | + return |
287 | + |
288 | + value_check = self.node.validity_check(self.node.attrs[iter_key][0], new_text) |
289 | + |
290 | + if value_check is not None and value_check != self.node.attrs[iter_key][1]: |
291 | + if iter_key == "name" and not self._name_check(value_check): |
292 | + return |
293 | + |
294 | + self.treeview.get_model().set_value(iter, 1, value_check) |
295 | + self.node.set_attr(iter_key, value_check) |
296 | + if iter_key == "name": |
297 | + self.emit("update-name") |
298 | + |
299 | + self.emit("on-store") |
300 | + |
301 | + return |
302 | + |
303 | + def combo_selected(self, cell_renderer, path, new_text): |
304 | + """ |
305 | + Called when an attribute combo box element is selected, or combo box entry |
306 | + element entry is edited. Updates data in the treestore. |
307 | + """ |
308 | + |
309 | + iter = self.treeview.get_model().get_iter(path) |
310 | + iter_key = self.treeview.get_model().get_value(iter, 0) |
311 | + |
312 | + if new_text is None: |
313 | + return |
314 | + |
315 | + if isinstance(self.node.attrs[iter_key][0][0], tuple) and new_text not in self.node.attrs[iter_key][0][0]: |
316 | + if self.node.get_attr(iter_key) is None and new_text == "": |
317 | + return |
318 | + |
319 | + new_text = self.node.validity_check(self.node.attrs[iter_key][0][1], new_text) |
320 | + if iter_key == "name" and not self._name_check(new_text): |
321 | + return False |
322 | + if new_text != self.selected_node.attrs[iter_key][1]: |
323 | + self.treeview.get_model().set_value(iter, 1, new_text) |
324 | + self.node.set_attr(iter_key, new_text) |
325 | + if iter_key == "name": |
326 | + self.emit("update-name") |
327 | + |
328 | + self.emit("on-store") |
329 | + |
330 | + return |
331 | + |
332 | + def _name_check(self, value): |
333 | + """ |
334 | + Check to see if the supplied data is a valid tree name. |
335 | + """ |
336 | + |
337 | + valid_chars = "_:[]1234567890qwertyuioplkjhgfdsazxcvbnmMNBVCXZASDFGHJKLPOIUYTREWQ" |
338 | + for char in value: |
339 | + if char not in valid_chars: |
340 | + dialogs.error(None, "Invalid value entered") |
341 | + return False |
342 | + |
343 | + return True |
344 | + |
345 | +gobject.type_register(AttributeWidget) |
346 | |
347 | === modified file 'diamond/diamond/choice.py' |
348 | --- diamond/diamond/choice.py 2009-08-07 14:17:55 +0000 |
349 | +++ diamond/diamond/choice.py 2011-07-25 13:56:22 +0000 |
350 | @@ -21,10 +21,19 @@ |
351 | import StringIO |
352 | from lxml import etree |
353 | |
354 | + |
355 | +import gobject |
356 | + |
357 | import tree |
358 | |
359 | -class Choice: |
360 | +class Choice(gobject.GObject): |
361 | + |
362 | + __gsignals__ = { "on-set-data" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (str,)), |
363 | + "on-set-attr" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (str, str))} |
364 | + |
365 | def __init__(self, l, cardinality=''): |
366 | + gobject.GObject.__init__(self) |
367 | + |
368 | self.l = l |
369 | if l == []: |
370 | raise Exception |
371 | @@ -33,6 +42,9 @@ |
372 | for choice in l: |
373 | assert choice.__class__ is tree.Tree |
374 | name = name + choice.name + ":" |
375 | + choice.connect("on-set-data", self._on_set_data) |
376 | + choice.connect("on-set-attr", self._on_set_attr) |
377 | + |
378 | name = name[:-1] |
379 | self.name = name |
380 | self.schemaname = name |
381 | @@ -40,6 +52,12 @@ |
382 | self.parent = None |
383 | self.set_default_active() |
384 | |
385 | + def _on_set_data(self, node, data): |
386 | + self.emit("on-set-data", data) |
387 | + |
388 | + def _on_set_attr(self, node, attr, value): |
389 | + self.emit("on-set-attr", attr, value) |
390 | + |
391 | def set_default_active(self): |
392 | self.active = True |
393 | if self.cardinality == '?' or self.cardinality == '*': |
394 | @@ -126,3 +144,57 @@ |
395 | |
396 | def choices(self): |
397 | return self.l |
398 | + |
399 | + def is_comment(self): |
400 | + return False |
401 | + |
402 | + def get_comment(self): |
403 | + return None |
404 | + |
405 | + def get_display_name(self): |
406 | + """ |
407 | + This is a fluidity hack, allowing the name displayed in the treeview on the |
408 | + left to be different to the element name. If it has an attribute name="xxx", |
409 | + element_tag (xxx) is displayed. |
410 | + """ |
411 | + |
412 | + return self.get_current_tree().get_display_name() |
413 | + |
414 | + def get_name(self): |
415 | + return self.get_current_tree().get_name() |
416 | + |
417 | + def get_children(self): |
418 | + return [self.get_current_tree()] |
419 | + |
420 | + def get_choices(self): |
421 | + return self.l |
422 | + |
423 | + def is_hidden(self): |
424 | + """ |
425 | + Tests whether the supplied choice should be hidden in view. |
426 | + """ |
427 | + return False |
428 | + |
429 | + def get_name_path(self, leaf = True): |
430 | + name = self.get_display_name() if leaf else self.get_name() |
431 | + |
432 | + if self.parent is None: |
433 | + return name |
434 | + else: |
435 | + |
436 | + pname = self.parent.get_name_path(False) |
437 | + |
438 | + if name is None: |
439 | + return pname |
440 | + elif pname is None: |
441 | + return name |
442 | + else: |
443 | + return pname + "/" + name |
444 | + |
445 | + def get_mixed_data(self): |
446 | + return self |
447 | + |
448 | + def is_sliceable(self): |
449 | + return self.get_current_tree().is_sliceable() |
450 | + |
451 | +gobject.type_register(Choice) |
452 | |
453 | === added file 'diamond/diamond/commentwidget.py' |
454 | --- diamond/diamond/commentwidget.py 1970-01-01 00:00:00 +0000 |
455 | +++ diamond/diamond/commentwidget.py 2011-07-25 13:56:22 +0000 |
456 | @@ -0,0 +1,146 @@ |
457 | +#!/usr/bin/env python |
458 | + |
459 | +# This file is part of Diamond. |
460 | +# |
461 | +# Diamond is free software: you can redistribute it and/or modify |
462 | +# it under the terms of the GNU General Public License as published by |
463 | +# the Free Software Foundation, either version 3 of the License, or |
464 | +# (at your option) any later version. |
465 | +# |
466 | +# Diamond is distributed in the hope that it will be useful, |
467 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
468 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
469 | +# GNU General Public License for more details. |
470 | +# |
471 | +# You should have received a copy of the GNU General Public License |
472 | +# along with Diamond. If not, see <http://www.gnu.org/licenses/>. |
473 | + |
474 | +import gobject |
475 | +import gtk |
476 | + |
477 | +class CommentWidget(gtk.Frame): |
478 | + |
479 | + __gsignals__ = { "on-store" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())} |
480 | + |
481 | + def __init__(self): |
482 | + gtk.Frame.__init__(self) |
483 | + |
484 | + scrolledWindow = gtk.ScrolledWindow() |
485 | + scrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
486 | + |
487 | + textView = self.textView = gtk.TextView() |
488 | + textView.set_editable(False) |
489 | + textView.set_wrap_mode(gtk.WRAP_WORD) |
490 | + textView.set_cursor_visible(False) |
491 | + textView.connect("focus-in-event", self.focus_in) |
492 | + textView.connect("focus-out-event", self.focus_out) |
493 | + textView.get_buffer().create_tag("tag") |
494 | + |
495 | + scrolledWindow.add(textView) |
496 | + |
497 | + label = gtk.Label() |
498 | + label.set_markup("<b>Comment</b>") |
499 | + |
500 | + self.set_shadow_type(gtk.SHADOW_NONE) |
501 | + self.set_label_widget(label) |
502 | + self.add(scrolledWindow) |
503 | + |
504 | + self.comment_tree = None |
505 | + return |
506 | + |
507 | + def update(self, node): |
508 | + """ |
509 | + Update the widget with the given node |
510 | + """ |
511 | + |
512 | + #before updateing store the old |
513 | + self.store() |
514 | + |
515 | + if node is None or not node.active: |
516 | + self.textView.get_buffer().set_text("") |
517 | + self.textView.set_cursor_visible(False) |
518 | + self.textView.set_editable(False) |
519 | + try: |
520 | + self.textView.set_tooltip_text("") |
521 | + self.textView.set_property("has-tooltip", False) |
522 | + except: |
523 | + pass |
524 | + |
525 | + return |
526 | + |
527 | + self.comment_tree = comment_tree = node.get_comment() |
528 | + text_tag = self.textView.get_buffer().get_tag_table().lookup("tag") |
529 | + if comment_tree is None: |
530 | + self.textView.get_buffer().set_text("No comment") |
531 | + self.textView.set_cursor_visible(False) |
532 | + self.textView.set_editable(False) |
533 | + text_tag.set_property("foreground", "grey") |
534 | + try: |
535 | + self.textView.set_tooltip_text("") |
536 | + self.textView.set_property("has-tooltip", False) |
537 | + except: |
538 | + pass |
539 | + else: |
540 | + if comment_tree.data is None: |
541 | + self.textView.get_buffer().set_text("(string)") |
542 | + else: |
543 | + self.textView.get_buffer().set_text(comment_tree.data) |
544 | + if node.active: |
545 | + self.textView.set_cursor_visible(True) |
546 | + self.textView.set_editable(True) |
547 | + text_tag.set_property("foreground", "black") |
548 | + else: |
549 | + self.textView.set_cursor_visible(False) |
550 | + self.textView.set_editable(False) |
551 | + text_tag.set_property("foreground", "grey") |
552 | + |
553 | + buffer_bounds = self.textView.get_buffer().get_bounds() |
554 | + self.textView.get_buffer().apply_tag(text_tag, buffer_bounds[0], buffer_bounds[1]) |
555 | + |
556 | + self.interacted = False |
557 | + |
558 | + return |
559 | + |
560 | + def store(self): |
561 | + """ |
562 | + Store data in the node comment. |
563 | + """ |
564 | + |
565 | + comment_tree = self.comment_tree |
566 | + if comment_tree is None or not self.interacted: |
567 | + return |
568 | + |
569 | + data_buffer_bounds = self.textView.get_buffer().get_bounds() |
570 | + new_comment = self.textView.get_buffer().get_text(data_buffer_bounds[0], data_buffer_bounds[1]) |
571 | + |
572 | + if new_comment != comment_tree.data: |
573 | + if new_comment == "": |
574 | + comment_tree.data = None |
575 | + comment_tree.active = False |
576 | + else: |
577 | + comment_tree.set_data(new_comment) |
578 | + comment_tree.active = True |
579 | + self.emit("on-store") |
580 | + return |
581 | + |
582 | + def focus_in(self, widget, event): |
583 | + """ |
584 | + Called when the comment widget gains focus. Removes the printable_type |
585 | + placeholder. |
586 | + """ |
587 | + |
588 | + comment_tree = self.comment_tree |
589 | + if not comment_tree is None and not self.interacted: |
590 | + self.interacted = True |
591 | + if comment_tree.data is None: |
592 | + self.textView.get_buffer().set_text("") |
593 | + |
594 | + return |
595 | + |
596 | + def focus_out(self, widget, event): |
597 | + """" |
598 | + Called when the comment widget loses focus. Stores the comment. |
599 | + """ |
600 | + self.store() |
601 | + |
602 | +gobject.type_register(CommentWidget) |
603 | |
604 | === added file 'diamond/diamond/databuttonswidget.py' |
605 | --- diamond/diamond/databuttonswidget.py 1970-01-01 00:00:00 +0000 |
606 | +++ diamond/diamond/databuttonswidget.py 2011-07-25 13:56:22 +0000 |
607 | @@ -0,0 +1,47 @@ |
608 | +#!/usr/bin/env python |
609 | + |
610 | +# This file is part of Diamond. |
611 | +# |
612 | +# Diamond is free software: you can redistribute it and/or modify |
613 | +# it under the terms of the GNU General Public License as published by |
614 | +# the Free Software Foundation, either version 3 of the License, or |
615 | +# (at your option) any later version. |
616 | +# |
617 | +# Diamond is distributed in the hope that it will be useful, |
618 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
619 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
620 | +# GNU General Public License for more details. |
621 | +# |
622 | +# You should have received a copy of the GNU General Public License |
623 | +# along with Diamond. If not, see <http://www.gnu.org/licenses/>. |
624 | + |
625 | +import gobject |
626 | +import gtk |
627 | + |
628 | +class DataButtonsWidget(gtk.HBox): |
629 | + |
630 | + __gsignals__ = { "revert" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), |
631 | + "store" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())} |
632 | + |
633 | + def __init__(self): |
634 | + gtk.HBox.__gobject_init__(self) |
635 | + revertButton = gtk.Button() |
636 | + revertButton.set_label("Revert data") |
637 | + revertButton.connect("clicked", self._revert) |
638 | + |
639 | + storeButton = gtk.Button() |
640 | + storeButton.set_label("Store data") |
641 | + storeButton.connect("clicked", self._store) |
642 | + |
643 | + self.pack_end(revertButton) |
644 | + self.pack_start(storeButton) |
645 | + |
646 | + return |
647 | + |
648 | + def _revert(self, widget = None): |
649 | + self.emit("revert") |
650 | + |
651 | + def _store(self, widget = None): |
652 | + self.emit("store") |
653 | + |
654 | +gobject.type_register(DataButtonsWidget) |
655 | |
656 | === added file 'diamond/diamond/datatype.py' |
657 | --- diamond/diamond/datatype.py 1970-01-01 00:00:00 +0000 |
658 | +++ diamond/diamond/datatype.py 2011-07-25 13:56:22 +0000 |
659 | @@ -0,0 +1,75 @@ |
660 | +#!/usr/bin/env python |
661 | + |
662 | +# This file is part of Diamond. |
663 | +# |
664 | +# Diamond is free software: you can redistribute it and/or modify |
665 | +# it under the terms of the GNU General Public License as published by |
666 | +# the Free Software Foundation, either version 3 of the License, or |
667 | +# (at your option) any later version. |
668 | +# |
669 | +# Diamond is distributed in the hope that it will be useful, |
670 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
671 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
672 | +# GNU General Public License for more details. |
673 | +# |
674 | +# You should have received a copy of the GNU General Public License |
675 | +# along with Diamond. If not, see <http://www.gnu.org/licenses/>. |
676 | + |
677 | +import plist |
678 | + |
679 | +def print_type(datatype, bracket = True): |
680 | + """ |
681 | + Create a string to be displayed in place of empty data / attributes. |
682 | + """ |
683 | + |
684 | + def familiar_type(type_as_printable): |
685 | + """ |
686 | + Convert some type names to more familiar equivalents. |
687 | + """ |
688 | + |
689 | + if type_as_printable == "decim": |
690 | + return "float" |
691 | + elif type_as_printable == "int": |
692 | + return "integer" |
693 | + elif type_as_printable == "str": |
694 | + return "string" |
695 | + else: |
696 | + return type_as_printable |
697 | + |
698 | + def type_name(datatype): |
699 | + """ |
700 | + Return a human readable version of datatype. |
701 | + """ |
702 | + |
703 | + datatype_string = str(datatype) |
704 | + |
705 | + if datatype_string[:7] == "<type '" and datatype_string[len(datatype_string) - 2:] == "'>": |
706 | + value_type_split = datatype_string.split("'") |
707 | + return familiar_type(value_type_split[1]) |
708 | + |
709 | + value_type_split1 = datatype_string.split(".") |
710 | + value_type_split2 = value_type_split1[len(value_type_split1) - 1].split(" ") |
711 | + if len(value_type_split2) == 1: |
712 | + return familiar_type(value_type_split2[0][0:len(value_type_split2[0]) - 6]) |
713 | + else: |
714 | + return familiar_type(value_type_split2[0]) |
715 | + |
716 | + #print_type |
717 | + |
718 | + if isinstance(datatype, plist.List): |
719 | + if (isinstance(datatype.cardinality, int) and datatype.cardinality == 1) or datatype.cardinality == "": |
720 | + type_as_printable = type_name(datatype.datatype).lower() |
721 | + else: |
722 | + type_as_printable = type_name(datatype).lower() + " of " |
723 | + list_type_as_printable = type_name(datatype.datatype).lower() |
724 | + if isinstance(datatype.cardinality, int): |
725 | + type_as_printable += str(datatype.cardinality) + " " + list_type_as_printable + "s" |
726 | + else: |
727 | + type_as_printable += list_type_as_printable + "s" |
728 | + else: |
729 | + type_as_printable = type_name(datatype).lower() |
730 | + |
731 | + if bracket: |
732 | + type_as_printable = "(" + type_as_printable + ")" |
733 | + |
734 | + return type_as_printable |
735 | |
736 | === added file 'diamond/diamond/datawidget.py' |
737 | --- diamond/diamond/datawidget.py 1970-01-01 00:00:00 +0000 |
738 | +++ diamond/diamond/datawidget.py 2011-07-25 13:56:22 +0000 |
739 | @@ -0,0 +1,501 @@ |
740 | +#!/usr/bin/env python |
741 | + |
742 | +# This file is part of Diamond. |
743 | +# |
744 | +# Diamond is free software: you can redistribute it and/or modify |
745 | +# it under the terms of the GNU General Public License as published by |
746 | +# the Free Software Foundation, either version 3 of the License, or |
747 | +# (at your option) any later version. |
748 | +# |
749 | +# Diamond is distributed in the hope that it will be useful, |
750 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
751 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
752 | +# GNU General Public License for more details. |
753 | +# |
754 | +# You should have received a copy of the GNU General Public License |
755 | +# along with Diamond. If not, see <http://www.gnu.org/licenses/>. |
756 | + |
757 | +import gobject |
758 | +import gtk |
759 | +import pango |
760 | + |
761 | +import dialogs |
762 | +import datatype |
763 | +import mixedtree |
764 | + |
765 | +class DataWidget(gtk.VBox): |
766 | + |
767 | + __gsignals__ = { "on-store" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())} |
768 | + |
769 | + def __init__(self): |
770 | + gtk.VBox.__init__(self) |
771 | + |
772 | + frame = self.frame = gtk.Frame() |
773 | + |
774 | + label = gtk.Label() |
775 | + label.set_markup("<b>Data</b>") |
776 | + |
777 | + frame.set_label_widget(label) |
778 | + frame.set_shadow_type(gtk.SHADOW_NONE) |
779 | + |
780 | + self.pack_start(frame) |
781 | + self.buttons = None |
782 | + return |
783 | + |
784 | + def set_buttons(self, buttons): |
785 | + self.buttons = buttons |
786 | + buttons.connect("revert", self.revert) |
787 | + buttons.connect("store", self.store) |
788 | + buttons.show_all() |
789 | + |
790 | + def update(self, node): |
791 | + |
792 | + self.node = node |
793 | + |
794 | + if not self.is_node_editable(): |
795 | + self.set_data_fixed() |
796 | + elif node.is_tensor(self.geometry_dim_tree): |
797 | + self.set_data_tensor() |
798 | + elif isinstance(node.datatype, tuple): |
799 | + self.set_data_combo() |
800 | + else: |
801 | + self.set_data_entry() |
802 | + |
803 | + return |
804 | + |
805 | + def revert(self, button = None): |
806 | + """ |
807 | + "Revert Data" button click signal handler. Reverts data in the data frame. |
808 | + """ |
809 | + |
810 | + self.update(self.node) |
811 | + |
812 | + return |
813 | + |
814 | + def store(self, button = None): |
815 | + """ |
816 | + "Store Data" button click signal handler. Stores data from the data frame |
817 | + in the treestore. |
818 | + """ |
819 | + |
820 | + if not self.is_node_editable(): |
821 | + return True |
822 | + elif self.node.is_tensor(self.geometry_dim_tree): |
823 | + return self.data_tensor_store() |
824 | + elif isinstance(self.node.datatype, tuple): |
825 | + return self.data_combo_store() |
826 | + else: |
827 | + return self.data_entry_store() |
828 | + |
829 | +# This would be nice, look at it later |
830 | +# if self.scherror.errlist_is_open(): |
831 | +# if self.scherror.errlist_type == 0: |
832 | +# self.scherror.on_validate_schematron() |
833 | +# else: |
834 | +# self.scherror.on_validate() |
835 | + |
836 | + def is_node_editable(self): |
837 | + return (self.node is not None |
838 | + and self.node.active |
839 | + and self.node.datatype is not None |
840 | + and self.node.datatype != "fixed" |
841 | + and (not self.node.is_tensor(self.geometry_dim_tree) or self.geometry_dim_tree.data is not None)) |
842 | + # not A or B == A implies B |
843 | + # A B T |
844 | + # 0 0 1 |
845 | + # 0 1 1 |
846 | + # 1 0 0 |
847 | + # 1 1 1 |
848 | + |
849 | + def add_scrolled_window(self): |
850 | + scrolledWindow = gtk.ScrolledWindow() |
851 | + scrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
852 | + self.frame.add(scrolledWindow) |
853 | + scrolledWindow.show() |
854 | + return scrolledWindow |
855 | + |
856 | + def add_text_view(self): |
857 | + scrolledWindow = self.add_scrolled_window() |
858 | + self.set_child_packing(self.frame, True, True, 0, gtk.PACK_START) |
859 | + |
860 | + try: |
861 | + import gtksourceview2 |
862 | + buf = gtksourceview2.Buffer() |
863 | + lang_manager = gtksourceview2.LanguageManager() |
864 | + buf.set_highlight_matching_brackets(True) |
865 | + if self.node is not None and self.node.is_python_code(): |
866 | + python = lang_manager.get_language("python") |
867 | + buf.set_language(python) |
868 | + buf.set_highlight_syntax(True) |
869 | + textview = gtksourceview2.View(buffer=buf) |
870 | + textview.set_auto_indent(True) |
871 | + textview.set_insert_spaces_instead_of_tabs(True) |
872 | + textview.set_tab_width(2) |
873 | + if self.node is not None and self.node.is_python_code(): |
874 | + textview.set_show_line_numbers(True) |
875 | + font_desc = pango.FontDescription("monospace") |
876 | + if font_desc: |
877 | + textview.modify_font(font_desc) |
878 | + except ImportError: |
879 | + textview = gtk.TextView() |
880 | + |
881 | + textview.set_pixels_above_lines(2) |
882 | + textview.set_pixels_below_lines(2) |
883 | + textview.set_wrap_mode(gtk.WRAP_WORD) |
884 | + textview.connect("focus-in-event", self.entry_focus_in) |
885 | + |
886 | + scrolledWindow.add(textview) |
887 | + textview.show() |
888 | + return textview |
889 | + |
890 | + def set_data_empty(self): |
891 | + """ |
892 | + Empty the data frame. |
893 | + """ |
894 | + |
895 | + if self.frame.child is not None: |
896 | + if isinstance(self.data, gtk.TextView): |
897 | + self.data.handler_block_by_func(self.entry_focus_in) |
898 | + elif isinstance(self.data, gtk.ComboBox): |
899 | + self.data.handler_block_by_func(self.combo_focus_child) |
900 | + |
901 | + self.frame.remove(self.frame.child) |
902 | + |
903 | + self.interacted = False |
904 | + |
905 | + return |
906 | + |
907 | + def set_data_fixed(self): |
908 | + """ |
909 | + Create a non-editable text view to show help or fixed data. |
910 | + """ |
911 | + |
912 | + self.set_data_empty() |
913 | + |
914 | + self.data = self.add_text_view() |
915 | + |
916 | + self.data.get_buffer().create_tag("tag") |
917 | + text_tag = self.data.get_buffer().get_tag_table().lookup("tag") |
918 | + |
919 | + self.data.set_cursor_visible(False) |
920 | + self.data.set_editable(False) |
921 | + self.buttons.hide() |
922 | + text_tag.set_property("foreground", "grey") |
923 | + |
924 | + if self.node is None: |
925 | + self.data.get_buffer().set_text("") |
926 | + elif not self.node.active: |
927 | + self.data.get_buffer().set_text("Inactive node") |
928 | + elif self.node.datatype is None: |
929 | + self.data.get_buffer().set_text("No data") |
930 | + elif self.node.is_tensor(self.geometry_dim_tree): |
931 | + self.data.get_buffer().set_text("Dimension not set") |
932 | + else: # self.node.datatype == "fixed": |
933 | + self.data.get_buffer().set_text(self.node.data) |
934 | + |
935 | + buffer_bounds = self.data.get_buffer().get_bounds() |
936 | + self.data.get_buffer().apply_tag(text_tag, buffer_bounds[0], buffer_bounds[1]) |
937 | + |
938 | + return |
939 | + |
940 | + def set_data_entry(self): |
941 | + """ |
942 | + Create a text view for data entry in the data frame. |
943 | + """ |
944 | + |
945 | + self.set_data_empty() |
946 | + |
947 | + self.data = self.add_text_view() |
948 | + |
949 | + self.data.get_buffer().create_tag("tag") |
950 | + text_tag = self.data.get_buffer().get_tag_table().lookup("tag") |
951 | + |
952 | + self.data.set_cursor_visible(True) |
953 | + self.data.set_editable(True) |
954 | + self.buttons.show() |
955 | + |
956 | + if self.node.data is None: |
957 | + self.data.get_buffer().set_text(datatype.print_type(self.node.datatype)) |
958 | + text_tag.set_property("foreground", "blue") |
959 | + else: |
960 | + self.data.get_buffer().set_text(self.node.data) |
961 | + |
962 | + buffer_bounds = self.data.get_buffer().get_bounds() |
963 | + self.data.get_buffer().apply_tag(text_tag, buffer_bounds[0], buffer_bounds[1]) |
964 | + |
965 | + return |
966 | + |
967 | + def set_data_tensor(self): |
968 | + """ |
969 | + Create a table container packed with appropriate widgets for tensor data entry |
970 | + in the node data frame. |
971 | + """ |
972 | + |
973 | + self.set_data_empty() |
974 | + |
975 | + scrolledWindow = self.add_scrolled_window() |
976 | + |
977 | + dim1, dim2 = self.node.tensor_shape(self.geometry_dim_tree) |
978 | + self.data = gtk.Table(dim1, dim2) |
979 | + scrolledWindow.add_with_viewport(self.data) |
980 | + scrolledWindow.child.set_property("shadow-type", gtk.SHADOW_NONE) |
981 | + |
982 | + self.set_child_packing(self.frame, True, True, 0, gtk.PACK_START) |
983 | + |
984 | + is_symmetric = self.node.is_symmetric_tensor(self.geometry_dim_tree) |
985 | + for i in range(dim1): |
986 | + for j in range(dim2): |
987 | + iindex = dim1 - i - 1 |
988 | + jindex = dim2 - j - 1 |
989 | + |
990 | + if not is_symmetric or i >= j: |
991 | + entry = gtk.Entry() |
992 | + entry.connect("focus-in-event", self.tensor_element_focus_in, jindex, iindex) |
993 | + self.data.attach(entry, jindex, jindex + 1, iindex, iindex + 1) |
994 | + |
995 | + if self.node.data is None: |
996 | + entry.set_text(datatype.print_type(self.node.datatype.datatype)) |
997 | + entry.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("blue")) |
998 | + else: |
999 | + entry.set_text(self.node.data.split(" ")[jindex + iindex * dim2]) |
1000 | + |
1001 | + self.interacted = [False for i in range(dim1 * dim2)] |
1002 | + self.show_all() |
1003 | + |
1004 | + return |
1005 | + |
1006 | + def set_data_combo(self): |
1007 | + """ |
1008 | + Create a combo box for node data selection in the node data frame. Add an |
1009 | + entry if required. |
1010 | + """ |
1011 | + |
1012 | + self.set_data_empty() |
1013 | + |
1014 | + if isinstance(self.node.datatype[0], tuple): |
1015 | + self.data = gtk.combo_box_entry_new_text() |
1016 | + else: |
1017 | + self.data = gtk.combo_box_new_text() |
1018 | + |
1019 | + self.frame.add(self.data) |
1020 | + self.data.show() |
1021 | + |
1022 | + self.data.connect("set-focus-child", self.combo_focus_child) |
1023 | + |
1024 | + self.set_child_packing(self.frame, False, False, 0, gtk.PACK_START) |
1025 | + |
1026 | + if isinstance(self.node.datatype[0], tuple): |
1027 | + self.buttons.show() |
1028 | + else: |
1029 | + self.buttons.hide() |
1030 | + |
1031 | + if self.node.data is None: |
1032 | + if isinstance(self.node.datatype[0], tuple): |
1033 | + self.data.child.set_text("Select " + datatype.print_type(self.node.datatype[1]) + "...") |
1034 | + else: |
1035 | + self.data.append_text("Select...") |
1036 | + self.data.set_active(0) |
1037 | + self.data.child.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("blue")) |
1038 | + self.data.child.modify_text(gtk.STATE_PRELIGHT, gtk.gdk.color_parse("blue")) |
1039 | + |
1040 | + if isinstance(self.node.datatype[0], tuple): |
1041 | + options = self.node.datatype[0] |
1042 | + else: |
1043 | + options = self.node.datatype |
1044 | + |
1045 | + for (i, opt) in enumerate(options): |
1046 | + self.data.append_text(opt) |
1047 | + if self.node.data == opt: |
1048 | + self.data.set_active(i) |
1049 | + |
1050 | + if (isinstance(self.node.datatype[0], tuple) |
1051 | + and self.node.data is not None |
1052 | + and self.node.data not in self.node.datatype[0]): |
1053 | + self.data.child.set_text(self.node.data) |
1054 | + |
1055 | + self.data.connect("changed", self.combo_changed) |
1056 | + |
1057 | + return |
1058 | + |
1059 | + def data_entry_store(self): |
1060 | + """ |
1061 | + Attempt to store data read from a textview packed in the data frame. |
1062 | + """ |
1063 | + |
1064 | + new_data = self.data.get_buffer().get_text(*self.data.get_buffer().get_bounds()) |
1065 | + |
1066 | + if new_data == "": |
1067 | + return True |
1068 | + |
1069 | + if self.node.data is None and not self.interacted: |
1070 | + return True |
1071 | + else: |
1072 | + value_check = self.node.validity_check(self.node.datatype, new_data) |
1073 | + if value_check is None: |
1074 | + dialogs.error(None, "Invalid value entered") |
1075 | + return False |
1076 | + elif value_check != self.node.data: |
1077 | + self.node.set_data(value_check) |
1078 | + if (isinstance(self.node, mixedtree.MixedTree) |
1079 | + and "shape" in self.node.child.attrs.keys() |
1080 | + and self.node.child.attrs["shape"][0] is int |
1081 | + and isinstance(self.node.datatype, plist.List) |
1082 | + and self.node.datatype.cardinality == "+"): |
1083 | + self.node.child.set_attr("shape", str(len(value_check.split(" ")))) |
1084 | + |
1085 | + self.emit("on-store") |
1086 | + self.interacted = False |
1087 | + |
1088 | + return True |
1089 | + |
1090 | + def data_tensor_store(self): |
1091 | + """ |
1092 | + Attempt to store data read from tensor data entry widgets packed in the |
1093 | + data frame. |
1094 | + """ |
1095 | + |
1096 | + dim1, dim2 = self.node.tensor_shape(self.geometry_dim_tree) |
1097 | + is_symmetric = self.node.is_symmetric_tensor(self.geometry_dim_tree) |
1098 | + |
1099 | + if True not in self.interacted: |
1100 | + return True |
1101 | + |
1102 | + entry_values = [] |
1103 | + for i in range(dim1): |
1104 | + for j in range(dim2): |
1105 | + if is_symmetric and i > j: |
1106 | + entry_values.append(self.data.get_children()[i + j * dim1].get_text()) |
1107 | + else: |
1108 | + entry_values.append(self.data.get_children()[j + i * dim2].get_text()) |
1109 | + |
1110 | + changed = False |
1111 | + for i in range(dim1): |
1112 | + for j in range(dim2): |
1113 | + if (self.interacted[j + i * dim2] |
1114 | + and entry_values[j + i * dim2] != "" |
1115 | + and (self.node.data is None |
1116 | + or self.node.data.split(" ")[j + i * dim2] != entry_values[j + i * dim2])): |
1117 | + changed = True |
1118 | + if not changed: |
1119 | + return True |
1120 | + elif (self.node.data is None and False in self.interacted) or "" in entry_values: |
1121 | + dialogs.error(None, "Invalid value entered") |
1122 | + return False |
1123 | + |
1124 | + new_data = "" |
1125 | + for i in range(dim1): |
1126 | + for j in range(dim2): |
1127 | + new_data += " " + entry_values[j + i * dim2] |
1128 | + |
1129 | + value_check = self.node.validity_check(self.node.datatype, new_data) |
1130 | + if value_check is None: |
1131 | + return False |
1132 | + elif not value_check == self.node.data: |
1133 | + self.node.set_data(value_check) |
1134 | + |
1135 | + dim1, dim2 = self.node.tensor_shape(self.geometry_dim_tree) |
1136 | + if int(self.node.child.attrs["rank"][1]) == 1: |
1137 | + self.node.child.set_attr("shape", str(dim1)) |
1138 | + else: |
1139 | + self.node.child.set_attr("shape", str(dim1) + " " + str(dim2)) |
1140 | + |
1141 | + self.emit("on-store") |
1142 | + self.interacted = [False for i in range(dim1 * dim2)] |
1143 | + |
1144 | + return True |
1145 | + |
1146 | + def data_combo_store(self): |
1147 | + """ |
1148 | + Attempt to store data read from a combo box entry packed in the node data. |
1149 | + """ |
1150 | + |
1151 | + if not isinstance(self.node.datatype[0], tuple): |
1152 | + return True |
1153 | + |
1154 | + new_data = self.data.get_text() |
1155 | + |
1156 | + if self.node.data is None and not self.interacted: |
1157 | + return True |
1158 | + elif not new_data in self.node.datatype[0]: |
1159 | + new_data = self.node.validity_check(self.node.datatype[1], new_data) |
1160 | + if new_data is None: |
1161 | + return False |
1162 | + |
1163 | + if not new_data == self.node.data: |
1164 | + self.node.set_data(new_data) |
1165 | + self.emit("on-store") |
1166 | + self.interacted = False |
1167 | + |
1168 | + return True |
1169 | + |
1170 | + def entry_focus_in(self, widget, event): |
1171 | + """ |
1172 | + Called when a text view data entry widget gains focus. Used to delete the |
1173 | + printable_type placeholder. |
1174 | + """ |
1175 | + |
1176 | + if (self.node is not None |
1177 | + and self.node.datatype is not None |
1178 | + and not self.node.is_tensor(self.geometry_dim_tree) |
1179 | + and self.node.data is None |
1180 | + and not self.interacted): |
1181 | + self.data.get_buffer().set_text("") |
1182 | + |
1183 | + self.interacted = True |
1184 | + |
1185 | + return |
1186 | + |
1187 | + def tensor_element_focus_in(self, widget, event, row, col): |
1188 | + """ |
1189 | + Called when a tensor data entry widget gains focus. Used to delete the |
1190 | + printable_type placeholder. |
1191 | + """ |
1192 | + |
1193 | + dim1, dim2 = self.node.tensor_shape(self.geometry_dim_tree) |
1194 | + if not self.interacted[col + row * dim2]: |
1195 | + self.interacted[col + row * dim2] = True |
1196 | + if self.node.is_symmetric_tensor(self.geometry_dim_tree): |
1197 | + self.interacted[row + col * dim1] = True |
1198 | + if self.node.data is None: |
1199 | + widget.set_text("") |
1200 | + widget.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) |
1201 | + |
1202 | + return |
1203 | + |
1204 | + def combo_focus_child(self, container, widget): |
1205 | + """ |
1206 | + Called when a data selection widget gains focus. Used to delete the select |
1207 | + placeholder. |
1208 | + """ |
1209 | + if not self.interacted: |
1210 | + self.interacted = True |
1211 | + if self.node.data is None: |
1212 | + self.data.handler_block_by_func(self.combo_changed) |
1213 | + if isinstance(self.node.datatype[0], tuple): |
1214 | + self.data.child.set_text("") |
1215 | + else: |
1216 | + self.data.remove_text(0) |
1217 | + |
1218 | + self.data.child.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) |
1219 | + self.data.child.modify_text(gtk.STATE_PRELIGHT, gtk.gdk.color_parse("black")) |
1220 | + self.data.handler_unblock_by_func(self.combo_changed) |
1221 | + |
1222 | + return |
1223 | + |
1224 | + def combo_changed(self, combo_box): |
1225 | + """ |
1226 | + Called when a data combo box element is selected. Updates data in the |
1227 | + treestore. |
1228 | + """ |
1229 | + |
1230 | + if not isinstance(self.node.datatype[0], tuple): |
1231 | + text = self.data.get_active_text() |
1232 | + if text is None: |
1233 | + return |
1234 | + |
1235 | + self.node.set_data(text) |
1236 | + self.emit("on-store") |
1237 | + self.interacted = False |
1238 | + return |
1239 | + |
1240 | +gobject.type_register(DataWidget) |
1241 | |
1242 | === added file 'diamond/diamond/descriptionwidget.py' |
1243 | --- diamond/diamond/descriptionwidget.py 1970-01-01 00:00:00 +0000 |
1244 | +++ diamond/diamond/descriptionwidget.py 2011-07-25 13:56:22 +0000 |
1245 | @@ -0,0 +1,190 @@ |
1246 | +#!/usr/bin/env python |
1247 | + |
1248 | +# This file is part of Diamond. |
1249 | +# |
1250 | +# Diamond is free software: you can redistribute it and/or modify |
1251 | +# it under the terms of the GNU General Public License as published by |
1252 | +# the Free Software Foundation, either version 3 of the License, or |
1253 | +# (at your option) any later version. |
1254 | +# |
1255 | +# Diamond is distributed in the hope that it will be useful, |
1256 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1257 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1258 | +# GNU General Public License for more details. |
1259 | +# |
1260 | +# You should have received a copy of the GNU General Public License |
1261 | +# along with Diamond. If not, see <http://www.gnu.org/licenses/>. |
1262 | + |
1263 | +import gtk |
1264 | +import re |
1265 | +import TextBufferMarkup |
1266 | +import webbrowser |
1267 | + |
1268 | +class DescriptionWidget(gtk.Frame): |
1269 | + def __init__(self): |
1270 | + gtk.Frame.__init__(self) |
1271 | + |
1272 | + scrolledWindow = gtk.ScrolledWindow() |
1273 | + scrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
1274 | + |
1275 | + textView = self.textView = gtk.TextView() |
1276 | + textView.set_editable(False) |
1277 | + textView.set_wrap_mode(gtk.WRAP_WORD) |
1278 | + textView.set_cursor_visible(False) |
1279 | + |
1280 | + textView.set_buffer(TextBufferMarkup.PangoBuffer()) |
1281 | + textView.connect("button-release-event", self.mouse_button_release) |
1282 | + textView.connect("motion-notify-event", self.mouse_over) |
1283 | + |
1284 | + scrolledWindow.add(textView) |
1285 | + |
1286 | + label = gtk.Label() |
1287 | + label.set_markup("<b>Description</b>") |
1288 | + |
1289 | + self.set_shadow_type(gtk.SHADOW_NONE) |
1290 | + self.set_label_widget(label) |
1291 | + self.add(scrolledWindow) |
1292 | + |
1293 | + return |
1294 | + |
1295 | + def update(self, node): |
1296 | + if node is None: |
1297 | + self.set_description("<span foreground=\"grey\">No node selected</span>") |
1298 | + elif node.doc is None: |
1299 | + self.set_description("<span foreground=\"red\">No documentation</span>") |
1300 | + else: |
1301 | + self.set_description(node.doc) |
1302 | + |
1303 | + return |
1304 | + |
1305 | + def set_description(self, text): |
1306 | + """ |
1307 | + Set the node description. |
1308 | + """ |
1309 | + |
1310 | + #text saved in self.text without markup |
1311 | + self.text = text = self.render_whitespace(text) |
1312 | + link_bounds = self.link_bounds = self.get_link_bounds(text) |
1313 | + |
1314 | + if link_bounds: |
1315 | + new_text = [] |
1316 | + index = 0 |
1317 | + for bounds in link_bounds: |
1318 | + new_text.append(text[index:bounds[0]]) |
1319 | + new_text.append("<span foreground=\"blue\" underline=\"single\">") |
1320 | + new_text.append(text[bounds[0]:bounds[1]]) |
1321 | + new_text.append("</span>") |
1322 | + index = bounds[1] |
1323 | + |
1324 | + new_text.append(text[index:]) |
1325 | + text = ''.join(new_text) |
1326 | + |
1327 | + self.textView.get_buffer().set_text(text) |
1328 | + |
1329 | + return |
1330 | + |
1331 | + def get_link_bounds(self, text): |
1332 | + """ |
1333 | + Return a list of tuples corresponding to the start and end points of links in |
1334 | + the supplied string. |
1335 | + """ |
1336 | + text = text.lower() |
1337 | + bounds = [] |
1338 | + |
1339 | + for match in re.finditer(r"\b(" #start at beginging of word |
1340 | + +r"(?:https?://|www\.)" #http:// https:// or www. |
1341 | + +r"(?:[a-z0-9][a-z0-9_\-]*[a-z0-9]\.)*" #Domains |
1342 | + +r"(?:[a-z][a-z0-9\-]*[a-z0-9])" #TLD |
1343 | + +r"(?:/([a-z0-9$_.+\\*'(),;:@&=\-]|%[0-9a-f]{2})*)*" #path |
1344 | + +r"(?:\?([a-z0-9$_.+!*'(),;:@&=\-]|%[0-9a-f]{2})*)?" #query |
1345 | + +r")", text): |
1346 | + bounds.append(match.span()) |
1347 | + |
1348 | + return bounds |
1349 | + |
1350 | + def render_whitespace(self, desc): |
1351 | + ''' Render the line wrapping in desc as follows: |
1352 | + |
1353 | + * Newlines followed by 0-1 spaces are ignored. |
1354 | + * Blank lines start new paragraphs. |
1355 | + * Newlines followed by more than 1 space are honoured. |
1356 | + ''' |
1357 | + |
1358 | + text = [] |
1359 | + para = False |
1360 | + literal = False |
1361 | + |
1362 | + for line in desc.split("\n"): |
1363 | + |
1364 | + if line == "" or line.isspace(): #Blank line, start paragraph |
1365 | + if literal: #if following a literal line add newlines |
1366 | + text.append("\n") |
1367 | + para = True |
1368 | + |
1369 | + elif line[0] == " " and line[1] == " ": # >1 space, treat literaly |
1370 | + text.append("\n") |
1371 | + text.append(line) |
1372 | + para = False |
1373 | + literal = True |
1374 | + |
1375 | + else: #normal case |
1376 | + if para: #add if starting a new paragraph |
1377 | + text.append("\n ") |
1378 | + text.append(line) |
1379 | + para = False |
1380 | + literal = False |
1381 | + |
1382 | + return ''.join(text) |
1383 | + |
1384 | + def get_hyperlink(self, x, y): |
1385 | + """ |
1386 | + Given an x and y window position (eg from a mouse click) return the hyperlink |
1387 | + at that position if there is one. Else return None. |
1388 | + """ |
1389 | + if self.text is None: |
1390 | + return None |
1391 | + |
1392 | + buffer_pos = self.textView.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, x, y) |
1393 | + char_offset = self.textView.get_iter_at_location(buffer_pos[0], buffer_pos[1]).get_offset() |
1394 | + |
1395 | + for bounds in self.link_bounds: |
1396 | + if char_offset >= bounds[0] and char_offset <= bounds[1]: |
1397 | + return self.text[bounds[0]:bounds[1]] |
1398 | + |
1399 | + return None |
1400 | + |
1401 | + def mouse_over(self, widget, event): |
1402 | + """ |
1403 | + Called when the mouse moves over the node description widget. Sets the cursor |
1404 | + to a hand if the mouse hovers over a link. |
1405 | + |
1406 | + Based on code from HyperTextDemo class in hypertext.py from PyGTK 2.12 demos |
1407 | + """ |
1408 | + |
1409 | + if self.get_hyperlink(int(event.x), int(event.y)) is not None: |
1410 | + self.textView.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) |
1411 | + else: |
1412 | + self.textView.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM)) |
1413 | + |
1414 | + return |
1415 | + |
1416 | + def mouse_button_release(self, widget, event): |
1417 | + """ |
1418 | + Called when a mouse button is released over the node description widget. |
1419 | + Launches a browser if the mouse release was over a link, the left mouse button |
1420 | + was released and no text was selected. |
1421 | + |
1422 | + Based on code from HyperTextDemo class in hypertext.py from PyGTK 2.12 demos |
1423 | + """ |
1424 | + |
1425 | + if not event.button == 1: |
1426 | + return |
1427 | + |
1428 | + if self.textView.get_buffer().get_selection_bounds(): |
1429 | + return |
1430 | + |
1431 | + hyperlink = self.get_hyperlink(int(event.x), int(event.y)) |
1432 | + if hyperlink is not None: |
1433 | + webbrowser.open(hyperlink) |
1434 | + |
1435 | + return |
1436 | |
1437 | === modified file 'diamond/diamond/interface.py' |
1438 | --- diamond/diamond/interface.py 2011-07-20 17:19:44 +0000 |
1439 | +++ diamond/diamond/interface.py 2011-07-25 13:56:22 +0000 |
1440 | @@ -20,7 +20,6 @@ |
1441 | import re |
1442 | import sys |
1443 | import tempfile |
1444 | -import webbrowser |
1445 | import cStringIO as StringIO |
1446 | |
1447 | import pango |
1448 | @@ -28,18 +27,28 @@ |
1449 | import gtk |
1450 | import gtk.glade |
1451 | |
1452 | +import choice |
1453 | +import config |
1454 | +import datatype |
1455 | import debug |
1456 | import dialogs |
1457 | -import choice |
1458 | -import config |
1459 | +import mixedtree |
1460 | import plist |
1461 | +import plugins |
1462 | import schema |
1463 | import scherror |
1464 | import tree |
1465 | -import plugins |
1466 | + |
1467 | import StringIO |
1468 | import TextBufferMarkup |
1469 | |
1470 | +import attributewidget |
1471 | +import commentwidget |
1472 | +import descriptionwidget |
1473 | +import databuttonswidget |
1474 | +import datawidget |
1475 | +import sliceview |
1476 | + |
1477 | from lxml import etree |
1478 | |
1479 | try: |
1480 | @@ -60,14 +69,13 @@ |
1481 | logofile: the GUI logo file |
1482 | main_window: GUI toplevel window |
1483 | node_attrs: RHS attributes entry widget |
1484 | - node_comment: RHS comment entry widget |
1485 | - node_comment_interacted: used to determine if the comment widget has been interacted with without the comment being stored |
1486 | + description: RHS description widget |
1487 | + data = RHS data widget |
1488 | + comment: RHS comment entry widget |
1489 | node_data: RHS data entry widget |
1490 | node_data_buttons_hbox: container for "Revert Data" and "Store Data" buttons |
1491 | node_data_interacted: used to determine if a node data widget has been interacted with without data being stored |
1492 | node_data_frame: frame containing data entry widgets |
1493 | - node_desc: RHS description widget |
1494 | - node_desc_link_bounds: a list of tuples corresponding to the start and end points of links in the current tree.Tree / MixedTree documentation |
1495 | options_tree_select_func_enabled: boolean, true if the options tree select function is enabled (used to overcome a nasty clash with the treeview clicked signal) - re-enabled on next options_tree_select_func call |
1496 | selected_node: a tree.Tree or MixedTree containing data to be displayed on the RHS |
1497 | selected_iter: last iter set by on_select_row |
1498 | @@ -83,7 +91,6 @@ |
1499 | |
1500 | Important routines: |
1501 | cellcombo_edited: called when a choice is selected on the left-hand pane |
1502 | - init_options_frame: initialise the right-hand side |
1503 | init_treemodel: set up the treemodel and treeview |
1504 | on_treeview_clicked: when a row is clicked, process the consequences (e.g. activate inactive instance) |
1505 | set_treestore: stuff the treestore with a given tree.Tree |
1506 | @@ -105,6 +112,8 @@ |
1507 | self.find = DiamondFindDialog(self, gladefile) |
1508 | self.popup = self.gui.get_widget("popupmenu") |
1509 | |
1510 | + self.add_custom_widgets() |
1511 | + |
1512 | self.plugin_buttonbox = self.gui.get_widget("plugin_buttonbox") |
1513 | self.plugin_buttonbox.set_layout(gtk.BUTTONBOX_START) |
1514 | self.plugin_buttonbox.show() |
1515 | @@ -129,7 +138,8 @@ |
1516 | "on_about": self.on_about, |
1517 | "on_copy_spud_path": self.on_copy_spud_path, |
1518 | "on_copy": self.on_copy, |
1519 | - "on_paste": self.on_paste} |
1520 | + "on_paste": self.on_paste, |
1521 | + "on_slice": self.on_slice} |
1522 | self.gui.signal_autoconnect(signals) |
1523 | |
1524 | self.main_window = self.gui.get_widget("mainWindow") |
1525 | @@ -147,7 +157,7 @@ |
1526 | self.suffix = suffix |
1527 | |
1528 | self.selected_node = None |
1529 | - self.init_options_frame() |
1530 | + self.update_options_frame() |
1531 | |
1532 | self.file_path = os.getcwd() |
1533 | self.schemafile_path = os.getcwd() |
1534 | @@ -338,6 +348,7 @@ |
1535 | |
1536 | self.treeview.freeze_child_notify() |
1537 | self.treeview.set_model(None) |
1538 | + self.signals = {} |
1539 | self.set_treestore(None, [self.tree], True) |
1540 | self.treeview.set_model(self.treestore) |
1541 | self.treeview.thaw_child_notify() |
1542 | @@ -426,7 +437,7 @@ |
1543 | one. |
1544 | """ |
1545 | |
1546 | - self.node_data_store() |
1547 | + self.data.store() |
1548 | |
1549 | if self.filename is None: |
1550 | return self.on_save_as(widget) |
1551 | @@ -521,35 +532,10 @@ |
1552 | return |
1553 | |
1554 | def on_display_properties_toggled(self, widget=None): |
1555 | - self.options_frame.set_property("visible", not self.options_frame.get_property("visible")) |
1556 | + optionsFrame = self.gui.get_widget("optionsFrame") |
1557 | + optionsFrame.set_property("visible", not optionsFrame.get_property("visible")) |
1558 | return |
1559 | |
1560 | - def set_cell_node(self, iter): |
1561 | - node = self.get_painted_tree(iter) |
1562 | - |
1563 | - if isinstance(node, MixedTree): |
1564 | - node = node.child |
1565 | - |
1566 | - if node.data is not None: |
1567 | - self.data_renderer.set_property("foreground", "black") |
1568 | - self.treestore.set_value(iter, 4, node.data) |
1569 | - elif isinstance(node, tree.Tree) and not node.not_editable(): |
1570 | - self.data_renderer.set_property("foreground", "gray") |
1571 | - |
1572 | - datatype = "" |
1573 | - |
1574 | - if isinstance(node.datatype, plist.List): |
1575 | - datatype = "(" + node.datatype.datatype.__name__ + ")" |
1576 | - elif isinstance(node.datatype, tuple): |
1577 | - datatype = str(node.datatype) |
1578 | - else: |
1579 | - datatype = node.datatype.__name__ |
1580 | - |
1581 | - self.treestore.set_value(iter, 4, datatype) |
1582 | - |
1583 | - def expand_fill_data(self, model, path, iter, user_data): |
1584 | - return self.set_cell_node(iter) |
1585 | - |
1586 | def on_go_to_node(self, widget=None): |
1587 | """ |
1588 | Go to a node, identified by an XPath |
1589 | @@ -566,7 +552,6 @@ |
1590 | """ |
1591 | |
1592 | self.treeview.expand_all() |
1593 | - self.treestore.foreach(self.expand_fill_data, None) |
1594 | |
1595 | return |
1596 | |
1597 | @@ -624,8 +609,6 @@ |
1598 | pass |
1599 | |
1600 | about.set_logo(logo) |
1601 | - about.connect("destroy", dialogs.close_dialog) |
1602 | - about.connect("response", dialogs.close_dialog) |
1603 | about.show() |
1604 | |
1605 | return |
1606 | @@ -652,13 +635,13 @@ |
1607 | return self._get_focus_widget(focus) |
1608 | |
1609 | def on_copy(self, widget=None): |
1610 | - |
1611 | - widget = self._get_focus_widget(self.main_window) |
1612 | - if widget is not self.treeview and gobject.signal_lookup("copy-clipboard", widget): |
1613 | - widget.emit("copy-clipboard") |
1614 | - return |
1615 | + if not isinstance(widget, gtk.MenuItem): |
1616 | + widget = self._get_focus_widget(self.main_window) |
1617 | + if widget is not self.treeview and gobject.signal_lookup("copy-clipboard", widget): |
1618 | + widget.emit("copy-clipboard") |
1619 | + return |
1620 | |
1621 | - if isinstance(self.selected_node, MixedTree): |
1622 | + if isinstance(self.selected_node, mixedtree.MixedTree): |
1623 | node = self.selected_node.parent |
1624 | else: |
1625 | node = self.selected_node |
1626 | @@ -675,11 +658,11 @@ |
1627 | return |
1628 | |
1629 | def on_paste(self, widget=None): |
1630 | - |
1631 | - widget = self._get_focus_widget(self.main_window) |
1632 | - if widget is not self.treeview and gobject.signal_lookup("paste-clipboard", widget): |
1633 | - widget.emit("paste-clipboard") |
1634 | - return |
1635 | + if not isinstance(widget, gtk.MenuItem): |
1636 | + widget = self._get_focus_widget(self.main_window) |
1637 | + if widget is not self.treeview and gobject.signal_lookup("paste-clipboard", widget): |
1638 | + widget.emit("paste-clipboard") |
1639 | + return |
1640 | |
1641 | clipboard = gtk.clipboard_get() |
1642 | ios = StringIO.StringIO(clipboard.wait_for_text()) |
1643 | @@ -732,6 +715,19 @@ |
1644 | |
1645 | return |
1646 | |
1647 | + def on_slice(self, widget = None): |
1648 | + if not self.selected_node.is_sliceable(): |
1649 | + self.statusbar.set_statusbar("Cannot slice on this element.") |
1650 | + return |
1651 | + |
1652 | + window = sliceview.SliceView(self.main_window) |
1653 | + window.geometry_dim_tree = self.geometry_dim_tree |
1654 | + window.update(self.selected_node, self.tree) |
1655 | + window.connect("destroy", self._slice_destroy) |
1656 | + return |
1657 | + |
1658 | + def _slice_destroy(self, widget): |
1659 | + self.on_select_row() |
1660 | |
1661 | ## LHS ### |
1662 | |
1663 | @@ -747,6 +743,7 @@ |
1664 | l = self.s.valid_children(":start") |
1665 | |
1666 | self.tree = l[0] |
1667 | + self.signals = {} |
1668 | self.set_treestore(None, l) |
1669 | |
1670 | root_iter = self.treestore.get_iter_first() |
1671 | @@ -764,18 +761,17 @@ |
1672 | """ |
1673 | |
1674 | self.treeview = optionsTree = self.gui.get_widget("optionsTree") |
1675 | - self.treeview.connect("row-collapsed", self.on_treeview_row_collapsed) |
1676 | self.treeview.connect("key_press_event", self.on_treeview_key_press) |
1677 | self.treeview.connect("button_press_event", self.on_treeview_button_press) |
1678 | + self.treeview.connect("row-activated", self.on_activate_row) |
1679 | self.treeview.connect("popup_menu", self.on_treeview_popup) |
1680 | - try: # allow for possibility of no tooltips (like elsewhere) |
1681 | - self.treeview.connect("query-tooltip", self.on_tooltip) |
1682 | - self.treeview.set_property("has-tooltip", False) |
1683 | - except: |
1684 | - pass |
1685 | |
1686 | self.treeview.set_property("rules-hint", True) |
1687 | + |
1688 | self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE) |
1689 | + self.treeview.get_selection().connect("changed", self.on_select_row) |
1690 | + self.treeview.get_selection().set_select_function(self.options_tree_select_func) |
1691 | + self.options_tree_select_func_enabled = True |
1692 | |
1693 | model = gtk.ListStore(str, str, gobject.TYPE_PYOBJECT) |
1694 | self.cellcombo = cellCombo = gtk.CellRendererCombo() |
1695 | @@ -783,6 +779,7 @@ |
1696 | cellCombo.set_property("text-column", 0) |
1697 | cellCombo.set_property("editable", True) |
1698 | cellCombo.set_property("has-entry", False) |
1699 | + cellCombo.connect("edited", self.cellcombo_edited) |
1700 | |
1701 | # Node column |
1702 | column = gtk.TreeViewColumn("Node", cellCombo, text=0) |
1703 | @@ -803,54 +800,31 @@ |
1704 | imgcolumn.set_cell_data_func(cellPicture, self.set_cellpicture_cardinality) |
1705 | optionsTree.append_column(imgcolumn) |
1706 | |
1707 | - # mtw07 - add column for quick preview of data. |
1708 | - self.data_renderer = gtk.CellRendererText() |
1709 | - self.data_renderer.set_property("editable", True) |
1710 | - self.data_renderer.connect("edited", self.on_cell_edit) |
1711 | - self.data_renderer.connect("editing-started", self.on_cell_edit_start) |
1712 | - |
1713 | -# self.data_col = data_col = gtk.TreeViewColumn("Data", self.data_renderer, text=4) |
1714 | -# data_col.set_property("expand", True) |
1715 | -# data_col.set_property("sizing", gtk.TREE_VIEW_COLUMN_AUTOSIZE) |
1716 | -# optionsTree.append_column(data_col) |
1717 | - |
1718 | - # display name, gtk.ListStore containing the display names of possible choices, pointer to node in self.tree -- a choice or a tree, pointer to currently active tree and its data. |
1719 | - self.treestore = gtk.TreeStore(str, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, str) |
1720 | + # 0: display name, |
1721 | + # 1: gtk.ListStore containing the display names of possible choices |
1722 | + # 2: pointer to node in self.tree -- a choice or a tree |
1723 | + # 3: pointer to currently active tree |
1724 | + |
1725 | + self.treestore = gtk.TreeStore(str, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT) |
1726 | self.treeview.set_model(self.treestore) |
1727 | -# self.treeview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_VERTICAL) |
1728 | - |
1729 | - optionsTree.get_selection().connect("changed", self.on_select_row) |
1730 | - self.treeview.get_selection().set_select_function(self.options_tree_select_func) |
1731 | - self.options_tree_select_func_enabled = True |
1732 | - optionsTree.connect("button_press_event", self.on_treeview_clicked) |
1733 | - optionsTree.connect("row-activated", self.on_activate_row) |
1734 | - cellCombo.connect("edited", self.cellcombo_edited) |
1735 | - |
1736 | self.treeview.set_enable_search(False) |
1737 | |
1738 | return |
1739 | |
1740 | - def create_liststore(self, l): |
1741 | + def create_liststore(self, choice_or_tree): |
1742 | """ |
1743 | Given a list of possible choices, create the liststore for the |
1744 | gtk.CellRendererCombo that contains the names of possible choices. |
1745 | """ |
1746 | |
1747 | liststore = gtk.ListStore(str, gobject.TYPE_PYOBJECT) |
1748 | - if l.__class__ is choice.Choice: |
1749 | - l = l.l # I'm really sorry about this, blame dham |
1750 | - |
1751 | - if not isinstance(l, list): |
1752 | - l = [l] |
1753 | - |
1754 | - # Ignoring the numerous l's involved in getting the choices, |
1755 | - # l is now a list of possible names. |
1756 | - for t in l: |
1757 | - name = self.get_display_name(t) |
1758 | + |
1759 | + for t in choice_or_tree.get_choices(): |
1760 | + name = t.get_display_name() |
1761 | liststore.append([name, t]) |
1762 | |
1763 | return liststore |
1764 | - |
1765 | + |
1766 | def set_treestore(self, iter=None, new_tree=[], recurse=False, replace=False): |
1767 | """ |
1768 | Given a list of children of a node in a treestore, stuff them in the treestore. |
1769 | @@ -861,39 +835,50 @@ |
1770 | iter = self.treestore.iter_parent(replacediter) |
1771 | else: |
1772 | self.remove_children(iter) |
1773 | - |
1774 | + |
1775 | for t in new_tree: |
1776 | - if t.__class__ is tree.Tree: |
1777 | - if self.choice_or_tree_is_hidden(t): |
1778 | + if t is not None and t in self.signals: |
1779 | + t.disconnect(self.signals[t][0]) |
1780 | + t.disconnect(self.signals[t][1]) |
1781 | + |
1782 | + if isinstance(t, tree.Tree): |
1783 | + if t.is_hidden(): |
1784 | + attrid = t.connect("on-set-attr", self.on_set_attr, self.treestore.get_path(iter)) |
1785 | + dataid = t.connect("on-set-data", self.on_set_data, self.treestore.get_path(iter)) |
1786 | + self.signals[t] = (attrid, dataid) |
1787 | continue |
1788 | |
1789 | liststore = self.create_liststore(t) |
1790 | |
1791 | - # Convert node data, if it exists, to a string |
1792 | - data = "" |
1793 | - node_data = t.data |
1794 | - if node_data is not None: |
1795 | -# if t.__class__ is str: |
1796 | -# if len(t) > 4: # Trim the string if it's long |
1797 | -# node_data = t[:4] + ".." |
1798 | - |
1799 | - data = str(node_data) |
1800 | - |
1801 | if replace: |
1802 | - child_iter = self.treestore.insert_before(iter, replacediter, [self.get_display_name(t), liststore, t, t, data]) |
1803 | + child_iter = self.treestore.insert_before(iter, replacediter, [t.get_display_name(), liststore, t, t]) |
1804 | else: |
1805 | - child_iter = self.treestore.append(iter, [self.get_display_name(t), liststore, t, t, data]) |
1806 | - |
1807 | + child_iter = self.treestore.append(iter, [t.get_display_name(), liststore, t, t]) |
1808 | + |
1809 | + attrid = t.connect("on-set-attr", self.on_set_attr, self.treestore.get_path(child_iter)) |
1810 | + dataid = t.connect("on-set-data", self.on_set_data, self.treestore.get_path(child_iter)) |
1811 | + self.signals[t] = (attrid, dataid) |
1812 | + |
1813 | if recurse and t.active: self.set_treestore(child_iter, t.children, recurse) |
1814 | - elif t.__class__ is choice.Choice: |
1815 | + |
1816 | + elif isinstance(t, choice.Choice): |
1817 | liststore = self.create_liststore(t) |
1818 | ts_choice = t.get_current_tree() |
1819 | - if self.choice_or_tree_is_hidden(ts_choice): |
1820 | + if ts_choice.is_hidden(): |
1821 | + attrid = t.connect("on-set-attr", self.on_set_attr, self.treestore.get_path(iter)) |
1822 | + dataid = t.connect("on-set-data", self.on_set_data, self.treestore.get_path(iter)) |
1823 | + self.signals[t] = (attrid, dataid) |
1824 | continue |
1825 | + |
1826 | if replace: |
1827 | - child_iter = self.treestore.insert_before(iter, replacediter, [self.get_display_name(ts_choice), liststore, t, ts_choice, ""]) |
1828 | + child_iter = self.treestore.insert_before(iter, replacediter, [ts_choice.get_display_name(), liststore, t, ts_choice]) |
1829 | else: |
1830 | - child_iter = self.treestore.append(iter, [self.get_display_name(ts_choice), liststore, t, ts_choice, ""]) |
1831 | + child_iter = self.treestore.append(iter, [ts_choice.get_display_name(), liststore, t, ts_choice]) |
1832 | + |
1833 | + attrid = t.connect("on-set-attr", self.on_set_attr, self.treestore.get_path(child_iter)) |
1834 | + dataid = t.connect("on-set-data", self.on_set_data, self.treestore.get_path(child_iter)) |
1835 | + self.signals[t] = (attrid, dataid) |
1836 | + |
1837 | if recurse and t.active: self.set_treestore(child_iter, ts_choice.children, recurse) |
1838 | |
1839 | if replace: |
1840 | @@ -976,10 +961,7 @@ |
1841 | foreground colour. |
1842 | """ |
1843 | |
1844 | - liststore = self.treestore.get_value(iter, 1) |
1845 | - choice_or_tree = self.treestore.get_value(iter, 2) |
1846 | - active_tree = self.treestore.get_value(iter, 3) |
1847 | - data = self.treestore.get_value(iter, 4) |
1848 | + liststore, choice_or_tree, active_tree = self.treestore.get(iter, 1, 2, 3) |
1849 | |
1850 | # set the model for the cellcombo, where it gets the possible choices for the name |
1851 | cellCombo.set_property("model", liststore) |
1852 | @@ -1007,9 +989,9 @@ |
1853 | """ |
1854 | |
1855 | choice_or_tree = self.treestore.get_value(iter, 2) |
1856 | - if choice_or_tree.__class__ is tree.Tree: |
1857 | + if isinstance(choice_or_tree, tree.Tree): |
1858 | cell.set_property("stock-id", None) |
1859 | - elif choice_or_tree.__class__ is choice.Choice: |
1860 | + elif isinstance(choice_or_tree, choice.Choice): |
1861 | cell.set_property("stock-id", gtk.STOCK_GO_DOWN) |
1862 | |
1863 | return |
1864 | @@ -1042,132 +1024,6 @@ |
1865 | |
1866 | return |
1867 | |
1868 | - def on_treeview_row_collapsed(self, treeview, iter, path): |
1869 | - """ |
1870 | - Called when a row in the LHS treeview is collapsed. |
1871 | - """ |
1872 | - |
1873 | - #self.treeview.get_column(0).queue_resize() |
1874 | - #self.treeview.get_column(1).queue_resize() |
1875 | - |
1876 | - return |
1877 | - |
1878 | - def on_tooltip(self, widget, x, y, keyboard_mode, tooltip): |
1879 | - y-=25 # It's hardcoded. Gtk doesn't offer a way to get at the column height without a lot more code. |
1880 | - (tx, ty) = self.treeview.convert_bin_window_to_tree_coords(x, y) |
1881 | - pathinfo = self.treeview.get_path_at_pos(x, y) |
1882 | - |
1883 | - if pathinfo is None: |
1884 | - return False |
1885 | - |
1886 | - path = pathinfo[0] |
1887 | - column = pathinfo[1] |
1888 | - |
1889 | - if path is None: |
1890 | - return |
1891 | - |
1892 | - ctitle = column.get_title() |
1893 | - |
1894 | - iter = self.treestore.get_iter(path) |
1895 | - |
1896 | - # Get the tree or choice pointed to by the iterator. |
1897 | - tree = self.treestore.get_value(iter, 2) |
1898 | - |
1899 | - if tree.__class__ is choice.Choice: |
1900 | - tree = tree.get_current_tree() |
1901 | - |
1902 | - if ctitle == "Node": |
1903 | - if tree.doc is None: |
1904 | - text = "(No documentation)" |
1905 | - else: |
1906 | - text = self.render_whitespace(tree.doc) |
1907 | - elif ctitle == "Data": |
1908 | - comment_tree = self.get_comment(tree) |
1909 | - |
1910 | - if comment_tree is None: |
1911 | - text = "(No comment)" |
1912 | - else: |
1913 | - text = comment_tree.data |
1914 | - else: |
1915 | - return False |
1916 | - |
1917 | - tooltip.set_text(text) |
1918 | - return True |
1919 | - |
1920 | - def on_cell_edit(self, cellr, path, new_text): |
1921 | - iter = self.treestore.get_iter(path) |
1922 | - node = self.selected_node |
1923 | - |
1924 | - if new_text == "": |
1925 | - return |
1926 | - |
1927 | - # mtw07 - Check whether the data is valid or not. If so, update the treeview |
1928 | - # and internal model. GTK automatically clears the text field if we don't |
1929 | - # add the new data to the treestore. |
1930 | - (invalid, data) = node.valid_data(node.datatype, new_text) |
1931 | - |
1932 | - if invalid: |
1933 | - try: |
1934 | - name = node.datatype.__name__ |
1935 | - dialogs.error(self.main_window, "Invalid data type. A %s is required." % name) |
1936 | - except: |
1937 | - pass |
1938 | - return |
1939 | - |
1940 | - self.treestore.set_value(iter, 4, new_text) |
1941 | - node.data = new_text |
1942 | - |
1943 | - # Update the node data box. |
1944 | - self.node_data.get_buffer().set_text(new_text) |
1945 | - |
1946 | - # Update the validation errors list. |
1947 | - if self.scherror.errlist_is_open(): |
1948 | - if self.scherror.errlist_type == 0: |
1949 | - self.scherror.on_validate_schematron() |
1950 | - else: |
1951 | - self.scherror.on_validate() |
1952 | - |
1953 | - return |
1954 | - |
1955 | - def on_cell_edit_start(self, cellrenderer, editable, path): |
1956 | - iter = self.treestore.get_iter(path) |
1957 | - node = self.selected_node |
1958 | - |
1959 | - # If the cell has (<type>) in it, clear it for the user to enter data. |
1960 | - if node.datatype is not None and node.data is None: |
1961 | - editable.set_text("") |
1962 | - |
1963 | - return True |
1964 | - |
1965 | - def on_treeview_clicked(self, treeview, event): |
1966 | - """ |
1967 | - This routine is called every time the mouse is clicked on the treeview on the |
1968 | - left-hand side. It processes the "buttons" gtk.STOCK_ADD and gtk.STOCK_REMOVE |
1969 | - in the right-hand column, activating, adding and removing tree nodes as |
1970 | - necessary. |
1971 | - """ |
1972 | - |
1973 | - if event.button != 1: |
1974 | - return |
1975 | - |
1976 | - pathinfo = treeview.get_path_at_pos(int(event.x), int(event.y)) |
1977 | - |
1978 | - if pathinfo is None: |
1979 | - return |
1980 | - |
1981 | - path = pathinfo[0] |
1982 | - col = pathinfo[1] |
1983 | - |
1984 | - iter = self.treestore.get_iter(path) |
1985 | - self.update_data_column(self.treestore, iter) |
1986 | - |
1987 | - if col is not self.imgcolumn: |
1988 | - return |
1989 | - |
1990 | - self.toggle_tree(iter) |
1991 | - |
1992 | - return |
1993 | - |
1994 | def toggle_tree(self, iter): |
1995 | """ |
1996 | Toggles the state of part of the tree. |
1997 | @@ -1285,7 +1141,10 @@ |
1998 | self.expand_treestore(iter) |
1999 | iter = self.treestore.insert_after( |
2000 | parent=parent_iter, sibling=iter, |
2001 | - row=[self.get_display_name(new_tree), liststore, new_tree, new_tree.get_current_tree(), ""]) |
2002 | + row=[new_tree.get_display_name(), liststore, new_tree, new_tree.get_current_tree()]) |
2003 | + attrid = new_tree.connect("on-set-attr", self.on_set_attr, self.treestore.get_path(iter)) |
2004 | + dataid = new_tree.connect("on-set-data", self.on_set_data, self.treestore.get_path(iter)) |
2005 | + self.signals[new_tree] = (attrid, dataid) |
2006 | self.set_saved(False) |
2007 | |
2008 | parent_tree.recompute_validity() |
2009 | @@ -1301,10 +1160,13 @@ |
2010 | self.options_tree_select_func_enabled = True |
2011 | return False |
2012 | |
2013 | - if not self.node_data_store(): |
2014 | + if not self.data.store(): |
2015 | return False |
2016 | |
2017 | - if isinstance(self.selected_node, MixedTree) and self.geometry_dim_tree is not None and self.selected_node.parent is self.geometry_dim_tree.parent and self.selected_node.data is not None: |
2018 | + if isinstance(self.selected_node, mixedtree.MixedTree) \ |
2019 | + and self.geometry_dim_tree is not None \ |
2020 | + and self.selected_node.parent is self.geometry_dim_tree.parent \ |
2021 | + and self.selected_node.data is not None: |
2022 | self.geometry_dim_tree.set_data(self.selected_node.data) |
2023 | |
2024 | return True |
2025 | @@ -1327,15 +1189,29 @@ |
2026 | return |
2027 | |
2028 | def on_treeview_button_press(self, treeview, event): |
2029 | - if event.button == 3: |
2030 | - x = int(event.x) |
2031 | - y = int(event.y) |
2032 | - path = treeview.get_path_at_pos(x, y)[0] |
2033 | - if path is not None: |
2034 | - treeview.get_selection().select_path(path) |
2035 | + """ |
2036 | + This routine is called every time the mouse is clicked on the treeview on the |
2037 | + left-hand side. It processes the "buttons" gtk.STOCK_ADD and gtk.STOCK_REMOVE |
2038 | + in the right-hand column, activating, adding and removing tree nodes as |
2039 | + necessary. |
2040 | + """ |
2041 | + pathinfo = treeview.get_path_at_pos(int(event.x), int(event.y)) |
2042 | + |
2043 | + if event.button == 1: |
2044 | + |
2045 | + if pathinfo is not None: |
2046 | + path = pathinfo[0] |
2047 | + col = pathinfo[1] |
2048 | + |
2049 | + if col is self.imgcolumn: |
2050 | + iter = self.treestore.get_iter(path) |
2051 | + self.toggle_tree(iter) |
2052 | + |
2053 | + elif event.button == 3: |
2054 | + if pathinfo is not None: |
2055 | + treeview.get_selection().select_path(pathinfo[0]) |
2056 | self.show_popup(None, event.button, event.time) |
2057 | return True |
2058 | - return False |
2059 | |
2060 | def popup_location(self, widget, user_data): |
2061 | column = self.treeview.get_column(0) |
2062 | @@ -1358,28 +1234,22 @@ |
2063 | Called when a row is selected. Update the options frame. |
2064 | """ |
2065 | |
2066 | - path = self.get_selected_row(selection) |
2067 | + if isinstance(selection, str) or isinstance(selection, tuple): |
2068 | + path = selection |
2069 | + else: |
2070 | + path = self.get_selected_row(selection) |
2071 | + |
2072 | if path is None: |
2073 | return |
2074 | + |
2075 | self.selected_iter = iter = self.treestore.get_iter(path) |
2076 | - |
2077 | - choice_or_tree = self.treestore.get_value(iter, 2) |
2078 | - |
2079 | - active_tree = self.treestore.get_value(iter, 3) |
2080 | + choice_or_tree, active_tree = self.treestore.get(iter, 2, 3) |
2081 | + |
2082 | debug.dprint(active_tree) |
2083 | |
2084 | self.selected_node = self.get_painted_tree(iter) |
2085 | self.update_options_frame() |
2086 | |
2087 | - node = self.selected_node |
2088 | - |
2089 | - if isinstance(node, MixedTree): |
2090 | - node = node.child |
2091 | - |
2092 | - # TODO: Handle tuple datatypes. Is it possible in a Gtk treeview column? |
2093 | - if not isinstance(node.datatype, tuple): |
2094 | - self.data_renderer.set_property("editable", not (node.not_editable())) |
2095 | - |
2096 | name = self.get_spudpath(active_tree) |
2097 | self.statusbar.set_statusbar(name) |
2098 | self.current_spudpath = name |
2099 | @@ -1477,18 +1347,6 @@ |
2100 | else: |
2101 | return None |
2102 | |
2103 | - def update_data_column(self, model, itParent): |
2104 | - """ |
2105 | - Update the data column in the treeview. Used when a user expands a row. |
2106 | - """ |
2107 | - |
2108 | - iter = model.iter_children(itParent) |
2109 | - |
2110 | - while iter is not None: |
2111 | - self.set_cell_node(iter) |
2112 | - iter = model.iter_next(iter) |
2113 | - |
2114 | - |
2115 | def on_activate_row(self, treeview, path, view_column): |
2116 | """ |
2117 | Called when you double click or press Enter on a row. |
2118 | @@ -1507,10 +1365,6 @@ |
2119 | else: |
2120 | treeview.expand_row(path, False) |
2121 | |
2122 | - # Update the data column for the newly expanded row. |
2123 | - (model, itParent) = self.treeview.get_selection().get_selected() |
2124 | - self.update_data_column(model, itParent) |
2125 | - |
2126 | return |
2127 | |
2128 | def cellcombo_edited(self, cellrenderertext, path, new_text): |
2129 | @@ -1561,53 +1415,6 @@ |
2130 | |
2131 | return |
2132 | |
2133 | - def paint_validity(self): |
2134 | - """ |
2135 | - Walk up the parental line, repainting the colour for their validity |
2136 | - appropriately. This is called when a validity-changing event occurs. |
2137 | - Repaint the whole tree if there is no selection. |
2138 | - """ |
2139 | - |
2140 | - def paint_iter_validity(iter): |
2141 | - while iter is not None: |
2142 | - active_tree = self.treestore.get_value(iter, 3) |
2143 | - if active_tree.valid: |
2144 | - self.cellcombo.set_property("foreground", "black") |
2145 | - else: |
2146 | - self.cellcombo.set_property("foreground", "blue") |
2147 | - iter = self.treestore.iter_next(iter) |
2148 | - |
2149 | - return |
2150 | - |
2151 | - selection = self.treeview.get_selection() |
2152 | - path = self.get_selected_row(selection) |
2153 | - if path is None: |
2154 | - paint_iter_validity(self.treestore.get_iter_first()) |
2155 | - else: |
2156 | - paint_iter_validity(self.treestore.get_iter(path)) |
2157 | - |
2158 | - self.treeview.queue_draw() |
2159 | - |
2160 | - return |
2161 | - |
2162 | - def get_display_name(self, active_tree): |
2163 | - """ |
2164 | - This is a fluidity hack, allowing the name displayed in the treeview on the |
2165 | - left to be different to the element name. If it has an attribute name="xxx", |
2166 | - element_tag (xxx) is displayed. |
2167 | - """ |
2168 | - |
2169 | - if active_tree.__class__ is tree.Tree: |
2170 | - displayname = active_tree.name |
2171 | - if "name" in active_tree.attrs.keys(): |
2172 | - attrname = active_tree.attrs["name"][1] |
2173 | - if attrname is not None: |
2174 | - displayname = displayname + " (" + attrname + ")" |
2175 | - elif active_tree.__class__ is choice.Choice: |
2176 | - displayname = self.get_display_name(active_tree.get_current_tree()) |
2177 | - |
2178 | - return displayname |
2179 | - |
2180 | def get_treeview_iter(self, selection): |
2181 | """ |
2182 | Get a treeview iterator object, given a selection. |
2183 | @@ -1619,19 +1426,19 @@ |
2184 | |
2185 | return self.treestore.get_iter(path) |
2186 | |
2187 | - def update_painted_name(self): |
2188 | - """ |
2189 | - This updates the treestore (and the liststore for the gtk.CellRendererCombo) |
2190 | - with a new name, when the name="xxx" attribute is changed. |
2191 | - """ |
2192 | + def on_set_data(self, node, data, path): |
2193 | + self.set_saved(False) |
2194 | + self.treeview.queue_draw() |
2195 | + #self.on_select_row(path) |
2196 | |
2197 | - iter = self.get_treeview_iter(self.treeview.get_selection()) |
2198 | - if iter is None: |
2199 | + def on_set_attr(self, node, attr, value, path): |
2200 | + if attr != "name": |
2201 | return |
2202 | |
2203 | + iter = self.treestore.get_iter(path) |
2204 | liststore = self.treestore.get_value(iter, 1) |
2205 | active_tree = self.treestore.get_value(iter, 3) |
2206 | - new_name = self.get_display_name(active_tree) |
2207 | + new_name = active_tree.get_display_name() |
2208 | self.treestore.set_value(iter, 0, new_name) |
2209 | |
2210 | # find the liststore iter corresponding to the painted choice |
2211 | @@ -1642,9 +1449,7 @@ |
2212 | liststore.set_value(list_iter, 0, new_name) |
2213 | list_iter = liststore.iter_next(list_iter) |
2214 | |
2215 | - self.treeview.get_column(0).queue_resize() |
2216 | - |
2217 | - return |
2218 | + self.treeview.queue_resize() |
2219 | |
2220 | def get_painted_tree(self, iter_or_tree, lock_geometry_dim = True): |
2221 | """ |
2222 | @@ -1662,25 +1467,7 @@ |
2223 | else: |
2224 | active_tree = self.treestore.get_value(iter_or_tree, 3) |
2225 | |
2226 | - integers = [child for child in active_tree.children if child.name == "integer_value"] |
2227 | - reals = [child for child in active_tree.children if child.name == "real_value"] |
2228 | - logicals = [child for child in active_tree.children if child.name == "logical_value"] |
2229 | - strings = [child for child in active_tree.children if child.name == "string_value"] |
2230 | - |
2231 | - child = None |
2232 | - if len(integers) > 0: |
2233 | - child = integers[0] |
2234 | - if len(reals) > 0: |
2235 | - child = reals[0] |
2236 | - if len(logicals) > 0: |
2237 | - child = logicals[0] |
2238 | - if len(strings) > 0: |
2239 | - child = strings[0] |
2240 | - |
2241 | - if child is None: |
2242 | - painted_tree = active_tree |
2243 | - else: |
2244 | - painted_tree = MixedTree(active_tree, child) |
2245 | + painted_tree = active_tree.get_mixed_data() |
2246 | |
2247 | if not isinstance(iter_or_tree, tree.Tree) and not self.treestore_iter_is_active(iter_or_tree): |
2248 | painted_tree = tree.Tree(painted_tree.name, painted_tree.schemaname, painted_tree.attrs, doc = painted_tree.doc) |
2249 | @@ -1690,10 +1477,10 @@ |
2250 | data_tree = tree.Tree(painted_tree.name, painted_tree.schemaname, datatype = "fixed") |
2251 | data_tree.data = painted_tree.data |
2252 | painted_tree = MixedTree(painted_tree, data_tree) |
2253 | - elif isinstance(self.geometry_dim_tree, MixedTree) and active_tree is self.geometry_dim_tree.parent: |
2254 | + elif isinstance(self.geometry_dim_tree, mixedtree.MixedTree) and active_tree is self.geometry_dim_tree.parent: |
2255 | data_tree = tree.Tree(painted_tree.child.name, painted_tree.child.schemaname, datatype = "fixed") |
2256 | data_tree.data = painted_tree.data |
2257 | - painted_tree = MixedTree(painted_tree, data_tree) |
2258 | + painted_tree = mixedtree.MixedTree(painted_tree, data_tree) |
2259 | |
2260 | return painted_tree |
2261 | |
2262 | @@ -1724,20 +1511,19 @@ |
2263 | Find the iter into the treestore corresponding to the geometry dimension, and |
2264 | perform checks to test that the geometry dimension node is valid. |
2265 | """ |
2266 | - |
2267 | + |
2268 | + self.geometry_dim_tree = self.data.geometry_dim_tree = None |
2269 | # The tree must exist |
2270 | if self.tree is None: |
2271 | - self.geometry_dim_tree = None |
2272 | return |
2273 | |
2274 | # A geometry dimension element must exist |
2275 | iter = self.get_treestore_iter_from_xmlpath("/" + self.tree.name + self.data_paths["dim"]) |
2276 | if iter is None: |
2277 | - self.geometry_dim_tree = None |
2278 | return |
2279 | |
2280 | painted_tree = self.get_painted_tree(iter, False) |
2281 | - if isinstance(painted_tree, MixedTree): |
2282 | + if isinstance(painted_tree, mixedtree.MixedTree): |
2283 | # If the geometry dimension element has a hidden data element, it must |
2284 | # have datatype tuple or fixed |
2285 | if not isinstance(painted_tree.datatype, tuple) and painted_tree.datatype != "fixed": |
2286 | @@ -1745,7 +1531,6 @@ |
2287 | return |
2288 | elif painted_tree.datatype != "fixed": |
2289 | # Otherwise, only fixed datatype is permitted |
2290 | - self.geometry_dim_tree = None |
2291 | return |
2292 | |
2293 | # All parents of the geometry dimension element must have cardinality "" |
2294 | @@ -1763,18 +1548,16 @@ |
2295 | elif painted_tree.datatype == "fixed": |
2296 | possible_dims = [painted_tree.data] |
2297 | else: |
2298 | - self.geometry_dim_tree = None |
2299 | return |
2300 | for opt in possible_dims: |
2301 | try: |
2302 | test = int(opt) |
2303 | assert test > 0 |
2304 | except: |
2305 | - self.geometry_dim_tree = None |
2306 | return |
2307 | |
2308 | # A valid geometry dimension element has been located |
2309 | - self.geometry_dim_tree = painted_tree |
2310 | + self.geometry_dim_tree = self.data.geometry_dim_tree = painted_tree |
2311 | |
2312 | return |
2313 | |
2314 | @@ -1792,55 +1575,6 @@ |
2315 | |
2316 | return True |
2317 | |
2318 | - def choice_or_tree_is_hidden(self, choice_or_tree): |
2319 | - """ |
2320 | - Tests whether the supplied choice or tree should be hidden from the LHS. |
2321 | - """ |
2322 | - |
2323 | - return self.choice_or_tree_is_comment(choice_or_tree) or choice_or_tree.name in ["integer_value", "real_value", "string_value", "logical_value"] |
2324 | - |
2325 | - def choice_or_tree_is_comment(self, choice_or_tree): |
2326 | - """ |
2327 | - Test whether the given node is a comment node. |
2328 | - """ |
2329 | - |
2330 | - if not isinstance(choice_or_tree, tree.Tree): |
2331 | - return False |
2332 | - |
2333 | - if not choice_or_tree.name == "comment": |
2334 | - return False |
2335 | - |
2336 | - if not choice_or_tree.attrs == {}: |
2337 | - return False |
2338 | - |
2339 | - if not choice_or_tree.children == []: |
2340 | - return False |
2341 | - |
2342 | - if not choice_or_tree.datatype is str: |
2343 | - return False |
2344 | - |
2345 | - if not choice_or_tree.cardinality == "?": |
2346 | - return False |
2347 | - |
2348 | - return True |
2349 | - |
2350 | - def get_comment(self, choice_or_tree): |
2351 | - """ |
2352 | - Return the first comment found as a child of the supplied node, or None if |
2353 | - none found. |
2354 | - """ |
2355 | - |
2356 | - if choice_or_tree is None or isinstance(choice_or_tree, choice.Choice): |
2357 | - return None |
2358 | - |
2359 | - for child in choice_or_tree.children: |
2360 | - if self.choice_or_tree_is_comment(child): |
2361 | - return child |
2362 | - |
2363 | - return None |
2364 | - |
2365 | - return |
2366 | - |
2367 | def choice_or_tree_matches(self, text, choice_or_tree, recurse, search_active_subtrees = False): |
2368 | """ |
2369 | See if the supplied node matches a given piece of text. If recurse is True, |
2370 | @@ -1849,7 +1583,7 @@ |
2371 | choice match. |
2372 | """ |
2373 | |
2374 | - if self.choice_or_tree_is_hidden(choice_or_tree): |
2375 | + if choice_or_tree.is_hidden(): |
2376 | return False |
2377 | elif isinstance(choice_or_tree, choice.Choice): |
2378 | if self.choice_or_tree_matches(text, choice_or_tree.get_current_tree(), False): |
2379 | @@ -1870,7 +1604,7 @@ |
2380 | text_re = re.compile(text) |
2381 | else: |
2382 | text_re = re.compile(text, re.IGNORECASE) |
2383 | - comment = self.get_comment(choice_or_tree) |
2384 | + comment = choice_or_tree.get_comment() |
2385 | if comment is not None and comment.data is not None and text_re.search(comment.data) is not None: |
2386 | return True |
2387 | elif recurse: |
2388 | @@ -1909,1279 +1643,58 @@ |
2389 | return |
2390 | |
2391 | ### RHS ### |
2392 | - |
2393 | - def render_whitespace(self, desc): |
2394 | - ''' Render the line wrapping in desc as follows: |
2395 | - |
2396 | - * Newlines followed by 0-1 spaces are ignored. |
2397 | - * Blank lines start new paragraphs. |
2398 | - * Newlines followed by more than 1 space are honoured. |
2399 | - ''' |
2400 | - |
2401 | - prev_line_literal=False |
2402 | - prev_line_new_para=False |
2403 | - newdesc="" |
2404 | - |
2405 | - for line in desc.split("\n"): |
2406 | - |
2407 | - if (line[:1]==" "): |
2408 | - # Literal line with leading blanks. |
2409 | - newdesc=newdesc+"\n"+line |
2410 | - prev_line_literal=True |
2411 | - prev_line_new_para=False |
2412 | - continue |
2413 | - |
2414 | - if (line.strip()==""): |
2415 | - # New paragraph. |
2416 | - |
2417 | - # Collapse multiple new paragraphs into one, except when |
2418 | - # following a literal line. |
2419 | - if (prev_line_new_para and not prev_line_literal): |
2420 | - continue |
2421 | - |
2422 | - newdesc=newdesc+"\n" |
2423 | - prev_line_new_para=True |
2424 | - continue |
2425 | - |
2426 | - if prev_line_literal: |
2427 | - newdesc=newdesc+"\n" |
2428 | - prev_line_literal=False |
2429 | - prev_line_new_para=False |
2430 | - |
2431 | - if prev_line_new_para: |
2432 | - newdesc=newdesc+" " |
2433 | - prev_line_new_para=False |
2434 | - |
2435 | - # Default case |
2436 | - newdesc=newdesc+line+" " |
2437 | - |
2438 | - return newdesc |
2439 | - |
2440 | - def link_bounds(self, text): |
2441 | - """ |
2442 | - Return a list of tuples corresponding to the start and end points of links in |
2443 | - the supplied string. |
2444 | - """ |
2445 | - |
2446 | - bounds = [] |
2447 | - |
2448 | - text_split = text.lower().split("http://") |
2449 | - if len(text_split) > 1: |
2450 | - lbound = -7 |
2451 | - for i in range(len(text_split))[1:]: |
2452 | - lbound += len(text_split[i - 1]) + 7 |
2453 | - ubound = lbound + len(text_split[i].split(" ")[0].split("\n")[0]) + 7 |
2454 | - while text[ubound - 1:ubound] in [".", ",", ":", ";", "\"", "'", ")", "]", "}"]: |
2455 | - ubound -= 1 |
2456 | - bounds.append((lbound, ubound)) |
2457 | - |
2458 | - return bounds |
2459 | - |
2460 | - def type_name(self, datatype): |
2461 | - """ |
2462 | - Return a human readable version of datatype. |
2463 | - """ |
2464 | - |
2465 | - def familiar_type(type_as_printable): |
2466 | - """ |
2467 | - Convert some type names to more familiar equivalents. |
2468 | - """ |
2469 | - |
2470 | - if type_as_printable == "decim": |
2471 | - return "float" |
2472 | - elif type_as_printable == "int": |
2473 | - return "integer" |
2474 | - elif type_as_printable == "str": |
2475 | - return "string" |
2476 | - else: |
2477 | - return type_as_printable |
2478 | - |
2479 | - datatype_string = str(datatype) |
2480 | - |
2481 | - if datatype_string[:7] == "<type '" and datatype_string[len(datatype_string) - 2:] == "'>": |
2482 | - value_type_split = datatype_string.split("'") |
2483 | - return familiar_type(value_type_split[1]) |
2484 | - |
2485 | - value_type_split1 = datatype_string.split(".") |
2486 | - value_type_split2 = value_type_split1[len(value_type_split1) - 1].split(" ") |
2487 | - if len(value_type_split2) == 1: |
2488 | - return familiar_type(value_type_split2[0][0:len(value_type_split2[0]) - 6]) |
2489 | - else: |
2490 | - return familiar_type(value_type_split2[0]) |
2491 | - |
2492 | - def printable_type(self, datatype, bracket = True): |
2493 | - """ |
2494 | - Create a string to be displayed in place of empty data / attributes. |
2495 | - """ |
2496 | - |
2497 | - if isinstance(datatype, plist.List): |
2498 | - if (isinstance(datatype.cardinality, int) and datatype.cardinality == 1) or datatype.cardinality == "": |
2499 | - type_as_printable = self.type_name(datatype.datatype).lower() |
2500 | - else: |
2501 | - type_as_printable = self.type_name(datatype).lower() + " of " |
2502 | - list_type_as_printable = self.type_name(datatype.datatype).lower() |
2503 | - if isinstance(datatype.cardinality, int): |
2504 | - type_as_printable += str(datatype.cardinality) + " " + list_type_as_printable + "s" |
2505 | - else: |
2506 | - type_as_printable += list_type_as_printable + "s" |
2507 | - else: |
2508 | - type_as_printable = self.type_name(datatype).lower() |
2509 | - |
2510 | - if bracket: |
2511 | - type_as_printable = "(" + type_as_printable + ")" |
2512 | - |
2513 | - return type_as_printable |
2514 | - |
2515 | - def init_options_frame(self): |
2516 | - """ |
2517 | - Initialise the RHS. |
2518 | - """ |
2519 | - |
2520 | - # mtw07 - TODO: set the visibility of the options frame, depending on the value in the schema file. |
2521 | - self.options_frame = self.gui.get_widget("optionsFrame") |
2522 | - |
2523 | - # Display the right hand side by default. |
2524 | - self.options_frame.set_property("visible", True) |
2525 | - |
2526 | - self.node_desc = self.gui.get_widget("nodeDescription") |
2527 | - self.node_desc.set_buffer(TextBufferMarkup.PangoBuffer()) |
2528 | - self.node_desc.connect("button-release-event", self.node_desc_mouse_button_release) |
2529 | - self.node_desc.connect("motion-notify-event", self.node_desc_mouse_over) |
2530 | - |
2531 | - self.node_attrs = self.gui.get_widget("nodeAttributes") |
2532 | - attrs_model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) |
2533 | - self.node_attrs.set_model(attrs_model) |
2534 | - self.node_attrs.connect("motion-notify-event", self.node_attrs_mouse_over) |
2535 | - key_renderer = gtk.CellRendererText() |
2536 | - key_renderer.set_property("editable", False) |
2537 | - attrs_col1 = gtk.TreeViewColumn("Name", key_renderer, text = 0) |
2538 | - attrs_col1.set_cell_data_func(key_renderer, self.node_attrs_key_data_func) |
2539 | - attrs_col1.set_property("min-width", 75) |
2540 | - attrs_val_entry_renderer = gtk.CellRendererText() |
2541 | - attrs_val_entry_renderer.connect("edited", self.node_attrs_edited) |
2542 | - attrs_val_entry_renderer.connect("editing-started", self.node_attrs_entry_edit_start) |
2543 | - attrs_val_combo_renderer = gtk.CellRendererCombo() |
2544 | - attrs_val_combo_renderer.set_property("text-column", 0) |
2545 | - attrs_val_combo_renderer.connect("edited", self.node_attrs_selected) |
2546 | - attrs_val_combo_renderer.connect("editing-started", self.node_attrs_combo_edit_start) |
2547 | - attrs_col2 = gtk.TreeViewColumn("Value", attrs_val_entry_renderer, text = 1) |
2548 | - attrs_col2.pack_start(attrs_val_combo_renderer) |
2549 | - attrs_col2.set_attributes(attrs_val_combo_renderer, text = 1) |
2550 | - attrs_col2.set_cell_data_func(attrs_val_entry_renderer, self.node_attrs_entry_data_func) |
2551 | - attrs_col2.set_cell_data_func(attrs_val_combo_renderer, self.node_attrs_combo_data_func) |
2552 | - attrs_col2.set_property("expand", True) |
2553 | - attrs_col2.set_property("min-width", 75) |
2554 | - attrs_icon_renderer = gtk.CellRendererPixbuf() |
2555 | - attrs_col3 = gtk.TreeViewColumn("", attrs_icon_renderer) |
2556 | - attrs_col3.set_cell_data_func(attrs_icon_renderer, self.node_attrs_icon_data_func) |
2557 | - self.node_attrs.append_column(attrs_col1) |
2558 | - self.node_attrs.append_column(attrs_col2) |
2559 | - self.node_attrs.append_column(attrs_col3) |
2560 | - |
2561 | - self.node_data_frame = self.gui.get_widget("dataFrame") |
2562 | - |
2563 | - self.node_data_buttons_hbox = self.gui.get_widget("dataButtonsHBox") |
2564 | - |
2565 | - data_revert_button = self.gui.get_widget("dataRevertButton") |
2566 | - data_revert_button.connect("clicked", self.node_data_revert) |
2567 | - |
2568 | - data_store_button = self.gui.get_widget("dataStoreButton") |
2569 | - data_store_button.connect("clicked", self.node_data_store) |
2570 | - |
2571 | - self.node_comment = self.gui.get_widget("nodeComment") |
2572 | - self.node_comment.get_buffer().create_tag("comment_buffer_tag") |
2573 | - self.node_comment.connect("focus-in-event", self.node_comment_focus_in) |
2574 | - self.node_comment.connect("expose-event", self.node_comment_expose) |
2575 | - |
2576 | - return |
2577 | - |
2578 | + |
2579 | + def add_custom_widgets(self): |
2580 | + """ |
2581 | + Adds custom python widgets that aren't easily handeled by glade. |
2582 | + """ |
2583 | + |
2584 | + optionsFrame = self.gui.get_widget("optionsFrame") |
2585 | + |
2586 | + vpane1 = gtk.VPaned() |
2587 | + vpane2 = gtk.VPaned() |
2588 | + vbox = gtk.VBox() |
2589 | + |
2590 | + vpane1.pack2(vpane2, True, False) |
2591 | + vpane2.pack1(vbox, True, False) |
2592 | + optionsFrame.add(vpane1) |
2593 | + |
2594 | + self.description = descriptionwidget.DescriptionWidget() |
2595 | + vpane1.pack1(self.description, True, False) |
2596 | + |
2597 | + self.attributes = attributewidget.AttributeWidget() |
2598 | + vbox.pack_start(self.attributes, True, True) |
2599 | + |
2600 | + databuttons = databuttonswidget.DataButtonsWidget() |
2601 | + vbox.pack_end(databuttons, False) |
2602 | + |
2603 | + self.data = datawidget.DataWidget() |
2604 | + self.data.set_buttons(databuttons) |
2605 | + vbox.pack_end(self.data, True, True) |
2606 | + |
2607 | + self.comment = commentwidget.CommentWidget() |
2608 | + vpane2.pack2(self.comment, True, False) |
2609 | + |
2610 | + optionsFrame.show_all() |
2611 | + return |
2612 | + |
2613 | def update_options_frame(self): |
2614 | """ |
2615 | Update the RHS. |
2616 | """ |
2617 | - |
2618 | - if self.selected_node is None: |
2619 | - self.set_node_desc("<span foreground=\"grey\">No node selected</span>") |
2620 | - elif self.selected_node.doc is None: |
2621 | - self.set_node_desc("<span foreground=\"red\">No documentation</span>") |
2622 | - else: |
2623 | - self.set_node_desc(self.selected_node.doc) |
2624 | - |
2625 | - self.update_node_attrs() |
2626 | - |
2627 | - if self.selected_node is None or not self.selected_node.active: |
2628 | - self.set_node_data_entry() |
2629 | - elif self.node_data_is_tensor() and self.geometry_dim_tree.data is not None: |
2630 | - self.set_node_data_tensor() |
2631 | - elif isinstance(self.selected_node.datatype, tuple): |
2632 | - self.set_node_data_combo() |
2633 | - else: |
2634 | - self.set_node_data_entry() |
2635 | - |
2636 | - self.update_node_comment() |
2637 | + |
2638 | + self.description.update(self.selected_node) |
2639 | + |
2640 | + self.attributes.update(self.selected_node) |
2641 | + |
2642 | + self.data.update(self.selected_node) |
2643 | + |
2644 | + self.comment.update(self.selected_node) |
2645 | |
2646 | self.gui.get_widget("optionsFrame").queue_resize() |
2647 | |
2648 | return |
2649 | |
2650 | - def node_desc_mouse_over(self, widget, event): |
2651 | - """ |
2652 | - Called when the mouse moves over the node description widget. Sets the cursor |
2653 | - to a hand if the mouse hovers over a link. |
2654 | - |
2655 | - Based on code from HyperTextDemo class in hypertext.py from PyGTK 2.12 demos |
2656 | - """ |
2657 | - |
2658 | - if self.selected_node is None or self.selected_node.doc is None: |
2659 | - return |
2660 | - |
2661 | - buffer_pos = self.node_desc.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, int(event.x), int(event.y)) |
2662 | - char_offset = self.node_desc.get_iter_at_location(buffer_pos[0], buffer_pos[1]).get_offset() |
2663 | - |
2664 | - for bounds in self.node_desc_link_bounds: |
2665 | - if char_offset >= bounds[0] and char_offset <= bounds[1]: |
2666 | - self.node_desc.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) |
2667 | - return |
2668 | - |
2669 | - self.node_desc.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM)) |
2670 | - |
2671 | - return |
2672 | - |
2673 | - def node_desc_mouse_button_release(self, widget, event): |
2674 | - """ |
2675 | - Called when a mouse button is released over the node description widget. |
2676 | - Launches a browser if the mouse release was over a link, the left mouse button |
2677 | - was released and no text was selected. |
2678 | - |
2679 | - Based on code from HyperTextDemo class in hypertext.py from PyGTK 2.12 demos |
2680 | - """ |
2681 | - |
2682 | - if self.selected_node is None or self.selected_node.doc is None or event.button != 1: |
2683 | - return |
2684 | - |
2685 | - selection_bounds = self.node_desc.get_buffer().get_selection_bounds() |
2686 | - if selection_bounds: |
2687 | - return |
2688 | - |
2689 | - buffer_pos = self.node_desc.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, int(event.x), int(event.y)) |
2690 | - char_offset = self.node_desc.get_iter_at_location(buffer_pos[0], buffer_pos[1]).get_offset() |
2691 | - |
2692 | - for bounds in self.node_desc_link_bounds: |
2693 | - if char_offset >= bounds[0] and char_offset <= bounds[1]: |
2694 | - webbrowser.open(self.selected_node.doc[bounds[0]:bounds[1]]) |
2695 | - return |
2696 | - |
2697 | - return |
2698 | - |
2699 | - def set_node_desc(self, desc): |
2700 | - """ |
2701 | - Set the node description. |
2702 | - """ |
2703 | - |
2704 | - self.node_desc_link_bounds = self.link_bounds(desc) |
2705 | - |
2706 | - desc = self.render_whitespace(desc) |
2707 | - |
2708 | - if self.node_desc_link_bounds: |
2709 | - new_desc = "" |
2710 | - for i in range(len(self.node_desc_link_bounds)): |
2711 | - if i == 0: |
2712 | - new_desc += desc[:self.node_desc_link_bounds[i][0]] |
2713 | - else: |
2714 | - new_desc += desc[self.node_desc_link_bounds[i - 1][1]:self.node_desc_link_bounds[i][0]] |
2715 | - new_desc += "<span foreground=\"blue\" underline=\"single\">" + desc[self.node_desc_link_bounds[i][0]:self.node_desc_link_bounds[i][1]] + "</span>" |
2716 | - new_desc += desc[self.node_desc_link_bounds[len(self.node_desc_link_bounds) - 1][1]:] |
2717 | - if self.node_desc_link_bounds[len(self.node_desc_link_bounds) - 1][1] == len(desc): |
2718 | - new_desc += " " |
2719 | - desc = new_desc |
2720 | - |
2721 | - self.node_desc.get_buffer().set_text(desc) |
2722 | - |
2723 | - return |
2724 | - |
2725 | - def update_node_attrs(self): |
2726 | - """ |
2727 | - Update the RHS attributes widget. |
2728 | - """ |
2729 | - |
2730 | - self.node_attrs.get_model().clear() |
2731 | - |
2732 | - if self.selected_node is None: |
2733 | - self.node_attrs.get_column(2).set_property("visible", False) |
2734 | - self.node_attrs.get_column(0).queue_resize() |
2735 | - self.node_attrs.get_column(1).queue_resize() |
2736 | - elif len(self.selected_node.attrs.keys()) == 0: |
2737 | - self.gui.get_widget("attributeFrame").set_property("visible", False) |
2738 | - else: |
2739 | - self.gui.get_widget("attributeFrame").set_property("visible", True) |
2740 | - for key in self.selected_node.attrs.keys(): |
2741 | - iter = self.node_attrs.get_model().append() |
2742 | - self.node_attrs.get_model().set_value(iter, 0, key) |
2743 | - cell_model = gtk.ListStore(gobject.TYPE_STRING) |
2744 | - self.node_attrs.get_model().set_value(iter, 2, cell_model) |
2745 | - if isinstance(self.selected_node.attrs[key][0], tuple): |
2746 | - if self.selected_node.attrs[key][1] is None: |
2747 | - if isinstance(self.selected_node.attrs[key][0][0], tuple): |
2748 | - self.node_attrs.get_model().set_value(iter, 1, "Select " + self.printable_type(self.selected_node.attrs[key][0][1]) + "...") |
2749 | - else: |
2750 | - self.node_attrs.get_model().set_value(iter, 1, "Select...") |
2751 | - else: |
2752 | - self.node_attrs.get_model().set_value(iter, 1, self.selected_node.attrs[key][1]) |
2753 | - if isinstance(self.selected_node.attrs[key][0][0], tuple): |
2754 | - opts = self.selected_node.attrs[key][0][0] |
2755 | - else: |
2756 | - opts = self.selected_node.attrs[key][0] |
2757 | - for opt in opts: |
2758 | - cell_iter = cell_model.append() |
2759 | - cell_model.set_value(cell_iter, 0, opt) |
2760 | - self.node_attrs.get_column(2).set_property("visible", True) |
2761 | - elif self.selected_node.attrs[key][0] is None: |
2762 | - self.node_attrs.get_model().set_value(iter, 1, "No data") |
2763 | - elif self.selected_node.attrs[key][1] is None: |
2764 | - self.node_attrs.get_model().set_value(iter, 1, self.printable_type(self.selected_node.attrs[key][0])) |
2765 | - else: |
2766 | - self.node_attrs.get_model().set_value(iter, 1, self.selected_node.attrs[key][1]) |
2767 | - self.node_attrs.get_column(0).queue_resize() |
2768 | - self.node_attrs.get_column(1).queue_resize() |
2769 | - self.node_attrs.get_column(2).queue_resize() |
2770 | - |
2771 | - return |
2772 | - |
2773 | - def node_attrs_mouse_over(self, widget, event): |
2774 | - """ |
2775 | - Called when the mouse moves over the node attributes widget. Sets the |
2776 | - appropriate attribute widget tooltip. |
2777 | - """ |
2778 | - |
2779 | - path_info = self.node_attrs.get_path_at_pos(int(event.x), int(event.y)) |
2780 | - if path_info is None: |
2781 | - try: |
2782 | - self.node_attrs.set_tooltip_text("") |
2783 | - self.node_attrs.set_property("has-tooltip", False) |
2784 | - except: |
2785 | - pass |
2786 | - return |
2787 | - |
2788 | - path = path_info[0] |
2789 | - col = path_info[1] |
2790 | - if col is not self.node_attrs.get_column(1): |
2791 | - try: |
2792 | - self.node_attrs.set_tooltip_text("") |
2793 | - self.node_attrs.set_property("has-tooltip", False) |
2794 | - except: |
2795 | - pass |
2796 | - return |
2797 | - |
2798 | - iter = self.node_attrs.get_model().get_iter(path) |
2799 | - iter_key = self.node_attrs.get_model().get_value(iter, 0) |
2800 | - |
2801 | - return |
2802 | - |
2803 | - def node_attrs_key_data_func(self, col, cell_renderer, model, iter): |
2804 | - """ |
2805 | - Attribute name data function. Sets the cell renderer text colours. |
2806 | - """ |
2807 | - |
2808 | - iter_key = model.get_value(iter, 0) |
2809 | - |
2810 | - if not self.selected_node.active or self.selected_node.attrs[iter_key][0] is None or self.selected_node.attrs[iter_key][0] == "fixed": |
2811 | - cell_renderer.set_property("foreground", "grey") |
2812 | - elif self.selected_node.attrs[iter_key][1] is None: |
2813 | - cell_renderer.set_property("foreground", "blue") |
2814 | - else: |
2815 | - cell_renderer.set_property("foreground", "black") |
2816 | - |
2817 | - return |
2818 | - |
2819 | - def node_attrs_entry_data_func(self, col, cell_renderer, model, iter): |
2820 | - """ |
2821 | - Attribute text data function. Hides the renderer if a combo box is required, |
2822 | - and sets colours and editability otherwise. |
2823 | - """ |
2824 | - |
2825 | - iter_key = model.get_value(iter, 0) |
2826 | - |
2827 | - if not self.selected_node.active or self.selected_node.attrs[iter_key][0] is None or self.selected_node.attrs[iter_key][0] == "fixed": |
2828 | - cell_renderer.set_property("editable", False) |
2829 | - cell_renderer.set_property("foreground", "grey") |
2830 | - cell_renderer.set_property("visible", True) |
2831 | - elif not isinstance(self.selected_node.attrs[iter_key][0], tuple): |
2832 | - cell_renderer.set_property("editable", True) |
2833 | - cell_renderer.set_property("visible", True) |
2834 | - if self.selected_node.attrs[iter_key][1] is None: |
2835 | - cell_renderer.set_property("foreground", "blue") |
2836 | - else: |
2837 | - cell_renderer.set_property("foreground", "black") |
2838 | - else: |
2839 | - cell_renderer.set_property("editable", False) |
2840 | - cell_renderer.set_property("visible", False) |
2841 | - |
2842 | - return |
2843 | - |
2844 | - def node_attrs_combo_data_func(self, col, cell_renderer, model, iter): |
2845 | - """ |
2846 | - Attribute combo box data function. Hides the renderer if a combo box is not |
2847 | - required, and sets the combo box options otherwise. Adds an entry if required. |
2848 | - """ |
2849 | - |
2850 | - iter_key = model.get_value(iter, 0) |
2851 | - |
2852 | - if self.selected_node.active and isinstance(self.selected_node.attrs[iter_key][0], tuple): |
2853 | - cell_renderer.set_property("editable", True) |
2854 | - cell_renderer.set_property("visible", True) |
2855 | - if isinstance(self.selected_node.attrs[iter_key][0][0], tuple): |
2856 | - cell_renderer.set_property("has-entry", True) |
2857 | - else: |
2858 | - cell_renderer.set_property("has-entry", False) |
2859 | - if self.selected_node.attrs[iter_key][1] is None: |
2860 | - cell_renderer.set_property("foreground", "blue") |
2861 | - else: |
2862 | - cell_renderer.set_property("foreground", "black") |
2863 | - else: |
2864 | - cell_renderer.set_property("visible", False) |
2865 | - cell_renderer.set_property("editable", False) |
2866 | - cell_renderer.set_property("model", model.get_value(iter, 2)) |
2867 | - |
2868 | - return |
2869 | - |
2870 | - def node_attrs_icon_data_func(self, col, cell_renderer, model, iter): |
2871 | - """ |
2872 | - Attribute icon data function. Used to add downward pointing arrows for combo |
2873 | - attributes, for consistency with the LHS. |
2874 | - """ |
2875 | - |
2876 | - iter_key = model.get_value(iter, 0) |
2877 | - |
2878 | - if self.selected_node.active and isinstance(self.selected_node.attrs[iter_key][0], tuple): |
2879 | - cell_renderer.set_property("stock-id", gtk.STOCK_GO_DOWN) |
2880 | - else: |
2881 | - cell_renderer.set_property("stock-id", None) |
2882 | - |
2883 | - return |
2884 | - |
2885 | - def node_attrs_entry_edit_start(self, cell_renderer, editable, path): |
2886 | - """ |
2887 | - Called when editing is started on an attribute text cell. Used to delete the |
2888 | - printable_type placeholder. |
2889 | - """ |
2890 | - |
2891 | - iter = self.node_attrs.get_model().get_iter(path) |
2892 | - iter_key = self.node_attrs.get_model().get_value(iter, 0) |
2893 | - |
2894 | - if self.selected_node.attrs[iter_key][1] is None: |
2895 | - editable.set_text("") |
2896 | - |
2897 | - return |
2898 | - |
2899 | - def node_attrs_combo_edit_start(self, cell_renderer, editable, path): |
2900 | - """ |
2901 | - Called when editing is started on an attribute combo cell. Used to delete the |
2902 | - select placeholder for mixed entry / combo attributes. |
2903 | - """ |
2904 | - |
2905 | - iter = self.node_attrs.get_model().get_iter(path) |
2906 | - iter_key = self.node_attrs.get_model().get_value(iter, 0) |
2907 | - |
2908 | - if isinstance(self.selected_node.attrs[iter_key][0][0], tuple) and self.selected_node.attrs[iter_key][1] is None: |
2909 | - editable.child.set_text("") |
2910 | - |
2911 | - return |
2912 | - |
2913 | - def node_attrs_edited(self, cell_renderer, path, new_text): |
2914 | - """ |
2915 | - Called when editing is finished on an attribute text cell. Updates data in the |
2916 | - treestore. |
2917 | - """ |
2918 | - |
2919 | - iter = self.node_attrs.get_model().get_iter(path) |
2920 | - iter_key = self.node_attrs.get_model().get_value(iter, 0) |
2921 | - |
2922 | - if self.selected_node.get_attr(iter_key) is None and new_text == "": |
2923 | - return |
2924 | - |
2925 | - value_check = self.validity_check(new_text, self.selected_node.attrs[iter_key][0]) |
2926 | - |
2927 | - if not value_check is None and not value_check == self.selected_node.attrs[iter_key][1]: |
2928 | - if iter_key == "name" and not self.name_check(value_check): |
2929 | - return |
2930 | - |
2931 | - self.node_attrs.get_model().set_value(iter, 1, value_check) |
2932 | - self.selected_node.set_attr(iter_key, value_check) |
2933 | - self.paint_validity() |
2934 | - if iter_key == "name": |
2935 | - self.update_painted_name() |
2936 | - self.on_select_row() |
2937 | - self.set_saved(False) |
2938 | - |
2939 | - return |
2940 | - |
2941 | - def node_attrs_selected(self, cell_renderer, path, new_text): |
2942 | - """ |
2943 | - Called when an attribute combo box element is selected, or combo box entry |
2944 | - element entry is edited. Updates data in the treestore. |
2945 | - """ |
2946 | - |
2947 | - iter = self.node_attrs.get_model().get_iter(path) |
2948 | - iter_key = self.node_attrs.get_model().get_value(iter, 0) |
2949 | - |
2950 | - if new_text is None: |
2951 | - return |
2952 | - |
2953 | - if isinstance(self.selected_node.attrs[iter_key][0][0], tuple) and not new_text in self.selected_node.attrs[iter_key][0][0]: |
2954 | - if self.selected_node.get_attr(iter_key) is None and new_text == "": |
2955 | - return |
2956 | - |
2957 | - new_text = self.validity_check(new_text, self.selected_node.attrs[iter_key][0][1]) |
2958 | - if iter_key == "name" and not self.name_check(new_text): |
2959 | - return False |
2960 | - if not new_text == self.selected_node.attrs[iter_key][1]: |
2961 | - self.node_attrs.get_model().set_value(iter, 1, new_text) |
2962 | - self.selected_node.set_attr(iter_key, new_text) |
2963 | - self.paint_validity() |
2964 | - if iter_key == "name": |
2965 | - self.update_painted_name() |
2966 | - self.set_saved(False) |
2967 | - |
2968 | - return |
2969 | - |
2970 | - def set_node_data_empty(self): |
2971 | - """ |
2972 | - Empty the node data frame. |
2973 | - """ |
2974 | - |
2975 | - if len(self.node_data_frame.get_children()) > 1: |
2976 | - if isinstance(self.node_data, gtk.TextView): |
2977 | - self.node_data.handler_block_by_func(self.node_data_entry_focus_in) |
2978 | - elif isinstance(self.node_data, gtk.ComboBox): |
2979 | - self.node_data.handler_block_by_func(self.node_data_combo_focus_child) |
2980 | - self.node_data_frame.remove(self.node_data_frame.child) |
2981 | - |
2982 | - self.node_data_interacted = False |
2983 | - |
2984 | - return |
2985 | - |
2986 | - def set_node_data_entry(self): |
2987 | - """ |
2988 | - Create a text view for data entry in the node data frame. |
2989 | - """ |
2990 | - |
2991 | - self.set_node_data_empty() |
2992 | - |
2993 | - data_scrolled_window = gtk.ScrolledWindow() |
2994 | - self.node_data_frame.add(data_scrolled_window) |
2995 | - data_scrolled_window.show() |
2996 | - |
2997 | - try: |
2998 | - import gtksourceview2 |
2999 | - buf = gtksourceview2.Buffer() |
3000 | - lang_manager = gtksourceview2.LanguageManager() |
3001 | - buf.set_highlight_matching_brackets(True) |
3002 | - if self.node_data_is_python_code(): |
3003 | - python = lang_manager.get_language("python") |
3004 | - buf.set_language(python) |
3005 | - buf.set_highlight_syntax(True) |
3006 | - self.node_data = gtksourceview2.View(buffer=buf) |
3007 | - self.node_data.set_auto_indent(True) |
3008 | - #self.node_data.set_highlight_current_line(True) |
3009 | - self.node_data.set_insert_spaces_instead_of_tabs(True) |
3010 | - self.node_data.set_tab_width(2) |
3011 | - if self.node_data_is_python_code(): |
3012 | - self.node_data.set_show_line_numbers(True) |
3013 | - font_desc = pango.FontDescription("monospace") |
3014 | - if font_desc: |
3015 | - self.node_data.modify_font(font_desc) |
3016 | - except ImportError: |
3017 | - self.node_data = gtk.TextView() |
3018 | - |
3019 | - data_scrolled_window.add(self.node_data) |
3020 | - self.node_data.show() |
3021 | - |
3022 | - data_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
3023 | - |
3024 | - self.node_data.set_pixels_above_lines(2) |
3025 | - self.node_data.set_pixels_below_lines(2) |
3026 | - self.node_data.set_wrap_mode(gtk.WRAP_WORD) |
3027 | - |
3028 | - self.node_data.connect("focus-in-event", self.node_data_entry_focus_in) |
3029 | - |
3030 | - data_frame_packing = self.node_data_frame.get_property("parent").query_child_packing(self.node_data_frame) |
3031 | - self.node_data_frame.get_property("parent").set_child_packing(self.node_data_frame, True, data_frame_packing[1], data_frame_packing[2], data_frame_packing[3]) |
3032 | - |
3033 | - self.node_data.get_buffer().create_tag("node_data_buffer_tag") |
3034 | - text_tag = self.node_data.get_buffer().get_tag_table().lookup("node_data_buffer_tag") |
3035 | - if self.selected_node is None: |
3036 | - self.node_data.set_cursor_visible(False) |
3037 | - self.node_data.set_editable(False) |
3038 | - self.node_data_buttons_hbox.hide() |
3039 | - self.node_data.get_buffer().set_text("") |
3040 | - text_tag.set_property("foreground", "grey") |
3041 | - elif not self.selected_node.active: |
3042 | - self.node_data.set_cursor_visible(False) |
3043 | - self.node_data.set_editable(False) |
3044 | - self.node_data_buttons_hbox.hide() |
3045 | - self.node_data.get_buffer().set_text("Inactive node") |
3046 | - text_tag.set_property("foreground", "grey") |
3047 | - elif self.selected_node.datatype is None: |
3048 | - self.node_data.set_cursor_visible(False) |
3049 | - self.node_data.set_editable(False) |
3050 | - self.node_data_buttons_hbox.hide() |
3051 | - self.node_data.get_buffer().set_text("No data") |
3052 | - text_tag.set_property("foreground", "grey") |
3053 | - elif self.node_data_is_tensor(): |
3054 | - self.node_data.set_cursor_visible(False) |
3055 | - self.node_data.set_editable(False) |
3056 | - self.node_data_buttons_hbox.hide() |
3057 | - self.node_data.get_buffer().set_text("Dimension not set") |
3058 | - text_tag.set_property("foreground", "grey") |
3059 | - elif self.selected_node.data is None: |
3060 | - self.node_data.set_cursor_visible(True) |
3061 | - self.node_data.set_editable(True) |
3062 | - self.node_data_buttons_hbox.show() |
3063 | - self.node_data.get_buffer().set_text(self.printable_type(self.selected_node.datatype)) |
3064 | - text_tag.set_property("foreground", "blue") |
3065 | - else: |
3066 | - self.node_data.get_buffer().set_text(self.selected_node.data) |
3067 | - if self.selected_node.datatype == "fixed": |
3068 | - self.node_data.set_cursor_visible(False) |
3069 | - self.node_data.set_editable(False) |
3070 | - self.node_data_buttons_hbox.hide() |
3071 | - text_tag.set_property("foreground", "grey") |
3072 | - else: |
3073 | - self.node_data.set_cursor_visible(True) |
3074 | - self.node_data.set_editable(True) |
3075 | - self.node_data_buttons_hbox.show() |
3076 | - #text_tag.set_property("foreground", "black") |
3077 | - buffer_bounds = self.node_data.get_buffer().get_bounds() |
3078 | - self.node_data.get_buffer().apply_tag(text_tag, buffer_bounds[0], buffer_bounds[1]) |
3079 | - |
3080 | - return |
3081 | - |
3082 | - def set_node_data_tensor(self): |
3083 | - """ |
3084 | - Create a table container packed with appropriate widgets for tensor data entry |
3085 | - in the node data frame. |
3086 | - """ |
3087 | - |
3088 | - self.set_node_data_empty() |
3089 | - |
3090 | - data_scrolled_window = gtk.ScrolledWindow() |
3091 | - self.node_data_frame.add(data_scrolled_window) |
3092 | - data_scrolled_window.show() |
3093 | - |
3094 | - data_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
3095 | - |
3096 | - dim1, dim2 = self.node_data_tensor_shape() |
3097 | - self.node_data = gtk.Table(dim1, dim2) |
3098 | - data_scrolled_window.add_with_viewport(self.node_data) |
3099 | - self.node_data.show() |
3100 | - |
3101 | - data_scrolled_window.child.set_property("shadow-type", gtk.SHADOW_NONE) |
3102 | - |
3103 | - data_frame_packing = self.node_data_frame.get_property("parent").query_child_packing(self.node_data_frame) |
3104 | - self.node_data_frame.get_property("parent").set_child_packing(self.node_data_frame, True, data_frame_packing[1], data_frame_packing[2], data_frame_packing[3]) |
3105 | - |
3106 | - self.node_data_buttons_hbox.show() |
3107 | - |
3108 | - is_symmetric = self.node_data_is_symmetric_tensor() |
3109 | - for i in range(dim1): |
3110 | - for j in range(dim2): |
3111 | - entry = gtk.Entry() |
3112 | - self.node_data.attach(entry, dim2 - j - 1, dim2 - j, dim1 - i - 1, dim1 - i) |
3113 | - if not is_symmetric or i >= j: |
3114 | - entry.show() |
3115 | - |
3116 | - entry.connect("focus-in-event", self.node_data_tensor_element_focus_in, dim2 - j - 1, dim1 - i - 1) |
3117 | - |
3118 | - if self.selected_node.data is None: |
3119 | - entry.set_text(self.printable_type(self.selected_node.datatype.datatype)) |
3120 | - entry.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("blue")) |
3121 | - else: |
3122 | - entry.set_text(self.selected_node.data.split(" ")[(dim2 - j - 1) + (dim1 - i - 1) * dim2]) |
3123 | - |
3124 | - self.node_data_interacted = [False for i in range(dim1 * dim2)] |
3125 | - |
3126 | - return |
3127 | - |
3128 | - def set_node_data_combo(self): |
3129 | - """ |
3130 | - Create a combo box for node data selection in the node data frame. Add an |
3131 | - entry if required. |
3132 | - """ |
3133 | - |
3134 | - self.set_node_data_empty() |
3135 | - |
3136 | - if isinstance(self.selected_node.datatype[0], tuple): |
3137 | - self.node_data = gtk.combo_box_entry_new_text() |
3138 | - else: |
3139 | - self.node_data = gtk.combo_box_new_text() |
3140 | - self.node_data_frame.add(self.node_data) |
3141 | - self.node_data.show() |
3142 | - |
3143 | - self.node_data.connect("set-focus-child", self.node_data_combo_focus_child) |
3144 | - self.node_data.connect("scroll-event", self.node_data_combo_scroll) |
3145 | - |
3146 | - data_frame_packing = self.node_data_frame.get_property("parent").query_child_packing(self.node_data_frame) |
3147 | - self.node_data_frame.get_property("parent").set_child_packing(self.node_data_frame, False, data_frame_packing[1], data_frame_packing[2], data_frame_packing[3]) |
3148 | - |
3149 | - if isinstance(self.selected_node.datatype[0], tuple): |
3150 | - self.node_data_buttons_hbox.show() |
3151 | - else: |
3152 | - self.node_data_buttons_hbox.hide() |
3153 | - |
3154 | - if self.selected_node.data is None: |
3155 | - if isinstance(self.selected_node.datatype[0], tuple): |
3156 | - self.node_data.child.set_text("Select " + self.printable_type(self.selected_node.datatype[1]) + "...") |
3157 | - else: |
3158 | - self.node_data.append_text("Select...") |
3159 | - self.node_data.set_active(0) |
3160 | - self.node_data.child.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("blue")) |
3161 | - self.node_data.child.modify_text(gtk.STATE_PRELIGHT, gtk.gdk.color_parse("blue")) |
3162 | - |
3163 | - if isinstance(self.selected_node.datatype[0], tuple): |
3164 | - options = self.selected_node.datatype[0] |
3165 | - else: |
3166 | - options = self.selected_node.datatype |
3167 | - for i in range(len(options)): |
3168 | - opt = options[i] |
3169 | - self.node_data.append_text(opt) |
3170 | - if self.selected_node.data == opt: |
3171 | - self.node_data.set_active(i) |
3172 | - |
3173 | - if isinstance(self.selected_node.datatype[0], tuple) and not self.selected_node.data is None and not self.selected_node.data in self.selected_node.datatype[0]: |
3174 | - self.node_data.child.set_text(self.selected_node.data) |
3175 | - |
3176 | - self.node_data.connect("changed", self.node_data_combo_changed) |
3177 | - |
3178 | - return |
3179 | - |
3180 | - def node_data_entry_focus_in(self, widget, event): |
3181 | - """ |
3182 | - Called when a text view data entry widget gains focus. Used to delete the |
3183 | - printable_type placeholder. |
3184 | - """ |
3185 | - |
3186 | - if not self.selected_node is None and not self.selected_node.datatype is None and not self.node_data_is_tensor() and self.selected_node.data is None and not self.node_data_interacted: |
3187 | - self.node_data.get_buffer().set_text("") |
3188 | - |
3189 | - self.node_data_interacted = True |
3190 | - |
3191 | - return |
3192 | - |
3193 | - def node_data_tensor_element_focus_in(self, widget, event, row, col): |
3194 | - """ |
3195 | - Called when a tensor data entry widget gains focus. Used to delete the |
3196 | - printable_type placeholder. |
3197 | - """ |
3198 | - |
3199 | - dim1, dim2 = self.node_data_tensor_shape() |
3200 | - if not self.node_data_interacted[col + row * dim2]: |
3201 | - self.node_data_interacted[col + row * dim2] = True |
3202 | - if self.node_data_is_symmetric_tensor(): |
3203 | - self.node_data_interacted[row + col * dim1] = True |
3204 | - if self.selected_node.data is None: |
3205 | - widget.set_text("") |
3206 | - widget.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) |
3207 | - |
3208 | - return |
3209 | - |
3210 | - def node_data_combo_focus_child(self, container, widget): |
3211 | - """ |
3212 | - Called when a data selection widget gains focus. Used to delete the select |
3213 | - placeholder. |
3214 | - """ |
3215 | - |
3216 | - if not self.node_data_interacted: |
3217 | - self.node_data_interacted = True |
3218 | - if self.selected_node.data is None: |
3219 | - self.node_data_interacted = True |
3220 | - if isinstance(self.selected_node.datatype[0], tuple): |
3221 | - self.node_data.handler_block_by_func(self.node_data_combo_changed) |
3222 | - self.node_data.child.set_text("") |
3223 | - self.node_data.handler_unblock_by_func(self.node_data_combo_changed) |
3224 | - else: |
3225 | - self.node_data.set_active(1) |
3226 | - self.node_data.remove_text(0) |
3227 | - self.node_data.child.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) |
3228 | - self.node_data.child.modify_text(gtk.STATE_PRELIGHT, gtk.gdk.color_parse("black")) |
3229 | - |
3230 | - return |
3231 | - |
3232 | - def node_data_combo_changed(self, combo_box): |
3233 | - """ |
3234 | - Called when a data combo box element is selected. Updates data in the |
3235 | - treestore. |
3236 | - """ |
3237 | - |
3238 | - if not isinstance(self.selected_node.datatype[0], tuple) or not self.node_data.child.get_property("has-focus"): |
3239 | - self.selected_node.set_data(self.node_data.get_active_text()) |
3240 | - self.paint_validity() |
3241 | - self.set_saved(False) |
3242 | - self.node_data_interacted = False |
3243 | - iter = self.get_treeview_iter(self.treeview.get_selection()) |
3244 | - self.treestore.set_value(iter, 4, self.node_data.get_active_text()) |
3245 | - |
3246 | - return |
3247 | - |
3248 | - def node_data_combo_scroll(self, widget, event): |
3249 | - """ |
3250 | - Called when the data combo box is scrolled with the mouse wheel. Removes the |
3251 | - select placeholder and updates data in the treestore. |
3252 | - """ |
3253 | - |
3254 | - self.node_data_combo_focus_child(self.node_data_frame, self.node_data) |
3255 | - if not isinstance(self.selected_node.datatype[0], tuple) or not self.selected_node.data is None: |
3256 | - self.node_data_combo_changed(self.node_data) |
3257 | - |
3258 | - return |
3259 | - |
3260 | - def node_data_revert(self, button = None): |
3261 | - """ |
3262 | - "Revert Data" button click signal handler. Reverts data in the node data frame. |
3263 | - """ |
3264 | - |
3265 | - if self.node_data_is_tensor() and not self.geometry_dim_tree.data is None: |
3266 | - self.set_node_data_tensor() |
3267 | - elif not self.selected_node is None and isinstance(self.selected_node.datatype, tuple): |
3268 | - self.set_node_data_combo() |
3269 | - else: |
3270 | - self.set_node_data_entry() |
3271 | - |
3272 | - return |
3273 | - |
3274 | - def node_data_store(self, button = None): |
3275 | - """ |
3276 | - "Store Data" button click signal handler. Stores data from the node data frame |
3277 | - in the treestore. |
3278 | - """ |
3279 | - |
3280 | - if self.node_data_is_tensor() and not self.geometry_dim_tree.data is None: |
3281 | - store_success = self.node_data_tensor_store() |
3282 | - elif not self.selected_node is None and isinstance(self.selected_node.datatype, tuple): |
3283 | - store_success = self.node_data_combo_store() |
3284 | - else: |
3285 | - store_success = self.node_data_entry_store() |
3286 | - |
3287 | - if self.scherror.errlist_is_open(): |
3288 | - if self.scherror.errlist_type == 0: |
3289 | - self.scherror.on_validate_schematron() |
3290 | - else: |
3291 | - self.scherror.on_validate() |
3292 | - |
3293 | - return store_success |
3294 | - |
3295 | - def node_data_entry_store(self): |
3296 | - """ |
3297 | - Attempt to store data read from a textview packed in the node data frame. |
3298 | - """ |
3299 | - |
3300 | - if self.selected_node is None or self.selected_node.datatype in ["fixed", None] or self.node_data_is_tensor(): |
3301 | - return True |
3302 | - |
3303 | - data_buffer_bounds = self.node_data.get_buffer().get_bounds() |
3304 | - new_data = self.node_data.get_buffer().get_text(data_buffer_bounds[0], data_buffer_bounds[1]) |
3305 | - |
3306 | - if new_data == "": |
3307 | - return True |
3308 | - |
3309 | - if self.selected_node.data is None and not self.node_data_interacted: |
3310 | - return True |
3311 | - else: |
3312 | - value_check = self.validity_check(new_data, self.selected_node.datatype) |
3313 | - if value_check is None: |
3314 | - return False |
3315 | - elif not value_check == self.selected_node.data: |
3316 | - self.selected_node.set_data(value_check) |
3317 | - if isinstance(self.selected_node, MixedTree) and "shape" in self.selected_node.child.attrs.keys() and self.selected_node.child.attrs["shape"][0] is int and isinstance(self.selected_node.datatype, plist.List) and self.selected_node.datatype.cardinality == "+": |
3318 | - self.selected_node.child.set_attr("shape", str(len(value_check.split(" ")))) |
3319 | - self.paint_validity() |
3320 | - |
3321 | - iter = self.selected_iter |
3322 | - self.treestore.set_value(iter, 4, new_data) |
3323 | - self.set_saved(False) |
3324 | - self.node_data_interacted = False |
3325 | - |
3326 | - return True |
3327 | - |
3328 | - def node_data_combo_store(self): |
3329 | - """ |
3330 | - Attempt to store data read from a combo box entry packed in the node data |
3331 | - frame. |
3332 | - """ |
3333 | - |
3334 | - if not isinstance(self.selected_node.datatype[0], tuple): |
3335 | - return True |
3336 | - |
3337 | - new_data = self.node_data.child.get_text() |
3338 | - |
3339 | - if self.selected_node.data is None and not self.node_data_interacted: |
3340 | - return True |
3341 | - elif not new_data in self.selected_node.datatype[0]: |
3342 | - new_data = self.validity_check(new_data, self.selected_node.datatype[1]) |
3343 | - if new_data is None: |
3344 | - return False |
3345 | - |
3346 | - if not new_data == self.selected_node.data: |
3347 | - self.selected_node.set_data(new_data) |
3348 | - self.paint_validity() |
3349 | - self.set_saved(False) |
3350 | - self.node_data_interacted = False |
3351 | - |
3352 | - return True |
3353 | - |
3354 | - def node_data_tensor_store(self): |
3355 | - """ |
3356 | - Attempt to store data read from tensor data entry widgets packed in the node |
3357 | - data frame. |
3358 | - """ |
3359 | - |
3360 | - dim1, dim2 = self.node_data_tensor_shape() |
3361 | - is_symmetric = self.node_data_is_symmetric_tensor() |
3362 | - |
3363 | - if not True in self.node_data_interacted: |
3364 | - return True |
3365 | - |
3366 | - entry_values = [] |
3367 | - for i in range(dim1): |
3368 | - for j in range(dim2): |
3369 | - if is_symmetric and i > j: |
3370 | - entry_values.append(self.node_data.get_children()[i + j * dim1].get_text()) |
3371 | - else: |
3372 | - entry_values.append(self.node_data.get_children()[j + i * dim2].get_text()) |
3373 | - |
3374 | - changed = False |
3375 | - for i in range(dim1): |
3376 | - for j in range(dim2): |
3377 | - if self.node_data_interacted[j + i * dim2] and not entry_values[j + i * dim2] == "" and (self.selected_node.data is None or not self.selected_node.data.split(" ")[j + i * dim2] == entry_values[j + i * dim2]): |
3378 | - changed = True |
3379 | - if not changed: |
3380 | - return True |
3381 | - elif (self.selected_node.data is None and False in self.node_data_interacted) or "" in entry_values: |
3382 | - dialogs.error(self.main_window, "Invalid value entered") |
3383 | - return False |
3384 | - |
3385 | - new_data = "" |
3386 | - for i in range(dim1): |
3387 | - for j in range(dim2): |
3388 | - new_data += " " + entry_values[j + i * dim2] |
3389 | - |
3390 | - value_check = self.validity_check(new_data, self.selected_node.datatype) |
3391 | - if value_check is None: |
3392 | - return False |
3393 | - elif not value_check == self.selected_node.data: |
3394 | - self.selected_node.set_data(value_check) |
3395 | - |
3396 | - dim1, dim2 = self.node_data_tensor_shape() |
3397 | - if int(self.selected_node.child.attrs["rank"][1]) == 1: |
3398 | - self.selected_node.child.set_attr("shape", str(dim1)) |
3399 | - else: |
3400 | - self.selected_node.child.set_attr("shape", str(dim1) + " " + str(dim2)) |
3401 | - |
3402 | - self.paint_validity() |
3403 | - self.set_saved(False) |
3404 | - self.node_data_interacted = [False for i in range(dim1 * dim2)] |
3405 | - |
3406 | - return True |
3407 | - |
3408 | - def update_node_comment(self): |
3409 | - """ |
3410 | - Update the comment widget. |
3411 | - """ |
3412 | - |
3413 | - if self.selected_node is None or not self.selected_node.active: |
3414 | - self.node_comment.get_buffer().set_text("") |
3415 | - self.node_comment.set_cursor_visible(False) |
3416 | - self.node_comment.set_editable(False) |
3417 | - try: |
3418 | - self.node_comment.set_tooltip_text("") |
3419 | - self.node_comment.set_property("has-tooltip", False) |
3420 | - except: |
3421 | - pass |
3422 | - |
3423 | - return |
3424 | - |
3425 | - comment_tree = self.get_comment(self.selected_node) |
3426 | - text_tag = self.node_comment.get_buffer().get_tag_table().lookup("comment_buffer_tag") |
3427 | - if comment_tree is None: |
3428 | - self.node_comment.get_buffer().set_text("No comment") |
3429 | - self.node_comment.set_cursor_visible(False) |
3430 | - self.node_comment.set_editable(False) |
3431 | - text_tag.set_property("foreground", "grey") |
3432 | - try: |
3433 | - self.node_comment.set_tooltip_text("") |
3434 | - self.node_comment.set_property("has-tooltip", False) |
3435 | - except: |
3436 | - pass |
3437 | - else: |
3438 | - if comment_tree.data is None: |
3439 | - self.node_comment.get_buffer().set_text("(string)") |
3440 | - else: |
3441 | - self.node_comment.get_buffer().set_text(comment_tree.data) |
3442 | - if self.selected_node.active: |
3443 | - self.node_comment.set_cursor_visible(True) |
3444 | - self.node_comment.set_editable(True) |
3445 | - text_tag.set_property("foreground", "black") |
3446 | - else: |
3447 | - self.node_comment.set_cursor_visible(False) |
3448 | - self.node_comment.set_editable(False) |
3449 | - text_tag.set_property("foreground", "grey") |
3450 | - |
3451 | - buffer_bounds = self.node_comment.get_buffer().get_bounds() |
3452 | - self.node_comment.get_buffer().apply_tag(text_tag, buffer_bounds[0], buffer_bounds[1]) |
3453 | - |
3454 | - self.node_comment_interacted = False |
3455 | - |
3456 | - return |
3457 | - |
3458 | - def node_comment_focus_in(self, widget, event): |
3459 | - """ |
3460 | - Called when the comment widget gains focus. Removes the printable_type |
3461 | - placeholder. |
3462 | - """ |
3463 | - |
3464 | - comment_tree = self.get_comment(self.selected_node) |
3465 | - if not comment_tree is None and not self.node_comment_interacted: |
3466 | - self.node_comment_interacted = True |
3467 | - if comment_tree.data is None: |
3468 | - self.node_comment.get_buffer().set_text("") |
3469 | - |
3470 | - return |
3471 | - |
3472 | - def node_comment_expose(self, widget, event): |
3473 | - """ |
3474 | - Called when the comment widget is repainted. Stores the comment if required. |
3475 | - """ |
3476 | - |
3477 | - self.node_comment_store() |
3478 | - |
3479 | - return |
3480 | - |
3481 | - def node_comment_store(self): |
3482 | - """ |
3483 | - Store data in the node comment. |
3484 | - """ |
3485 | - |
3486 | - comment_tree = self.get_comment(self.selected_node) |
3487 | - if comment_tree is None or not self.node_comment_interacted: |
3488 | - return |
3489 | - |
3490 | - data_buffer_bounds = self.node_comment.get_buffer().get_bounds() |
3491 | - new_comment = self.node_comment.get_buffer().get_text(data_buffer_bounds[0], data_buffer_bounds[1]) |
3492 | - |
3493 | - if not new_comment == comment_tree.data: |
3494 | - if new_comment == "": |
3495 | - comment_tree.data = None |
3496 | - comment_tree.active = False |
3497 | - else: |
3498 | - comment_tree.set_data(new_comment) |
3499 | - comment_tree.active = True |
3500 | - self.set_saved(False) |
3501 | - |
3502 | - return |
3503 | - |
3504 | - def validity_check(self, val, val_type): |
3505 | - """ |
3506 | - Check to see if the supplied data with supplied type can be stored in a |
3507 | - tree.Tree. |
3508 | - """ |
3509 | - |
3510 | - (invalid, data) = self.selected_node.valid_data(val_type, val) |
3511 | - if not invalid and isinstance(data, str) and not data == "": |
3512 | - if not data == val and self.validity_check(data, val_type) is None: |
3513 | - return None |
3514 | - else: |
3515 | - return data |
3516 | - else: |
3517 | - dialogs.error(self.main_window, "Invalid value entered") |
3518 | - return None |
3519 | - |
3520 | - def name_check(self, val): |
3521 | - """ |
3522 | - Check to see if the supplied data is a valid tree name. |
3523 | - """ |
3524 | - |
3525 | - valid_chars = "_:[]1234567890qwertyuioplkjhgfdsazxcvbnmMNBVCXZASDFGHJKLPOIUYTREWQ" |
3526 | - for char in val: |
3527 | - if not char in valid_chars: |
3528 | - dialogs.error(self.main_window, "Invalid value entered") |
3529 | - return False |
3530 | - |
3531 | - return True |
3532 | - |
3533 | - def node_data_is_python_code(self): |
3534 | - """ |
3535 | - Perform a series of tests on the current tree.Tree / MixedTree, to determine if |
3536 | - it is intended to be used to store python code data. |
3537 | - """ |
3538 | - |
3539 | - try: |
3540 | - lang = self.selected_node.get_attr("language") |
3541 | - if lang == "python": |
3542 | - return True |
3543 | - except: |
3544 | - pass |
3545 | - |
3546 | - if not isinstance(self.selected_node, MixedTree): |
3547 | - return False |
3548 | - |
3549 | - if self.selected_node.datatype is not str: |
3550 | - return False |
3551 | - |
3552 | - if "type" in self.selected_node.child.attrs.keys(): |
3553 | - return self.selected_node.child.attrs["type"][1] == "python" |
3554 | - else: |
3555 | - return False |
3556 | - |
3557 | - def node_data_is_tensor(self): |
3558 | - """ |
3559 | - Perform a series of tests on the current tree.Tree / MixedTree, to determine if |
3560 | - it is intended to be used to store tensor or vector data. |
3561 | - """ |
3562 | - |
3563 | - # Check that a geometry is defined |
3564 | - if self.geometry_dim_tree is None: |
3565 | - return False |
3566 | - |
3567 | - # Check that this element has calculable and positive dimensions |
3568 | - if isinstance(self.geometry_dim_tree.datatype, tuple): |
3569 | - possible_dims = self.geometry_dim_tree.datatype |
3570 | - else: |
3571 | - possible_dims = [self.geometry_dim_tree.data] |
3572 | - for opt in possible_dims: |
3573 | - try: |
3574 | - dim1, dim2 = self.node_data_tensor_shape(int(opt)) |
3575 | - assert dim1 > 0 |
3576 | - assert dim2 > 0 |
3577 | - except: |
3578 | - return False |
3579 | - |
3580 | - # All tensor elements must be of MixedTree type |
3581 | - if not isinstance(self.selected_node, MixedTree): |
3582 | - return False |
3583 | - |
3584 | - # The element must have dim1, rank and shape attributes |
3585 | - if not "dim1" in self.selected_node.child.attrs.keys() or not "rank" in self.selected_node.child.attrs.keys() or not "shape" in self.selected_node.child.attrs.keys(): |
3586 | - return False |
3587 | - # The dim1 and rank attributes must be of fixed type |
3588 | - if not self.selected_node.child.attrs["dim1"][0] == "fixed" or not self.selected_node.child.attrs["rank"][0] == "fixed": |
3589 | - return False |
3590 | - |
3591 | - if "dim2" in self.selected_node.child.attrs.keys(): |
3592 | - # If a dim2 attribute is specified, it must be of fixed type and the rank must be 2 |
3593 | - # Also, the shape attribute must be a list of integers with cardinality equal to the rank |
3594 | - if not self.selected_node.child.attrs["dim2"][0] == "fixed" or not self.selected_node.child.attrs["rank"][1] == "2" or not isinstance(self.selected_node.child.attrs["shape"][0], plist.List) or not self.selected_node.child.attrs["shape"][0].datatype is int or not str(self.selected_node.child.attrs["shape"][0].cardinality) == self.selected_node.child.attrs["rank"][1]: |
3595 | - return False |
3596 | - # Otherwise, the rank must be one and the shape an integer |
3597 | - elif not self.selected_node.child.attrs["rank"][1] == "1" or not self.selected_node.child.attrs["shape"][0] is int: |
3598 | - return False |
3599 | - |
3600 | - # The data for the element must be a list of one or more |
3601 | - if not isinstance(self.selected_node.datatype, plist.List) or not self.selected_node.datatype.cardinality == "+": |
3602 | - return False |
3603 | - |
3604 | - # If the shape has been set, check that it has a valid value |
3605 | - if not self.selected_node.child.attrs["shape"][1] == None: |
3606 | - if self.geometry_dim_tree.data is None: |
3607 | - return False |
3608 | - |
3609 | - dim1, dim2 = self.node_data_tensor_shape() |
3610 | - if "dim2" in self.selected_node.child.attrs.keys(): |
3611 | - if not self.selected_node.child.attrs["shape"][1] == str(dim1) + " " + str(dim2): |
3612 | - return False |
3613 | - elif not self.selected_node.child.attrs["shape"][1] == str(dim1): |
3614 | - return False |
3615 | - |
3616 | - return True |
3617 | - |
3618 | - def node_data_tensor_shape(self, geometry_dim = None): |
3619 | - """ |
3620 | - Read the tensor shape for tensor or vector data in the current MixedTree. |
3621 | - """ |
3622 | - |
3623 | - if geometry_dim == None: |
3624 | - geometry_dim = int(self.geometry_dim_tree.data) |
3625 | - |
3626 | - dim1 = 1 |
3627 | - dim2 = 1 |
3628 | - if "dim1" in self.selected_node.child.attrs.keys(): |
3629 | - dim1 = int(eval(self.selected_node.child.attrs["dim1"][1], {"dim":geometry_dim})) |
3630 | - if "dim2" in self.selected_node.child.attrs.keys(): |
3631 | - dim2 = int(eval(self.selected_node.child.attrs["dim2"][1], {"dim":geometry_dim})) |
3632 | - |
3633 | - return (dim1, dim2) |
3634 | - |
3635 | - def node_data_is_symmetric_tensor(self): |
3636 | - """ |
3637 | - Read if the tensor data in the current MixedTree is symmetric. |
3638 | - """ |
3639 | - |
3640 | - dim1, dim2 = self.node_data_tensor_shape() |
3641 | - if not dim1 == dim2: |
3642 | - return False |
3643 | - |
3644 | - if "symmetric" in self.selected_node.child.attrs.keys() and self.selected_node.child.attrs["symmetric"][1] == "true": |
3645 | - return True |
3646 | - else: |
3647 | - return False |
3648 | - |
3649 | -class MixedTree: |
3650 | - def __init__(self, parent, child): |
3651 | - """ |
3652 | - The .doc and .attrs comes from parent, and the .data comes from child. This |
3653 | - is used to hide integer_value etc. from the left hand side, but for its data |
3654 | - entry to show up on the right. |
3655 | - """ |
3656 | - |
3657 | - self.parent = parent |
3658 | - self.child = child |
3659 | - |
3660 | - self.name = parent.name |
3661 | - self.schemaname = parent.schemaname |
3662 | - self.attrs = self.parent.attrs |
3663 | - self.children = parent.children |
3664 | - self.datatype = child.datatype |
3665 | - self.data = child.data |
3666 | - self.doc = parent.doc |
3667 | - self.active = parent.active |
3668 | - |
3669 | - return |
3670 | - |
3671 | - def set_attr(self, attr, val): |
3672 | - self.parent.set_attr(attr, val) |
3673 | - |
3674 | - return |
3675 | - |
3676 | - def get_attr(self, attr): |
3677 | - return self.parent.get_attr(attr) |
3678 | - |
3679 | - def set_data(self, data): |
3680 | - self.child.set_data(data) |
3681 | - self.datatype = self.child.datatype |
3682 | - self.data = self.child.data |
3683 | - |
3684 | - return |
3685 | - |
3686 | - def valid_data(self, datatype, data): |
3687 | - return self.parent.valid_data(datatype, data) |
3688 | - |
3689 | - def matches(self, text, case_sensitive = False): |
3690 | - old_parent_data = self.parent.data |
3691 | - self.parent.data = None |
3692 | - parent_matches = self.parent.matches(text, case_sensitive) |
3693 | - self.parent.data = old_parent_data |
3694 | - |
3695 | - if parent_matches: |
3696 | - return True |
3697 | - |
3698 | - if case_sensitive: |
3699 | - text_re = re.compile(text) |
3700 | - else: |
3701 | - text_re = re.compile(text, re.IGNORECASE) |
3702 | - |
3703 | - if not self.child.data is None and not text_re.search(self.child.data) is None: |
3704 | - return True |
3705 | - else: |
3706 | - return False |
3707 | - |
3708 | class DiamondFindDialog: |
3709 | def __init__(self, parent, gladefile): |
3710 | self.parent = parent |
3711 | |
3712 | === added file 'diamond/diamond/mixedtree.py' |
3713 | --- diamond/diamond/mixedtree.py 1970-01-01 00:00:00 +0000 |
3714 | +++ diamond/diamond/mixedtree.py 2011-07-25 13:56:22 +0000 |
3715 | @@ -0,0 +1,204 @@ |
3716 | +#!/usr/bin/env python |
3717 | + |
3718 | +# This file is part of Diamond. |
3719 | +# |
3720 | +# Diamond is free software: you can redistribute it and/or modify |
3721 | +# it under the terms of the GNU General Public License as published by |
3722 | +# the Free Software Foundation, either version 3 of the License, or |
3723 | +# (at your option) any later version. |
3724 | +# |
3725 | +# Diamond is distributed in the hope that it will be useful, |
3726 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3727 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3728 | +# GNU General Public License for more details. |
3729 | +# |
3730 | +# You should have received a copy of the GNU General Public License |
3731 | +# along with Diamond. If not, see <http://www.gnu.org/licenses/>. |
3732 | + |
3733 | +import plist |
3734 | + |
3735 | +class MixedTree: |
3736 | + def __init__(self, parent, child): |
3737 | + """ |
3738 | + The .doc and .attrs comes from parent, and the .data comes from child. This |
3739 | + is used to hide integer_value etc. from the left hand side, but for its data |
3740 | + entry to show up on the right. |
3741 | + """ |
3742 | + |
3743 | + self.parent = parent |
3744 | + self.child = child |
3745 | + |
3746 | + self.name = parent.name |
3747 | + self.schemaname = parent.schemaname |
3748 | + self.attrs = self.parent.attrs |
3749 | + self.children = parent.children |
3750 | + self.datatype = child.datatype |
3751 | + self.data = child.data |
3752 | + self.doc = parent.doc |
3753 | + self.active = parent.active |
3754 | + |
3755 | + return |
3756 | + |
3757 | + def set_attr(self, attr, val): |
3758 | + self.parent.set_attr(attr, val) |
3759 | + |
3760 | + return |
3761 | + |
3762 | + def get_attr(self, attr): |
3763 | + return self.parent.get_attr(attr) |
3764 | + |
3765 | + def set_data(self, data): |
3766 | + self.child.set_data(data) |
3767 | + self.datatype = self.child.datatype |
3768 | + self.data = self.child.data |
3769 | + |
3770 | + return |
3771 | + |
3772 | + def valid_data(self, datatype, data): |
3773 | + return self.parent.valid_data(datatype, data) |
3774 | + |
3775 | + def validity_check(self, datatype, data): |
3776 | + return self.parent.validity_check(datatype, data) |
3777 | + |
3778 | + def matches(self, text, case_sensitive = False): |
3779 | + old_parent_data = self.parent.data |
3780 | + self.parent.data = None |
3781 | + parent_matches = self.parent.matches(text, case_sensitive) |
3782 | + self.parent.data = old_parent_data |
3783 | + |
3784 | + if parent_matches: |
3785 | + return True |
3786 | + |
3787 | + if case_sensitive: |
3788 | + text_re = re.compile(text) |
3789 | + else: |
3790 | + text_re = re.compile(text, re.IGNORECASE) |
3791 | + |
3792 | + if not self.child.data is None and not text_re.search(self.child.data) is None: |
3793 | + return True |
3794 | + else: |
3795 | + return False |
3796 | + |
3797 | + def is_comment(self): |
3798 | + return self.parent.is_comment() |
3799 | + |
3800 | + def get_comment(self): |
3801 | + return self.parent.get_comment() |
3802 | + |
3803 | + def is_tensor(self, geometry_dim_tree): |
3804 | + """ |
3805 | + Perform a series of tests on the current MixedTree, to determine if |
3806 | + it is intended to be used to store tensor or vector data. |
3807 | + """ |
3808 | + |
3809 | + # Check that a geometry is defined |
3810 | + if geometry_dim_tree is None: |
3811 | + return False |
3812 | + |
3813 | + # Check that this element has calculable and positive dimensions |
3814 | + if isinstance(geometry_dim_tree.datatype, tuple): |
3815 | + possible_dims = geometry_dim_tree.datatype |
3816 | + else: |
3817 | + possible_dims = [geometry_dim_tree.data] |
3818 | + for opt in possible_dims: |
3819 | + try: |
3820 | + dim1, dim2 = self.tensor_shape(int(opt)) |
3821 | + assert dim1 > 0 |
3822 | + assert dim2 > 0 |
3823 | + except: |
3824 | + return False |
3825 | + |
3826 | + # The element must have dim1, rank and shape attributes |
3827 | + if "dim1" not in self.child.attrs.keys() \ |
3828 | + or "rank" not in self.child.attrs.keys() \ |
3829 | + or "shape" not in self.child.attrs.keys(): |
3830 | + return False |
3831 | + |
3832 | + # The dim1 and rank attributes must be of fixed type |
3833 | + if self.child.attrs["dim1"][0] != "fixed" or self.child.attrs["rank"][0] != "fixed": |
3834 | + return False |
3835 | + |
3836 | + if "dim2" in self.child.attrs.keys(): |
3837 | + # If a dim2 attribute is specified, it must be of fixed type and the rank must be 2 |
3838 | + # Also, the shape attribute must be a list of integers with cardinality equal to the rank |
3839 | + if self.child.attrs["dim2"][0] != "fixed" \ |
3840 | + or self.child.attrs["rank"][1] != "2" \ |
3841 | + or not isinstance(self.child.attrs["shape"][0], plist.List) \ |
3842 | + or self.child.attrs["shape"][0].datatype is not int \ |
3843 | + or str(self.child.attrs["shape"][0].cardinality) != self.child.attrs["rank"][1]: |
3844 | + return False |
3845 | + |
3846 | + # Otherwise, the rank must be one and the shape an integer |
3847 | + elif self.child.attrs["rank"][1] != "1" or self.child.attrs["shape"][0] is not int: |
3848 | + return False |
3849 | + |
3850 | + # The data for the element must be a list of one or more |
3851 | + if not isinstance(self.datatype, plist.List) or self.datatype.cardinality != "+": |
3852 | + return False |
3853 | + |
3854 | + # If the shape has been set, check that it has a valid value |
3855 | + if self.child.attrs["shape"][1] != None: |
3856 | + if geometry_dim_tree.data is None: |
3857 | + return False |
3858 | + |
3859 | + dim1, dim2 = self.tensor_shape() |
3860 | + if "dim2" in self.child.attrs.keys(): |
3861 | + if self.child.attrs["shape"][1] != str(dim1) + " " + str(dim2): |
3862 | + return False |
3863 | + elif self.child.attrs["shape"][1] != str(dim1): |
3864 | + return False |
3865 | + |
3866 | + return True |
3867 | + |
3868 | + def tensor_shape(self, dimension): |
3869 | + """ |
3870 | + Read the tensor shape for tensor or vector data in the current MixedTree. |
3871 | + """ |
3872 | + |
3873 | + if not isinstance(dimension, int): |
3874 | + dimension = int(dimension.data) |
3875 | + |
3876 | + dim1 = 1 |
3877 | + dim2 = 1 |
3878 | + if "dim1" in self.child.attrs.keys(): |
3879 | + dim1 = int(eval(self.child.attrs["dim1"][1], {"dim":dimension})) |
3880 | + if "dim2" in self.child.attrs.keys(): |
3881 | + dim2 = int(eval(self.child.attrs["dim2"][1], {"dim":dimension})) |
3882 | + |
3883 | + return (dim1, dim2) |
3884 | + |
3885 | + def is_symmetric_tensor(self, geometry): |
3886 | + """ |
3887 | + Read if the tensor data in the current MixedTree is symmetric. |
3888 | + """ |
3889 | + |
3890 | + dim1, dim2 = self.tensor_shape(geometry) |
3891 | + |
3892 | + return dim1 == dim2 and "symmetric" in self.child.attrs.keys() and self.child.attrs["symmetric"][1] == "true" |
3893 | + |
3894 | + def is_python_code(self): |
3895 | + """ |
3896 | + Perform a series of tests on the current MixedTree, to determine if |
3897 | + it is intended to be used to store python code data. |
3898 | + """ |
3899 | + |
3900 | + try: |
3901 | + lang = self.get_attr("language") |
3902 | + if lang == "python": |
3903 | + return True |
3904 | + except: |
3905 | + pass |
3906 | + |
3907 | + if self.datatype is not str: |
3908 | + return False |
3909 | + |
3910 | + if "type" in self.child.attrs.keys(): |
3911 | + return self.child.attrs["type"][1] == "python" |
3912 | + |
3913 | + return False |
3914 | + |
3915 | + def get_name_path(self, leaf = True): |
3916 | + return self.parent.get_name_path(leaf) |
3917 | + |
3918 | + def is_sliceable(self): |
3919 | + return True |
3920 | |
3921 | === modified file 'diamond/diamond/schema.py' |
3922 | --- diamond/diamond/schema.py 2011-07-08 17:15:57 +0000 |
3923 | +++ diamond/diamond/schema.py 2011-07-25 13:56:22 +0000 |
3924 | @@ -160,7 +160,7 @@ |
3925 | node = xpath[0] |
3926 | |
3927 | node = self.to_tree(node) |
3928 | - |
3929 | + |
3930 | if eidtree is not None: |
3931 | if eidtree.parent is not None: |
3932 | eidtree.parent.children.append(node) |
3933 | |
3934 | === added file 'diamond/diamond/sliceview.py' |
3935 | --- diamond/diamond/sliceview.py 1970-01-01 00:00:00 +0000 |
3936 | +++ diamond/diamond/sliceview.py 2011-07-25 13:56:22 +0000 |
3937 | @@ -0,0 +1,117 @@ |
3938 | +#!/usr/bin/env python |
3939 | + |
3940 | +# This file is part of Diamond. |
3941 | +# |
3942 | +# Diamond is free software: you can redistribute it and/or modify |
3943 | +# it under the terms of the GNU General Public License as published by |
3944 | +# the Free Software Foundation, either version 3 of the License, or |
3945 | +# (at your option) any later version. |
3946 | +# |
3947 | +# Diamond is distributed in the hope that it will be useful, |
3948 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3949 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3950 | +# GNU General Public License for more details. |
3951 | +# |
3952 | +# You should have received a copy of the GNU General Public License |
3953 | +# along with Diamond. If not, see <http://www.gnu.org/licenses/>. |
3954 | + |
3955 | +import gobject |
3956 | +import gtk |
3957 | + |
3958 | +import attributewidget |
3959 | +import databuttonswidget |
3960 | +import datawidget |
3961 | +import mixedtree |
3962 | + |
3963 | +class SliceView(gtk.Window): |
3964 | + |
3965 | + __gsignals__ = { "on-store" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), |
3966 | + "update-name" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())} |
3967 | + |
3968 | + def __init__(self, parent): |
3969 | + gtk.Window.__init__(self) |
3970 | + |
3971 | + self.set_default_size(800, 600) |
3972 | + self.set_title("Slice View") |
3973 | + self.set_modal(True) |
3974 | + self.set_transient_for(parent) |
3975 | + |
3976 | + mainvbox = gtk.VBox() |
3977 | + self.vbox = gtk.VBox() |
3978 | + |
3979 | + scrolledWindow = gtk.ScrolledWindow() |
3980 | + scrolledWindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) |
3981 | + scrolledWindow.add_with_viewport(self.vbox) |
3982 | + |
3983 | + self.databuttons = databuttonswidget.DataButtonsWidget() |
3984 | + |
3985 | + self.statusbar = gtk.Statusbar() |
3986 | + |
3987 | + mainvbox.pack_start(scrolledWindow, True) |
3988 | + mainvbox.pack_start(self.databuttons, False) |
3989 | + mainvbox.pack_start(self.statusbar, False) |
3990 | + |
3991 | + self.add(mainvbox) |
3992 | + self.show_all() |
3993 | + |
3994 | + def update(self, node, tree): |
3995 | + nodes = self.get_nodes(node, tree) |
3996 | + if not nodes: |
3997 | + self.destroy() |
3998 | + |
3999 | + for n in nodes: |
4000 | + self.vbox.pack_start(self.control(n)) |
4001 | + |
4002 | + maxwidth = 0 |
4003 | + for child in self.vbox.get_children(): |
4004 | + width, height = child.label.get_size_request() |
4005 | + maxwidth = max(maxwidth, width) |
4006 | + |
4007 | + for child in self.vbox.get_children(): |
4008 | + child.label.set_size_request(maxwidth, -1) |
4009 | + |
4010 | + self.check_resize() |
4011 | + |
4012 | + def get_nodes(self, node, tree): |
4013 | + nodes = [] |
4014 | + |
4015 | + for child in tree.get_children(): |
4016 | + if child.active: |
4017 | + if child.name == node.name and child.is_sliceable(): |
4018 | + nodes.append(child.get_mixed_data()) |
4019 | + nodes += self.get_nodes(node, child) |
4020 | + |
4021 | + return nodes |
4022 | + |
4023 | + def control(self, node): |
4024 | + hbox = gtk.HBox() |
4025 | + |
4026 | + label = gtk.Label(node.get_name_path()) |
4027 | + hbox.label = label |
4028 | + |
4029 | + data = datawidget.DataWidget() |
4030 | + data.geometry_dim_tree = self.geometry_dim_tree |
4031 | + data.connect("on-store", self.on_store) |
4032 | + data.set_buttons(self.databuttons) |
4033 | + data.update(node) |
4034 | + |
4035 | + attributes = attributewidget.AttributeWidget() |
4036 | + attributes.connect("on-store", self.on_store) |
4037 | + attributes.connect("update-name", self.update_name) |
4038 | + attributes.update(node) |
4039 | + |
4040 | + hbox.pack_start(label) |
4041 | + hbox.pack_start(data) |
4042 | + hbox.pack_start(attributes) |
4043 | + |
4044 | + hbox.show_all() |
4045 | + |
4046 | + return hbox |
4047 | + |
4048 | + def on_store(self, widget = None): |
4049 | + self.emit("on-store") |
4050 | + |
4051 | + def update_name(self, widget = None): |
4052 | + self.emit("update-name") |
4053 | + |
4054 | +gobject.type_register(SliceView) |
4055 | |
4056 | === modified file 'diamond/diamond/tree.py' |
4057 | --- diamond/diamond/tree.py 2011-07-07 12:38:32 +0000 |
4058 | +++ diamond/diamond/tree.py 2011-07-25 13:56:22 +0000 |
4059 | @@ -1,4 +1,4 @@ |
4060 | -#!/usr/bin/env python |
4061 | +#/usr/bin/env python |
4062 | |
4063 | # This file is part of Diamond. |
4064 | # |
4065 | @@ -25,18 +25,26 @@ |
4066 | from lxml import etree |
4067 | import sys |
4068 | |
4069 | +import gobject |
4070 | + |
4071 | import debug |
4072 | import choice |
4073 | +import mixedtree |
4074 | |
4075 | -class Tree: |
4076 | +class Tree(gobject.GObject): |
4077 | """This class maps pretty much 1-to-1 with an xml tree. |
4078 | It is used to represent the options in-core.""" |
4079 | |
4080 | + __gsignals__ = { "on-set-data" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (str,)), |
4081 | + "on-set-attr" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (str, str))} |
4082 | + |
4083 | def __init__(self, name="", schemaname="", attrs={}, children=None, cardinality='', datatype=None, doc=None): |
4084 | + gobject.GObject.__init__(self) |
4085 | + |
4086 | # name: the element name in the options XML |
4087 | # e.g. "fluidity_options" |
4088 | self.name = name |
4089 | - |
4090 | + |
4091 | # schemaname: the label given to it in the Xvif parsing of the schema |
4092 | # this is necessary to walk the tree to see what possible valid |
4093 | # children this node could have |
4094 | @@ -99,6 +107,7 @@ |
4095 | raise Exception, "invalid data: (%s, %s)" % (datatype, val) |
4096 | self.attrs[attr] = (datatype, newdata) |
4097 | self.recompute_validity() |
4098 | + self.emit("on-set-attr", attr, val) |
4099 | |
4100 | def get_attr(self, attr): |
4101 | """Get an attribute.""" |
4102 | @@ -111,6 +120,7 @@ |
4103 | raise Exception, "invalid data: (%s, %s)" % (str(self.datatype), data) |
4104 | self.data = data |
4105 | self.recompute_validity() |
4106 | + self.emit("on-set-data", data) |
4107 | |
4108 | def valid_data(self, datatype, data): |
4109 | if datatype is None: |
4110 | @@ -146,19 +156,31 @@ |
4111 | |
4112 | return (True, data) |
4113 | |
4114 | + def validity_check(self, datatype, data): |
4115 | + """ |
4116 | + Check to see if the supplied data with supplied type can be stored in a |
4117 | + tree.Tree. |
4118 | + """ |
4119 | + |
4120 | + (invalid, new_data) = self.valid_data(datatype, data) |
4121 | + if not invalid and isinstance(new_data, str) and not new_data == "": |
4122 | + if new_data != data and self.validity_check(new_data, datatype) is None: |
4123 | + return None |
4124 | + else: |
4125 | + return new_data |
4126 | + else: |
4127 | + return None |
4128 | + |
4129 | def copy(self): |
4130 | new_copy = Tree() |
4131 | - for attr in ["attrs", "name", "schemaname", "doc", "cardinality", "datatype", "parent", "active", "valid"]: |
4132 | + for attr in ["attrs", "name", "schemaname", "doc", "cardinality", "datatype", "data", "active", "valid"]: |
4133 | setattr(new_copy, attr, copy.copy(getattr(self, attr))) |
4134 | |
4135 | - new_copy.data = self.data |
4136 | - new_copy.children = copy.copy([]) |
4137 | + new_copy.parent = self.parent |
4138 | + new_copy.children = [] |
4139 | |
4140 | return new_copy |
4141 | |
4142 | - def not_editable(self): |
4143 | - return not self.active or self.datatype is None or self.datatype == "fixed" |
4144 | - |
4145 | def recompute_validity(self): |
4146 | |
4147 | new_valid = True |
4148 | @@ -330,7 +352,7 @@ |
4149 | for i in range(len(self.children)): |
4150 | if isinstance(self.children[i], Tree): |
4151 | self.children[i].print_recursively(indent + ">>") |
4152 | - elif isinstance(self.children[i], Choice): |
4153 | + elif isinstance(self.children[i], choice.Choice): |
4154 | ref = self.children[i].get_current_tree() |
4155 | ref.print_recursively(indent + ">>") |
4156 | if i < len(self.children) - 1: |
4157 | @@ -387,3 +409,135 @@ |
4158 | |
4159 | def choices(self): |
4160 | return [self] |
4161 | + |
4162 | + def is_comment(self): |
4163 | + """ |
4164 | + Test whether the given node is a comment node. |
4165 | + """ |
4166 | + |
4167 | + if not self.name == "comment": |
4168 | + return False |
4169 | + |
4170 | + if not self.attrs == {}: |
4171 | + return False |
4172 | + |
4173 | + if not self.children == []: |
4174 | + return False |
4175 | + |
4176 | + if not self.datatype is str: |
4177 | + return False |
4178 | + |
4179 | + if not self.cardinality == "?": |
4180 | + return False |
4181 | + |
4182 | + return True |
4183 | + |
4184 | + def get_comment(self): |
4185 | + """ |
4186 | + Return the first comment found as a child of the supplied node, or None if |
4187 | + none found. |
4188 | + """ |
4189 | + |
4190 | + for child in self.children: |
4191 | + if child.is_comment(): |
4192 | + return child |
4193 | + |
4194 | + return None |
4195 | + |
4196 | + def is_tensor(self, geometry_dim_tree): |
4197 | + return False |
4198 | + |
4199 | + def is_python_code(self): |
4200 | + """ |
4201 | + Perform a series of tests on the current Tree, to determine if |
4202 | + it is intended to be used to store python code data. |
4203 | + """ |
4204 | + |
4205 | + try: |
4206 | + lang = self.selected_node.get_attr("language") |
4207 | + if lang == "python": |
4208 | + return True |
4209 | + except: |
4210 | + pass |
4211 | + |
4212 | + return False |
4213 | + |
4214 | + def get_display_name(self): |
4215 | + """ |
4216 | + This is a fluidity hack, allowing the name displayed in the treeview on the |
4217 | + left to be different to the element name. If it has an attribute name="xxx", |
4218 | + element_tag (xxx) is displayed. |
4219 | + """ |
4220 | + |
4221 | + name = self.get_name() |
4222 | + |
4223 | + if name is None: |
4224 | + return self.name |
4225 | + else: |
4226 | + return self.name + " (" + name + ")" |
4227 | + |
4228 | + def get_name(self): |
4229 | + if "name" in self.attrs: |
4230 | + name = self.attrs["name"][1] |
4231 | + return name |
4232 | + |
4233 | + return None |
4234 | + |
4235 | + def get_children(self): |
4236 | + return self.children |
4237 | + |
4238 | + def get_choices(self): |
4239 | + return [self] |
4240 | + |
4241 | + def is_hidden(self): |
4242 | + """ |
4243 | + Tests whether the supplied tree should be hidden in view. |
4244 | + """ |
4245 | + return self.is_comment() or self.name in ["integer_value", "real_value", "string_value", "logical_value"] |
4246 | + |
4247 | + def get_name_path(self, leaf = True): |
4248 | + name = self.get_display_name() if leaf else self.get_name() |
4249 | + |
4250 | + if self.parent is None: |
4251 | + return name |
4252 | + else: |
4253 | + |
4254 | + pname = self.parent.get_name_path(False) |
4255 | + |
4256 | + if name is None: |
4257 | + return pname |
4258 | + elif pname is None: |
4259 | + return name |
4260 | + else: |
4261 | + return pname + "/" + name |
4262 | + |
4263 | + def get_mixed_data(self): |
4264 | + integers = [child for child in self.children if child.name == "integer_value"] |
4265 | + reals = [child for child in self.children if child.name == "real_value"] |
4266 | + logicals = [child for child in self.children if child.name == "logical_value"] |
4267 | + strings = [child for child in self.children if child.name == "string_value"] |
4268 | + |
4269 | + child = None |
4270 | + if len(integers) > 0: |
4271 | + child = integers[0] |
4272 | + if len(reals) > 0: |
4273 | + child = reals[0] |
4274 | + if len(logicals) > 0: |
4275 | + child = logicals[0] |
4276 | + if len(strings) > 0: |
4277 | + child = strings[0] |
4278 | + |
4279 | + if child is None: |
4280 | + return self |
4281 | + else: |
4282 | + return mixedtree.MixedTree(self, child) |
4283 | + |
4284 | + def is_sliceable(self): |
4285 | + mixed = self.get_mixed_data() |
4286 | + if isinstance(mixed, mixedtree.MixedTree): |
4287 | + return True |
4288 | + |
4289 | + return (self.datatype is not None and self.datatype != "fixed") or self.attrs |
4290 | + |
4291 | +gobject.type_register(Tree) |
4292 | + |
4293 | |
4294 | === modified file 'diamond/gui/gui.glade' |
4295 | --- diamond/gui/gui.glade 2011-07-20 17:19:44 +0000 |
4296 | +++ diamond/gui/gui.glade 2011-07-25 13:56:22 +0000 |
4297 | @@ -290,225 +290,6 @@ |
4298 | <property name="label_xalign">0</property> |
4299 | <property name="shadow_type">none</property> |
4300 | <child> |
4301 | - <widget class="GtkAlignment" id="optionsFrameAlignment"> |
4302 | - <property name="visible">True</property> |
4303 | - <property name="xalign">0</property> |
4304 | - <property name="yalign">0</property> |
4305 | - <property name="top_padding">6</property> |
4306 | - <property name="left_padding">12</property> |
4307 | - <child> |
4308 | - <widget class="GtkVPaned" id="optionsFrameVPanes1"> |
4309 | - <property name="visible">True</property> |
4310 | - <property name="can_focus">True</property> |
4311 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4312 | - <child> |
4313 | - <widget class="GtkFrame" id="descriptionFrame"> |
4314 | - <property name="visible">True</property> |
4315 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4316 | - <property name="label_xalign">0</property> |
4317 | - <property name="shadow_type">none</property> |
4318 | - <child> |
4319 | - <widget class="GtkScrolledWindow" id="descriptionFrameScrolledWindow"> |
4320 | - <property name="visible">True</property> |
4321 | - <property name="can_focus">True</property> |
4322 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4323 | - <property name="hscrollbar_policy">automatic</property> |
4324 | - <property name="vscrollbar_policy">automatic</property> |
4325 | - <child> |
4326 | - <widget class="GtkTextView" id="nodeDescription"> |
4327 | - <property name="visible">True</property> |
4328 | - <property name="can_focus">True</property> |
4329 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4330 | - <property name="editable">False</property> |
4331 | - <property name="wrap_mode">word</property> |
4332 | - <property name="cursor_visible">False</property> |
4333 | - </widget> |
4334 | - </child> |
4335 | - </widget> |
4336 | - </child> |
4337 | - <child> |
4338 | - <widget class="GtkLabel" id="descriptionFrameLabel"> |
4339 | - <property name="visible">True</property> |
4340 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4341 | - <property name="label" translatable="yes"><b>Description</b></property> |
4342 | - <property name="use_markup">True</property> |
4343 | - </widget> |
4344 | - <packing> |
4345 | - <property name="type">label_item</property> |
4346 | - </packing> |
4347 | - </child> |
4348 | - </widget> |
4349 | - <packing> |
4350 | - <property name="resize">True</property> |
4351 | - <property name="shrink">False</property> |
4352 | - </packing> |
4353 | - </child> |
4354 | - <child> |
4355 | - <widget class="GtkVPaned" id="optionsFrameVPanes2"> |
4356 | - <property name="visible">True</property> |
4357 | - <property name="can_focus">True</property> |
4358 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4359 | - <child> |
4360 | - <widget class="GtkVBox" id="attributeDataVBox"> |
4361 | - <property name="visible">True</property> |
4362 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4363 | - <child> |
4364 | - <widget class="GtkFrame" id="attributeFrame"> |
4365 | - <property name="visible">True</property> |
4366 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4367 | - <property name="label_xalign">0</property> |
4368 | - <property name="shadow_type">none</property> |
4369 | - <child> |
4370 | - <widget class="GtkScrolledWindow" id="attributeFrameScrolledWindow"> |
4371 | - <property name="visible">True</property> |
4372 | - <property name="can_focus">True</property> |
4373 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4374 | - <property name="hscrollbar_policy">automatic</property> |
4375 | - <property name="vscrollbar_policy">automatic</property> |
4376 | - <child> |
4377 | - <widget class="GtkTreeView" id="nodeAttributes"> |
4378 | - <property name="visible">True</property> |
4379 | - <property name="can_focus">True</property> |
4380 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4381 | - </widget> |
4382 | - </child> |
4383 | - </widget> |
4384 | - </child> |
4385 | - <child> |
4386 | - <widget class="GtkLabel" id="attributeFrameLabel"> |
4387 | - <property name="visible">True</property> |
4388 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4389 | - <property name="label" translatable="yes"><b>Attributes</b></property> |
4390 | - <property name="use_markup">True</property> |
4391 | - </widget> |
4392 | - <packing> |
4393 | - <property name="type">label_item</property> |
4394 | - </packing> |
4395 | - </child> |
4396 | - </widget> |
4397 | - <packing> |
4398 | - <property name="expand">False</property> |
4399 | - <property name="fill">False</property> |
4400 | - <property name="padding">2</property> |
4401 | - <property name="position">0</property> |
4402 | - </packing> |
4403 | - </child> |
4404 | - <child> |
4405 | - <widget class="GtkFrame" id="dataFrame"> |
4406 | - <property name="visible">True</property> |
4407 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4408 | - <property name="label_xalign">0</property> |
4409 | - <property name="shadow_type">none</property> |
4410 | - <child> |
4411 | - <widget class="GtkLabel" id="dataFrameLabel"> |
4412 | - <property name="visible">True</property> |
4413 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4414 | - <property name="label" translatable="yes"><b>Data</b></property> |
4415 | - <property name="use_markup">True</property> |
4416 | - </widget> |
4417 | - <packing> |
4418 | - <property name="type">label_item</property> |
4419 | - </packing> |
4420 | - </child> |
4421 | - </widget> |
4422 | - <packing> |
4423 | - <property name="padding">2</property> |
4424 | - <property name="position">1</property> |
4425 | - </packing> |
4426 | - </child> |
4427 | - <child> |
4428 | - <widget class="GtkHBox" id="dataButtonsHBox"> |
4429 | - <property name="visible">True</property> |
4430 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4431 | - <child> |
4432 | - <widget class="GtkButton" id="dataRevertButton"> |
4433 | - <property name="label" translatable="yes">Revert Data</property> |
4434 | - <property name="visible">True</property> |
4435 | - <property name="can_focus">True</property> |
4436 | - <property name="receives_default">True</property> |
4437 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4438 | - </widget> |
4439 | - <packing> |
4440 | - <property name="position">0</property> |
4441 | - </packing> |
4442 | - </child> |
4443 | - <child> |
4444 | - <widget class="GtkButton" id="dataStoreButton"> |
4445 | - <property name="label" translatable="yes">Store Data</property> |
4446 | - <property name="visible">True</property> |
4447 | - <property name="can_focus">True</property> |
4448 | - <property name="receives_default">True</property> |
4449 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4450 | - </widget> |
4451 | - <packing> |
4452 | - <property name="position">1</property> |
4453 | - </packing> |
4454 | - </child> |
4455 | - </widget> |
4456 | - <packing> |
4457 | - <property name="expand">False</property> |
4458 | - <property name="position">2</property> |
4459 | - </packing> |
4460 | - </child> |
4461 | - </widget> |
4462 | - <packing> |
4463 | - <property name="resize">True</property> |
4464 | - <property name="shrink">False</property> |
4465 | - </packing> |
4466 | - </child> |
4467 | - <child> |
4468 | - <widget class="GtkFrame" id="commentFrame"> |
4469 | - <property name="visible">True</property> |
4470 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4471 | - <property name="label_xalign">0</property> |
4472 | - <property name="shadow_type">none</property> |
4473 | - <child> |
4474 | - <widget class="GtkScrolledWindow" id="commentFrameScrolledWindow"> |
4475 | - <property name="visible">True</property> |
4476 | - <property name="can_focus">True</property> |
4477 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4478 | - <property name="hscrollbar_policy">automatic</property> |
4479 | - <property name="vscrollbar_policy">automatic</property> |
4480 | - <child> |
4481 | - <widget class="GtkTextView" id="nodeComment"> |
4482 | - <property name="visible">True</property> |
4483 | - <property name="can_focus">True</property> |
4484 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4485 | - <property name="editable">False</property> |
4486 | - <property name="wrap_mode">word</property> |
4487 | - <property name="cursor_visible">False</property> |
4488 | - </widget> |
4489 | - </child> |
4490 | - </widget> |
4491 | - </child> |
4492 | - <child> |
4493 | - <widget class="GtkLabel" id="commentFrameLabel"> |
4494 | - <property name="visible">True</property> |
4495 | - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> |
4496 | - <property name="label" translatable="yes"><b>Comment</b></property> |
4497 | - <property name="use_markup">True</property> |
4498 | - </widget> |
4499 | - <packing> |
4500 | - <property name="type">label_item</property> |
4501 | - </packing> |
4502 | - </child> |
4503 | - </widget> |
4504 | - <packing> |
4505 | - <property name="resize">False</property> |
4506 | - <property name="shrink">False</property> |
4507 | - </packing> |
4508 | - </child> |
4509 | - </widget> |
4510 | - <packing> |
4511 | - <property name="resize">True</property> |
4512 | - <property name="shrink">False</property> |
4513 | - </packing> |
4514 | - </child> |
4515 | - </widget> |
4516 | - </child> |
4517 | - </widget> |
4518 | - </child> |
4519 | - <child> |
4520 | <widget class="GtkLabel" id="optionsFrameLabel"> |
4521 | <property name="visible">True</property> |
4522 | <property name="label" translatable="yes"><b>Option Properties</b></property> |
4523 | @@ -706,7 +487,6 @@ |
4524 | </widget> |
4525 | <widget class="GtkMenu" id="popupmenu"> |
4526 | <property name="visible">True</property> |
4527 | - <property name="ubuntu_local">True</property> |
4528 | <child> |
4529 | <widget class="GtkMenuItem" id="menuitemCopy"> |
4530 | <property name="visible">True</property> |
4531 | @@ -723,5 +503,13 @@ |
4532 | <signal name="activate" handler="on_paste"/> |
4533 | </widget> |
4534 | </child> |
4535 | + <child> |
4536 | + <widget class="GtkMenuItem" id="menuitemSlice"> |
4537 | + <property name="visible">True</property> |
4538 | + <property name="label" translatable="yes">Slice</property> |
4539 | + <property name="use_underline">True</property> |
4540 | + <signal name="activate" handler="on_slice"/> |
4541 | + </widget> |
4542 | + </child> |
4543 | </widget> |
4544 | </glade-interface> |
Fraser and I have gone through several iterations; I have tried to break it, and can't. I've also read through the code changes and it all looks good. I'm going to merge this so that I can push a new diamond package out, so that users can give us bug reports.