28
28
from gi.repository import Gtk, Gdk, Pango
30
from GTG import _, ngettext
31
from GTG.gtk.editor import GnomeConfig
30
from GTG import _, ngettext
31
from GTG.gtk.editor import GnomeConfig
32
32
from GTG.gtk.editor.taskview import TaskView
33
33
from GTG.core.plugins.engine import PluginEngine
34
from GTG.core.plugins.api import PluginAPI
35
from GTG.core.task import Task
36
from GTG.tools.dates import Date
34
from GTG.core.plugins.api import PluginAPI
35
from GTG.core.task import Task
36
from GTG.tools.dates import Date
37
37
from GTG.gtk.editor.calendar import GTGCalendar
39
40
class TaskEditor(object):
100
101
self.builder.connect_signals(dic)
101
self.window = self.builder.get_object("TaskEditor")
102
self.window = self.builder.get_object("TaskEditor")
102
103
#Removing the Normal textview to replace it by our own
103
104
#So don't try to change anything with GtkBuilder, this is a home-made widget
104
105
textview = self.builder.get_object("textview")
105
106
scrolled = self.builder.get_object("scrolledtask")
106
107
scrolled.remove(textview)
107
self.textview = TaskView(self.req, self.clipboard)
108
self.textview = TaskView(self.req, self.clipboard)
108
109
self.textview.show()
109
110
self.textview.set_subtask_callback(self.new_subtask)
110
111
self.textview.open_task_callback(self.vmanager.open_task)
113
114
scrolled.add(self.textview)
114
115
conf_font_value = self.browser_config.get("font_name")
115
116
if conf_font_value!= "":
116
self.textview.modify_font(Pango.FontDescription(conf_font_value))
117
self.textview.modify_font(Pango.FontDescription(conf_font_value))
117
118
#Voila! it's done
118
self.calendar = GTGCalendar(self.builder)
119
self.calendar = GTGCalendar(self.builder)
119
120
self.duedate_widget = self.builder.get_object("duedate_entry")
120
121
self.startdate_widget = self.builder.get_object("startdate_entry")
121
122
self.closeddate_widget = self.builder.get_object("closeddate_entry")
122
self.dayleft_label = self.builder.get_object("dayleft")
123
self.dayleft_label = self.builder.get_object("dayleft")
123
124
self.tasksidebar = self.builder.get_object("tasksidebar")
124
125
# Define accelerator keys
125
126
self.init_accelerators()
176
177
if tid in self.config:
177
178
if "position" in self.config[tid]:
178
179
pos = self.config[tid]["position"]
179
self.move(pos[0],pos[1])
180
#print "restoring position %s %s" %(pos[0],pos[1])
180
self.move(pos[0], pos[1])
181
#print "restoring position %s %s" %(pos[0], pos[1])
181
182
if "size" in self.config[tid]:
182
183
size = self.config[tid]["size"]
183
184
#print "size %s - %s" %(str(size[0]), str(size[1]))
184
#this eval(str()) is a ugly (!) hack to accept both int and str
185
#this eval(str()) is a ugly (!) hack to accept both int and
185
187
#FIXME: Fix this!
186
self.window.resize(eval(str(size[0])),eval(str(size[1])))
188
self.window.resize(eval(str(size[0])), eval(str(size[1])))
188
190
self.textview.set_editable(True)
189
191
self.window.show()
209
211
# Ctrl-Shift-N creates a new subtask
210
212
insert_subtask = self.builder.get_object("insert_subtask")
211
key, mod = Gtk.accelerator_parse("<Control><Shift>n")
212
insert_subtask.add_accelerator('clicked', agr, key, mod, Gtk.AccelFlags.VISIBLE)
213
key, mod = Gtk.accelerator_parse("<Control><Shift>n")
214
insert_subtask.add_accelerator('clicked', agr, key, mod,
215
Gtk.AccelFlags.VISIBLE)
214
217
# Ctrl-D marks task as done
215
218
mark_as_done_editor = self.builder.get_object('mark_as_done_editor')
216
219
key, mod = Gtk.accelerator_parse('<Control>d')
217
mark_as_done_editor.add_accelerator('clicked', agr, key, mod, Gtk.AccelFlags.VISIBLE)
220
mark_as_done_editor.add_accelerator('clicked', agr, key, mod,
221
Gtk.AccelFlags.VISIBLE)
219
223
# Ctrl-I marks task as dismissed
220
224
dismiss_editor = self.builder.get_object('dismiss_editor')
221
225
key, mod = Gtk.accelerator_parse('<Control>i')
222
dismiss_editor.add_accelerator('clicked', agr, key, mod, Gtk.AccelFlags.VISIBLE)
226
dismiss_editor.add_accelerator('clicked', agr, key, mod,
227
Gtk.AccelFlags.VISIBLE)
224
229
#Can be called at any time to reflect the status of the Task
225
230
#Refresh should never interfere with the TaskView.
231
236
if self.window == None:
236
241
self.window.set_title(title)
239
244
self.window.set_title(self.task.get_title())
241
status = self.task.get_status()
246
status = self.task.get_status()
247
dismiss_tooltip = GnomeConfig.MARK_UNDISMISS_TOOLTIP
242
248
if status == Task.STA_DISMISSED:
243
249
self.donebutton.set_label(GnomeConfig.MARK_DONE)
244
250
self.donebutton.set_tooltip_text(GnomeConfig.MARK_DONE_TOOLTIP)
245
251
self.donebutton.set_icon_name("gtg-task-done")
246
252
self.dismissbutton.set_label(GnomeConfig.MARK_UNDISMISS)
247
self.dismissbutton.set_tooltip_text(GnomeConfig.MARK_UNDISMISS_TOOLTIP)
253
self.dismissbutton.set_tooltip_text(dismiss_tooltip)
248
254
self.dismissbutton.set_icon_name("gtg-task-undismiss")
249
255
elif status == Task.STA_DONE:
250
256
self.donebutton.set_label(GnomeConfig.MARK_UNDONE)
251
257
self.donebutton.set_tooltip_text(GnomeConfig.MARK_UNDONE_TOOLTIP)
252
258
self.donebutton.set_icon_name("gtg-task-undone")
253
259
self.dismissbutton.set_label(GnomeConfig.MARK_DISMISS)
254
self.dismissbutton.set_tooltip_text(GnomeConfig.MARK_DISMISS_TOOLTIP)
260
self.dismissbutton.set_tooltip_text(dismiss_tooltip)
255
261
self.dismissbutton.set_icon_name("gtg-task-dismiss")
257
263
self.donebutton.set_label(GnomeConfig.MARK_DONE)
258
264
self.donebutton.set_tooltip_text(GnomeConfig.MARK_DONE_TOOLTIP)
259
265
self.donebutton.set_icon_name("gtg-task-done")
260
266
self.dismissbutton.set_label(GnomeConfig.MARK_DISMISS)
261
self.dismissbutton.set_tooltip_text(GnomeConfig.MARK_DISMISS_TOOLTIP)
267
self.dismissbutton.set_tooltip_text(dismiss_tooltip)
262
268
self.dismissbutton.set_icon_name("gtg-task-dismiss")
263
269
self.donebutton.show()
264
270
self.tasksidebar.show()
273
279
self.builder.get_object("label4").hide()
274
280
self.builder.get_object("hbox4").hide()
275
self.builder.get_object("label2").show()
281
self.builder.get_object("label2").show()
276
282
self.builder.get_object("hbox1").show()
278
284
#refreshing the start date field
304
310
self.closeddate_widget.set_text(str(closeddate))
306
312
#refreshing the day left label
307
#If the task is marked as done, we display the delay between the
308
#due date and the actual closing date. If the task isn't marked
313
#If the task is marked as done, we display the delay between the
314
#due date and the actual closing date. If the task isn't marked
309
315
#as done, we display the number of days left.
310
316
if status in [Task.STA_DISMISSED, Task.STA_DONE]:
311
317
delay = self.task.get_days_late()
315
321
txt = "Completed on time"
317
txt = ngettext("Completed %(days)d day late", "Completed %(days)d days late", delay) % {'days': delay}
323
txt = ngettext("Completed %(days)d day late",
324
"Completed %(days)d days late", delay) % {'days': delay}
318
325
elif delay <= -1:
319
326
abs_delay = abs(delay)
320
txt = ngettext("Completed %(days)d day early", "Completed %(days)d days early", abs_delay) % {'days': abs_delay}
327
txt = ngettext("Completed %(days)d day early",
328
"Completed %(days)d days early", abs_delay) % \
322
331
due_date = self.task.get_due_date()
323
332
result = due_date.days_left()
324
333
if due_date.is_fuzzy():
327
txt = ngettext("Due tomorrow!", "%(days)d days left", result) % {'days': result}
336
txt = ngettext("Due tomorrow!", "%(days)d days left", result) \
328
338
elif result == 0:
329
339
txt = _("Due today!")
331
341
abs_result = abs(result)
332
txt = ngettext("Due yesterday!", "Was %(days)d days ago", abs_result) % {'days': abs_result}
342
txt = ngettext("Due yesterday!", "Was %(days)d days ago",
343
abs_result) % {'days': abs_result}
334
345
style_context = self.window.get_style_context()
335
346
color = style_context.get_color(Gtk.StateFlags.INSENSITIVE).to_color()
336
self.dayleft_label.set_markup("<span color='"+color.to_string()+"'>"+txt+"</span>")
347
self.dayleft_label.set_markup("<span color='%s'>%s</span>" % (
348
color.to_string(), txt))
338
350
#Refreshing the tag list in the insert tag button
339
351
taglist = self.req.get_used_tags()
428
440
for task in all_subtasks:
429
441
self.vmanager.close_task(task.get_id())
431
def dismiss(self,widget): #pylint: disable-msg=W0613
443
def dismiss(self, widget): #pylint: disable-msg=W0613
432
444
stat = self.task.get_status()
433
445
if stat == "Dismiss":
434
446
self.task.set_status("Active")
438
450
self.close_all_subtasks()
441
def change_status(self,widget): #pylint: disable-msg=W0613
453
def change_status(self, widget): #pylint: disable-msg=W0613
442
454
stat = self.task.get_status()
443
455
if stat == "Done":
444
456
self.task.set_status("Active")
457
469
self.vmanager.ask_delete_tasks([self.task.get_id()])
459
471
#Take the title as argument and return the subtask ID
460
def new_subtask(self,title=None,tid=None):
472
def new_subtask(self, title=None, tid=None):
462
474
self.task.add_child(tid)
472
484
task_id = task.get_id()
473
485
self.vmanager.open_task(task_id)
475
def insert_subtask(self,widget): #pylint: disable-msg=W0613
487
def insert_subtask(self, widget): #pylint: disable-msg=W0613
476
488
self.textview.insert_newtask()
477
489
self.textview.grab_focus()
479
def inserttag_clicked(self,widget): #pylint: disable-msg=W0613
491
def inserttag_clicked(self, widget): #pylint: disable-msg=W0613
480
492
itera = self.textview.get_insert()
481
493
if itera.starts_line():
482
self.textview.insert_text("@",itera)
494
self.textview.insert_text("@", itera)
484
self.textview.insert_text(" @",itera)
496
self.textview.insert_text(" @", itera)
485
497
self.textview.grab_focus()
487
def inserttag(self,widget,tag): #pylint: disable-msg=W0613
499
def inserttag(self, widget, tag): #pylint: disable-msg=W0613
488
500
self.textview.insert_tags([tag])
489
501
self.textview.grab_focus()
492
504
self.task.set_title(self.textview.get_title())
493
self.task.set_text(self.textview.get_text())
505
self.task.set_text(self.textview.get_text())
495
507
if self.config != None:
496
508
self.config.write()
512
#This will bring the Task Editor to front
524
#This will bring the Task Editor to front
513
525
def present(self):
514
526
self.window.present()
528
def move(self, x, y):
520
self.window.move(xx,yy)
532
self.window.move(xx, yy)
524
536
def get_position(self):
525
537
return self.window.get_position()
527
def on_move(self,widget,event):
539
def on_move(self, widget, event):
528
540
#saving the position
529
541
if self.config != None:
530
542
tid = self.task.get_id()
535
547
self.config[tid]["size"] = self.window.get_size()
537
549
#We define dummy variable for when close is called from a callback
538
def close(self,window=None,a=None,b=None,c=None): #pylint: disable-msg=W0613
550
def close(self, window=None, a=None, b=None, c=None):
551
#pylint: disable-msg=W0613
539
552
#We should also destroy the whole taskeditor object.
541
554
self.window.destroy()
542
555
self.window = None
544
557
#The destroy signal is linked to the "close" button. So if we call
545
#destroy in the close function, this will cause the close to be called twice
558
#destroy in the close function, this will cause the close to be called
546
560
#To solve that, close will just call "destroy" and the destroy signal
547
561
#Will be linked to this destruction method that will save the task
548
def destruction(self,a=None):
562
def destruction(self, a=None):
549
563
#Save should be also called when buffer is modified
550
564
self.pengine.onTaskClose(self.plugin_api)
551
565
self.pengine.remove_api(self.plugin_api)