35
from GTG import _, info, ngettext
38
36
from GTG.backends.backendsignals import BackendSignals
37
from GTG.core import CoreConfig
38
from GTG.core.search import parse_search_query, SEARCH_COMMANDS, InvalidQuery
39
from GTG.core.task import Task
40
from GTG.gtk.tag_completion import TagCompletion
41
from GTG.gtk.browser import GnomeConfig
39
42
from GTG.gtk.browser.custominfobar import CustomInfoBar
40
from GTG.core import CoreConfig
41
from GTG import _, info, ngettext
42
from GTG.core.task import Task
43
from GTG.gtk.browser import GnomeConfig
43
from GTG.gtk.browser.modifytags_dialog import ModifyTagsDialog
44
from GTG.gtk.browser.tag_context_menu import TagContextMenu
44
45
from GTG.gtk.browser.treeview_factory import TreeviewFactory
45
from GTG.tools import openurl
46
from GTG.tools.dates import Date
47
from GTG.tools.logger import Log
48
from GTG.tools.tags import extract_tags_from_text
49
from GTG.core.search import parse_search_query, search_commands, InvalidQuery
50
#FIXME Why is this commented?
51
#from GTG.tools import clipboard
46
from GTG.tools.dates import Date
47
from GTG.tools.logger import Log
53
49
#=== MAIN CLASS ===============================================================
55
51
WINDOW_TITLE = "Getting Things GNOME!"
56
DOCUMENTATION_URL = "http://live.gnome.org/gtg/documentation"
58
#Some default preferences that we should save in a file
62
def __init__(self,st):
64
def __enter__(self): self.start = time.time()
54
def __init__(self, name):
58
self.start = time.time()
65
60
def __exit__(self, *args):
66
print "%s : %s" %(self.st,time.time() - self.start)
61
print "%s : %s" % (self.name, time.time() - self.start)
69
64
class TaskBrowser(gobject.GObject):
132
123
# Initialize search completion
133
124
self._init_search_completion()
135
# Rember values from last time
136
self.last_added_tags = "NewTag"
137
self.last_apply_tags_to_subtasks = False
139
126
self.restore_state_from_conf()
141
128
self.on_select_tag()
142
129
self.browser_shown = False
144
131
#Update the title when a task change
145
self.activetree.register_cllbck('node-added-inview',self._update_window_title)
146
self.activetree.register_cllbck('node-deleted-inview',self._update_window_title)
132
self.activetree.register_cllbck('node-added-inview', self._update_window_title)
133
self.activetree.register_cllbck('node-deleted-inview', self._update_window_title)
134
self._update_window_title()
148
136
### INIT HELPER FUNCTIONS #####################################################
162
150
defines aliases for UI elements found in the glide file
164
152
self.window = self.builder.get_object("MainWindow")
165
self.tagpopup = self.builder.get_object("tag_context_menu")
166
153
self.searchpopup = self.builder.get_object("search_context_menu")
167
self.nonworkviewtag_cb = self.builder.get_object("nonworkviewtag_mi")
168
self.nonworkviewtag_cb.set_label(GnomeConfig.TAG_IN_WORKVIEW_TOGG)
169
154
self.taskpopup = self.builder.get_object("task_context_menu")
170
155
self.defertopopup = self.builder.get_object("defer_to_context_menu")
171
156
self.ctaskpopup = self.builder.get_object("closed_task_context_menu")
195
180
self.vbox_toolbars = self.builder.get_object("vbox_toolbars")
197
182
self.closed_pane = None
183
self.tagpopup = TagContextMenu(self.req)
199
185
def _init_ui_widget(self):
201
sets the main pane with the tree with active tasks
187
sets the main pane with the tree with active tasks and create ModifyTagsDialog
203
189
# The Active tasks treeview
204
190
self.main_pane.add(self.vtree_panes['active'])
192
tag_completion = TagCompletion(self.req.get_tag_tree())
193
self.modifytags_dialog = ModifyTagsDialog(tag_completion, self.req)
206
195
def init_tags_sidebar(self):
217
206
self.on_select_tag)
218
207
self.tagtreeview.connect('button-press-event',\
219
208
self.on_tag_treeview_button_press_event)
209
self.tagtreeview.connect('key-press-event',\
210
self.on_tag_treeview_key_press_event)
220
211
self.sidebar_container.add(self.tagtreeview)
214
self.tagtree.reset_filters(transparent_only=True)
222
216
# expanding search tag does not work automatically, request it
223
217
self.expand_search_tag()
303
297
self.on_size_allocate,
307
self.on_addtag_confirm,
310
"on_tag_entry_key_press_event":
311
self.on_tag_entry_key_press_event,
312
300
"on_add_subtask":
313
301
self.on_add_subtask,
314
"on_colorchooser_activate":
315
self.on_colorchooser_activate,
316
"on_resetcolor_activate":
317
self.on_resetcolor_activate,
318
302
"on_tagcontext_deactivate":
319
303
self.on_tagcontext_deactivate,
320
304
"on_workview_toggled":
346
328
"on_about_close":
347
329
self.on_about_close,
348
330
"on_documentation_clicked":
349
self.on_documentation_clicked,
350
"on_nonworkviewtag_toggled":
351
self.on_nonworkviewtag_toggled,
331
lambda w: openurl(info.DOCUMENTATION_URL),
332
"on_translate_clicked":
333
lambda w: openurl(info.TRANSLATE_URL),
334
"on_report_bug_clicked":
335
lambda w: openurl(info.REPORT_BUG_URL),
352
336
"on_preferences_activate":
353
337
self.open_preferences,
354
338
"on_edit_backends_activate":
404
388
self._add_accelerator_for_widget(agr, "done_mi", "<Control>d")
405
389
self._add_accelerator_for_widget(agr, "dismiss_mi", "<Control>i")
406
390
self._add_accelerator_for_widget(agr, "delete_mi", "Cancel")
407
self._add_accelerator_for_widget(agr, "tcm_addtag", "<Control>t")
391
self._add_accelerator_for_widget(agr, "tcm_modifytags", "<Control>t")
408
392
self._add_accelerator_for_widget(agr, "view_closed", "<Control>F9")
393
self._add_accelerator_for_widget(agr, "online_help", "F1")
410
395
edit_button = self.builder.get_object("edit_b")
411
396
key, mod = gtk.accelerator_parse("<Control>e")
415
400
key, mod = gtk.accelerator_parse("<Control>l")
416
401
quickadd_field.add_accelerator("grab-focus", agr, key, mod, gtk.ACCEL_VISIBLE)
418
def _init_tag_completion(self):
420
Entry completation for the add tag to a task dialog
422
#Initialize tag completion.
423
tagtree = self.req.get_tag_tree()
424
completion_view = self.tv_factory.tags_completion_treeview(tagtree)
427
self.tag_completion = gtk.EntryCompletion()
428
self.tag_completion.set_model(completion_view.get_model())
429
self.tag_completion.set_text_column(col_num)
430
self.tag_completion.set_match_func(self.tag_match_func, col_num)
431
self.tag_completion.set_inline_completion(True)
432
self.tag_completion.set_inline_selection(True)
433
self.tag_completion.set_popup_single_match(False)
435
403
### HELPER FUNCTIONS ########################################################
437
405
def open_preferences(self, widget):
443
411
def quit(self,widget=None):
444
412
self.vmanager.close_browser()
414
def on_window_state_event(self,widget,event,data=None):
415
"""This event checks for the window state: maximized?
416
and stores the state in self.config.max
417
This is used to check the window state afterwards
418
and maximize it if needed """
419
mask = gtk.gdk.WINDOW_STATE_MAXIMIZED
420
if widget.get_window().get_state() & mask == mask:
421
self.config.set("max", True)
423
self.config.set("max", False)
446
425
def restore_state_from_conf(self):
448
427
# # Extract state from configuration dictionary
603
584
count) % {'tasks': count}
604
585
self.window.set_title("%s - "%parenthesis + WINDOW_TITLE)
606
def tag_match_func(self, completion, key, iter, column):
608
function that defines autocompletation on the add tag dialog
610
model = completion.get_model()
611
text = model.get_value(iter, column)
613
# key is always lowercase => convert text also into lowercase
616
# Exclude the special tags.
617
if text.startswith("<span") or text.startswith('gtg-tags-'):
620
# Compare normalized unicode representation
621
text = unicodedata.normalize('NFC', unicode(text))
622
key = unicodedata.normalize('NFC', unicode(key))
623
return text.startswith(key)
625
587
def _add_page(self, notebook, label, page):
626
588
notebook.append_page(page, label)
627
589
if notebook.get_n_pages() > 1:
693
655
self.about.hide()
696
def on_documentation_clicked(self, widget):
697
webbrowser.open(DOCUMENTATION_URL)
699
def on_color_changed(self, widget):
700
gtkcolor = widget.get_current_color()
701
strcolor = gtk.color_selection_palette_to_string([gtkcolor])
702
tags = self.get_selected_tags()
704
t = self.req.get_tag(tname)
705
t.set_attribute("color", strcolor)
707
def on_colorchooser_activate(self, widget):
708
#TODO: Color chooser should be refactorized in its own class. Well, in
709
#fact we should have a TagPropertiesEditor (like for project) Also,
710
#color change should be immediate. There's no reason for a Ok/Cancel
711
self.set_target_cursor()
712
color_dialog = gtk.ColorSelectionDialog('Choose color')
713
colorsel = color_dialog.colorsel
714
colorsel.connect("color_changed", self.on_color_changed)
717
tags= self.get_selected_tags()
720
ta = self.req.get_tag(tags[0])
721
color = ta.get_attribute("color")
722
if color is not None:
723
colorspec = gtk.gdk.color_parse(color)
724
colorsel.set_previous_color(colorspec)
725
colorsel.set_current_color(colorspec)
726
init_color = colorsel.get_current_color()
727
response = color_dialog.run()
728
# Check response and set color if required
729
if response != gtk.RESPONSE_OK and init_color:
730
strcolor = gtk.color_selection_palette_to_string([init_color])
731
tags = self.get_selected_tags()
733
t.set_attribute("color", strcolor)
735
color_dialog.destroy()
737
def on_resetcolor_activate(self, widget):
739
handler for the right click popup menu item from tag tree, when its a @tag
741
self.set_target_cursor()
742
tags = self.get_selected_tags()
744
t = self.req.get_tag(tname)
745
t.del_attribute("color")
748
658
def on_tagcontext_deactivate(self, menushell):
749
659
self.reset_cursor()
819
729
self.builder.get_object("view_closed").set_active(False)
820
730
self.config.set('closed_task_pane',False)
822
def on_bg_color_toggled(self, widget):
823
if widget.get_active():
824
self.config.set("bg_color_enable",True)
826
self.config.set("bg_color_enable",False)
828
732
def on_toolbar_toggled(self, widget):
829
733
if widget.get_active():
830
734
self.toolbar.show()
940
844
# Then we are looking at single, normal tag rather than
941
845
# the special 'All tags' or 'Tasks without tags'. We only
942
846
# want to popup the menu for normal tags.
944
display_in_workview_item = self.tagpopup.get_children()[2]
945
847
selected_tag = self.req.get_tag(selected_tags[0])
946
nonworkview = selected_tag.get_attribute("nonworkview")
947
# We must invert because the tagstore has "True" for tasks
948
# that are *not* in workview, and the checkbox is set if
949
# the tag *is* shown in the workview.
950
if nonworkview == "True":
954
# HACK: CheckMenuItem.set_active() emits a toggled() when
955
# switching between True and False, which will reset
956
# the cursor. Using self.dont_reset to work around that.
957
# Calling set_target_cursor after set_active() is another
958
# option, but there's noticeable amount of lag when right
959
# clicking tags that way.
960
self.dont_reset = True
961
display_in_workview_item.set_active(shown)
962
self.dont_reset = False
848
self.tagpopup.set_tag(selected_tag)
963
849
self.tagpopup.popup(None, None, None, event.button, time)
965
851
self.reset_cursor()
968
def on_nonworkviewtag_toggled(self, widget):
969
self.set_target_cursor()
970
tag_id = self.get_selected_tags()[0]
971
#We must inverse because the tagstore has True
972
#for tasks that are not in workview (and also convert to string)
973
toset = not self.nonworkviewtag_cb.get_active()
974
tag = self.req.get_tag(tag_id)
975
tag.set_attribute("nonworkview", str(toset))
977
label = GnomeConfig.TAG_NOTIN_WORKVIEW_TOGG
979
label = GnomeConfig.TAG_IN_WORKVIEW_TOGG
980
self.nonworkviewtag_cb.set_label(label)
981
if not self.dont_reset:
854
def on_tag_treeview_key_press_event(self, treeview, event):
855
keyname = gtk.gdk.keyval_name(event.keyval)
856
is_shift_f10 = keyname == "F10" and event.get_state() & gtk.gdk.SHIFT_MASK
857
if is_shift_f10 or keyname == "Menu":
858
selected_tags = self.get_selected_tags(nospecial=True)
859
selected_search = self.get_selected_search()
860
#popup menu for searches
861
if selected_search is not None:
862
self.searchpopup.popup(None, None, None, 0, event.time)
863
elif len(selected_tags) > 0:
864
# Then we are looking at single, normal tag rather than
865
# the special 'All tags' or 'Tasks without tags'. We only
866
# want to popup the menu for normal tags.
867
selected_tag = self.req.get_tag(selected_tags[0])
868
self.tagpopup.set_tag(selected_tag)
869
self.tagpopup.popup(None, None, None, 0, event.time)
984
874
def on_task_treeview_button_press_event(self, treeview, event):
985
875
"""Pop up context menu on right mouse click in the main task tree view"""
999
889
treeview.set_cursor(path, col, 0)
1000
890
treeview.grab_focus()
1001
891
self.taskpopup.popup(None, None, None, event.button, time)
1004
894
def on_task_treeview_key_press_event(self, treeview, event):
1005
if gtk.gdk.keyval_name(event.keyval) == "Delete":
895
keyname = gtk.gdk.keyval_name(event.keyval)
896
is_shift_f10 = keyname == "F10" and event.get_state() & gtk.gdk.SHIFT_MASK
898
if keyname == "Delete":
1006
899
self.on_delete_tasks()
901
elif is_shift_f10 or keyname == "Menu":
902
self.taskpopup.popup(None, None, None, 0, event.time)
1008
905
def on_closed_task_treeview_button_press_event(self, treeview, event):
1009
906
if event.button == 3:
1016
913
treeview.grab_focus()
1017
914
treeview.set_cursor(path, col, 0)
1018
915
self.ctaskpopup.popup(None, None, None, event.button, time)
1021
918
def on_closed_task_treeview_key_press_event(self, treeview, event):
1022
if gtk.gdk.keyval_name(event.keyval) == "Delete":
919
keyname = gtk.gdk.keyval_name(event.keyval)
920
is_shift_f10 = keyname == "F10" and event.get_state() & gtk.gdk.SHIFT_MASK
922
if keyname == "Delete":
1023
923
self.on_delete_tasks()
925
elif is_shift_f10 or keyname == "Menu":
926
self.ctaskpopup.popup(None, None, None, 0, event.time)
1025
929
def on_add_task(self, widget, status=None):
1026
930
tags = [tag for tag in self.get_selected_tags() if tag.startswith('@')]
1129
1033
def on_set_due_clear(self, widget):
1130
1034
self.update_due_date(widget, None)
1132
def on_add_new_tag(self, widget=None, tid=None, tryagain = False):
1134
self.tids_to_addtag = self.get_selected_tasks()
1136
self.tids_to_addtag = [tid]
1138
if not self.tids_to_addtag == [None]:
1139
tag_entry = self.builder.get_object("tag_entry")
1140
apply_to_subtasks = self.builder.get_object("apply_to_subtasks")
1141
# We don't want to reset the text entry and checkbox if we got
1142
# sent back here after a warning.
1144
tag_entry.set_text(self.last_added_tags)
1145
tag_entry.set_completion(self.tag_completion)
1146
apply_to_subtasks.set_active(self.last_apply_tags_to_subtasks)
1147
tag_entry.grab_focus()
1148
addtag_dialog = self.builder.get_object("addtag_dialog")
1150
addtag_dialog.hide()
1151
self.tids_to_addtag = None
1155
def on_addtag_confirm(self, widget):
1156
tag_entry = self.builder.get_object("tag_entry")
1157
addtag_dialog = self.builder.get_object("addtag_dialog")
1158
apply_to_subtasks = self.builder.get_object("apply_to_subtasks")
1159
addtag_error = False
1160
entry_text = tag_entry.get_text()
1161
#use spaces and commas as separators
1163
for text in entry_text.split(","):
1164
tags = [t.strip() for t in text.split(" ")]
1167
if not tag.startswith('@'):
1169
new_tags.append(tag)
1170
# If the checkbox is checked, add all the subtasks to the list of
1172
if apply_to_subtasks.get_active():
1173
for tid in self.tids_to_addtag:
1174
task = self.req.get_task(tid)
1175
# FIXME: Python not reinitialize the default value of its parameter
1176
# therefore it must be done manually. This function should be refractored
1177
# as far it is marked as depricated
1178
for i in task.get_self_and_all_subtasks(tasks=[]):
1180
if taskid not in self.tids_to_addtag:
1181
self.tids_to_addtag.append(taskid)
1182
for tid in self.tids_to_addtag:
1183
task = self.req.get_task(tid)
1184
for new_tag in new_tags:
1185
task.add_tag(new_tag)
1188
# Rember the last actions
1189
self.last_added_tags = tag_entry.get_text()
1190
self.last_apply_tags_to_subtasks = apply_to_subtasks.get_active()
1192
def on_tag_entry_key_press_event(self, widget, event):
1193
if gtk.gdk.keyval_name(event.keyval) == "Return":
1194
self.on_addtag_confirm()
1036
def on_modify_tags(self, widget):
1037
""" Run Modify Tags dialog on selected tasks """
1038
tasks = self.get_selected_tasks()
1039
self.modifytags_dialog.modify_tags(tasks)
1196
1041
def close_all_task_editors(self, task_id):
1197
1042
""" Including editors of subtasks """