4 @todo Implement a custom label that encodes random info (Not set in stone, just throwing these out there)
5 Background color based on Location
6 Text intensity based on time estimate
7 Short/long version with long including tags colored specially
23 @contextlib.contextmanager
25 gtk.gdk.threads_enter()
29 gtk.gdk.threads_leave()
32 class ContextHandler(object):
37 def __init__(self, actionWidget, eventTarget = coroutines.null_sink()):
38 self._actionWidget = actionWidget
39 self._eventTarget = eventTarget
41 self._actionPressId = None
42 self._actionReleaseId = None
43 self._motionNotifyId = None
44 self._popupMenuId = None
45 self._holdTimerId = None
47 self._respondOnRelease = False
48 self._startPosition = None
51 self._actionPressId = self._actionWidget.connect("button-press-event", self._on_press)
52 self._actionReleaseId = self._actionWidget.connect("button-release-event", self._on_release)
53 self._motionNotifyId = self._actionWidget.connect("motion-notify-event", self._on_motion)
54 self._popupMenuId = self._actionWidget.connect("popup-menu", self._on_popup)
58 self._actionWidget.disconnect(self._actionPressId)
59 self._actionWidget.disconnect(self._actionReleaseId)
60 self._actionWidget.disconnect(self._motionNotifyId)
61 self._actionWidget.disconnect(self._popupMenuId)
63 def _respond(self, position):
64 widgetPosition = 0, 0 # @todo Figure out how to get a widgets root position
66 widgetPosition[0] + position[0],
67 widgetPosition[1] + position[1],
69 self._eventTarget.send((self._actionWidget, responsePosition))
72 if self._holdTimerId is not None:
73 gobject.source_remove(self._holdTimerId)
74 self._respondOnRelease = False
75 self._startPosition = None
77 def _is_cleared(self):
78 return self._startPosition is None
80 def _on_press(self, widget, event):
81 if not self._is_cleared():
84 self._startPosition = event.get_coords()
87 self._holdTimerId = gobject.timeout_add(self.HOLD_TIMEOUT, self._on_hold_timeout)
90 self._respondOnRelease = True
92 def _on_release(self, widget, event):
93 if self._is_cleared():
96 if self._respondOnRelease:
97 position = self._startPosition
99 self._respond(position)
103 def _on_hold_timeout(self):
104 assert not self._is_cleared()
105 gobject.source_remove(self._holdTimerId)
106 self._holdTimerId = None
108 position = self._startPosition
110 self._respond(position)
112 def _on_motion(self, widget, event):
113 if self._is_cleared():
115 curPosition = event.get_coords()
117 curPosition[1] - self._startPosition[1],
118 curPosition[1] - self._startPosition[1],
120 delta = (dx ** 2 + dy ** 2) ** (0.5)
121 if self.MOVE_THRESHHOLD <= delta:
124 def _on_popup(self, widget):
127 self._respond(position)
130 class LoginWindow(object):
132 def __init__(self, widgetTree):
134 @note Thread agnostic
136 self._dialog = widgetTree.get_widget("loginDialog")
137 self._parentWindow = widgetTree.get_widget("mainWindow")
138 self._serviceCombo = widgetTree.get_widget("serviceCombo")
139 self._usernameEntry = widgetTree.get_widget("usernameentry")
140 self._passwordEntry = widgetTree.get_widget("passwordentry")
142 self._serviceList = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING)
143 self._serviceCombo.set_model(self._serviceList)
144 cell = gtk.CellRendererText()
145 self._serviceCombo.pack_start(cell, True)
146 self._serviceCombo.add_attribute(cell, 'text', 1)
147 self._serviceCombo.set_active(0)
150 "on_loginbutton_clicked": self._on_loginbutton_clicked,
151 "on_loginclose_clicked": self._on_loginclose_clicked,
153 widgetTree.signal_autoconnect(callbackMapping)
155 def request_credentials(self, parentWindow = None):
159 if parentWindow is None:
160 parentWindow = self._parentWindow
162 self._serviceCombo.hide()
163 self._serviceList.clear()
166 self._dialog.set_transient_for(parentWindow)
167 self._dialog.set_default_response(gtk.RESPONSE_OK)
168 response = self._dialog.run()
169 if response != gtk.RESPONSE_OK:
170 raise RuntimeError("Login Cancelled")
172 username = self._usernameEntry.get_text()
173 password = self._passwordEntry.get_text()
174 self._passwordEntry.set_text("")
178 return username, password
180 def request_credentials_from(self, services, parentWindow = None):
184 if parentWindow is None:
185 parentWindow = self._parentWindow
187 self._serviceList.clear()
188 for serviceIdserviceName in services.iteritems():
189 self._serviceList.append(serviceIdserviceName)
190 self._serviceCombo.set_active(0)
191 self._serviceCombo.show()
194 self._dialog.set_transient_for(parentWindow)
195 self._dialog.set_default_response(gtk.RESPONSE_OK)
196 response = self._dialog.run()
197 if response != gtk.RESPONSE_OK:
198 raise RuntimeError("Login Cancelled")
200 username = self._usernameEntry.get_text()
201 password = self._passwordEntry.get_text()
202 self._passwordEntry.set_text("")
206 itr = self._serviceCombo.get_active_iter()
207 serviceId = int(self._serviceList.get_value(itr, 0))
208 self._serviceList.clear()
209 return serviceId, username, password
211 def _on_loginbutton_clicked(self, *args):
212 self._dialog.response(gtk.RESPONSE_OK)
214 def _on_loginclose_clicked(self, *args):
215 self._dialog.response(gtk.RESPONSE_CANCEL)
218 class ErrorDisplay(object):
220 def __init__(self, widgetTree):
221 super(ErrorDisplay, self).__init__()
222 self.__errorBox = widgetTree.get_widget("errorEventBox")
223 self.__errorDescription = widgetTree.get_widget("errorDescription")
224 self.__errorClose = widgetTree.get_widget("errorClose")
225 self.__parentBox = self.__errorBox.get_parent()
227 self.__errorBox.connect("button_release_event", self._on_close)
230 self.__parentBox.remove(self.__errorBox)
232 def push_message_with_lock(self, message):
233 gtk.gdk.threads_enter()
235 self.push_message(message)
237 gtk.gdk.threads_leave()
239 def push_message(self, message):
240 if 0 < len(self.__messages):
241 self.__messages.append(message)
243 self.__show_message(message)
245 def push_exception(self, exception = None):
246 if exception is None:
247 userMessage = sys.exc_value.message
248 warningMessage = traceback.format_exc()
250 userMessage = exception.message
251 warningMessage = exception
252 self.push_message(userMessage)
253 warnings.warn(warningMessage, stacklevel=3)
255 def pop_message(self):
256 if 0 < len(self.__messages):
257 self.__show_message(self.__messages[0])
258 del self.__messages[0]
260 self.__hide_message()
262 def _on_close(self, *args):
265 def __show_message(self, message):
266 self.__errorDescription.set_text(message)
267 self.__parentBox.pack_start(self.__errorBox, False, False)
268 self.__parentBox.reorder_child(self.__errorBox, 1)
270 def __hide_message(self):
271 self.__errorDescription.set_text("")
272 self.__parentBox.remove(self.__errorBox)
275 class DummyErrorDisplay(object):
278 super(DummyErrorDisplay, self).__init__()
282 def push_message_with_lock(self, message):
283 self.push_message(message)
285 def push_message(self, message):
286 if 0 < len(self.__messages):
287 self.__messages.append(message)
289 self.__show_message(message)
291 def push_exception(self, exception = None):
292 if exception is None:
293 warningMessage = traceback.format_exc()
295 warningMessage = exception
296 warnings.warn(warningMessage, stacklevel=3)
298 def pop_message(self):
299 if 0 < len(self.__messages):
300 self.__show_message(self.__messages[0])
301 del self.__messages[0]
303 def __show_message(self, message):
304 warnings.warn(message, stacklevel=2)
307 class MessageBox(gtk.MessageDialog):
309 def __init__(self, message):
311 gtk.MessageDialog.__init__(
314 gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
319 self.set_default_response(gtk.RESPONSE_OK)
320 self.connect('response', self._handle_clicked)
322 def _handle_clicked(self, *args):
326 class MessageBox2(gtk.MessageDialog):
328 def __init__(self, message):
330 gtk.MessageDialog.__init__(
333 gtk.DIALOG_DESTROY_WITH_PARENT,
338 self.set_default_response(gtk.RESPONSE_OK)
339 self.connect('response', self._handle_clicked)
341 def _handle_clicked(self, *args):
345 class PopupCalendar(object):
347 def __init__(self, parent, displayDate, title = ""):
348 self._displayDate = displayDate
350 self._calendar = gtk.Calendar()
351 self._calendar.select_month(self._displayDate.month, self._displayDate.year)
352 self._calendar.select_day(self._displayDate.day)
353 self._calendar.set_display_options(
354 gtk.CALENDAR_SHOW_HEADING |
355 gtk.CALENDAR_SHOW_DAY_NAMES |
356 gtk.CALENDAR_NO_MONTH_CHANGE |
359 self._calendar.connect("day-selected", self._on_day_selected)
361 self._popupWindow = gtk.Window()
362 self._popupWindow.set_title(title)
363 self._popupWindow.add(self._calendar)
364 self._popupWindow.set_transient_for(parent)
365 self._popupWindow.set_modal(True)
366 self._popupWindow.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
367 self._popupWindow.set_skip_pager_hint(True)
368 self._popupWindow.set_skip_taskbar_hint(True)
371 self._popupWindow.show_all()
373 def _on_day_selected(self, *args):
375 self._calendar.select_month(self._displayDate.month, self._displayDate.year)
376 self._calendar.select_day(self._displayDate.day)
377 except StandardError, e:
378 warnings.warn(e.message)
381 class QuickAddView(object):
383 def __init__(self, widgetTree, errorDisplay, signalSink, prefix):
384 self._errorDisplay = errorDisplay
386 self._signalSink = signalSink
388 self._clipboard = gtk.clipboard_get()
390 self._taskNameEntry = widgetTree.get_widget(prefix+"-nameEntry")
391 self._addTaskButton = widgetTree.get_widget(prefix+"-addButton")
392 self._pasteTaskNameButton = widgetTree.get_widget(prefix+"-pasteNameButton")
393 self._clearTaskNameButton = widgetTree.get_widget(prefix+"-clearNameButton")
395 self._onAddClickedId = None
396 self._onAddReleasedId = None
397 self._addToEditTimerId = None
398 self._onClearId = None
399 self._onPasteId = None
401 def enable(self, manager):
402 self._manager = manager
404 self._onAddId = self._addTaskButton.connect("clicked", self._on_add)
405 self._onAddClickedId = self._addTaskButton.connect("pressed", self._on_add_pressed)
406 self._onAddReleasedId = self._addTaskButton.connect("released", self._on_add_released)
407 self._onPasteId = self._pasteTaskNameButton.connect("clicked", self._on_paste)
408 self._onClearId = self._clearTaskNameButton.connect("clicked", self._on_clear)
413 self._addTaskButton.disconnect(self._onAddId)
414 self._addTaskButton.disconnect(self._onAddClickedId)
415 self._addTaskButton.disconnect(self._onAddReleasedId)
416 self._pasteTaskNameButton.disconnect(self._onPasteId)
417 self._clearTaskNameButton.disconnect(self._onClearId)
419 def set_addability(self, addability):
420 self._addTaskButton.set_sensitive(addability)
422 def _on_add(self, *args):
424 name = self._taskNameEntry.get_text()
425 self._taskNameEntry.set_text("")
427 self._signalSink.stage.send(("add", name))
428 except StandardError, e:
429 self._errorDisplay.push_exception()
431 def _on_add_edit(self, *args):
433 name = self._taskNameEntry.get_text()
434 self._taskNameEntry.set_text("")
436 self._signalSink.stage.send(("add-edit", name))
437 except StandardError, e:
438 self._errorDisplay.push_exception()
440 def _on_add_pressed(self, widget):
442 self._addToEditTimerId = gobject.timeout_add(1000, self._on_add_edit)
443 except StandardError, e:
444 self._errorDisplay.push_exception()
446 def _on_add_released(self, widget):
448 if self._addToEditTimerId is not None:
449 gobject.source_remove(self._addToEditTimerId)
450 self._addToEditTimerId = None
451 except StandardError, e:
452 self._errorDisplay.push_exception()
454 def _on_paste(self, *args):
456 entry = self._taskNameEntry.get_text()
457 addedText = self._clipboard.wait_for_text()
460 self._taskNameEntry.set_text(entry)
461 except StandardError, e:
462 self._errorDisplay.push_exception()
464 def _on_clear(self, *args):
466 self._taskNameEntry.set_text("")
467 except StandardError, e:
468 self._errorDisplay.push_exception()
471 class NotesDialog(object):
473 def __init__(self, widgetTree):
474 self._dialog = widgetTree.get_widget("notesDialog")
475 self._notesBox = widgetTree.get_widget("notes-notesBox")
476 self._addButton = widgetTree.get_widget("notes-addButton")
477 self._saveButton = widgetTree.get_widget("notes-saveButton")
478 self._cancelButton = widgetTree.get_widget("notes-cancelButton")
480 self._onSaveId = None
481 self._onCancelId = None
484 self._notesToDelete = []
487 self._dialog.set_default_size(800, 300)
488 self._onAddId = self._addButton.connect("clicked", self._on_add_clicked)
489 self._onSaveId = self._saveButton.connect("clicked", self._on_save_clicked)
490 self._onCancelId = self._cancelButton.connect("clicked", self._on_cancel_clicked)
493 self._addButton.disconnect(self._onAddId)
494 self._saveButton.disconnect(self._onSaveId)
495 self._cancelButton.disconnect(self._onCancelId)
497 def run(self, todoManager, taskId, parentWindow = None):
498 if parentWindow is not None:
499 self._dialog.set_transient_for(parentWindow)
501 taskDetails = todoManager.get_task_details(taskId)
503 self._dialog.set_default_response(gtk.RESPONSE_OK)
504 for note in taskDetails["notes"].itervalues():
505 noteBox, titleEntry, noteDeleteButton, noteEntry = self._append_notebox(note)
506 noteDeleteButton.connect("clicked", self._on_delete_existing, note["id"], noteBox)
509 response = self._dialog.run()
510 if response != gtk.RESPONSE_OK:
511 raise RuntimeError("Edit Cancelled")
515 for note in self._notes:
517 noteTitle = note[2].get_text()
518 noteBody = note[4].get_buffer().get_text()
520 print "New note:", note
521 todoManager.add_note(taskId, noteTitle, noteBody)
523 # @todo Provide way to only update on change
524 print "Updating note:", note
525 todoManager.update_note(noteId, noteTitle, noteBody)
527 for deletedNoteId in self._notesToDelete:
528 print "Deleted note:", deletedNoteId
529 todoManager.delete_note(noteId)
531 def _append_notebox(self, noteDetails = None):
532 if noteDetails is None:
533 noteDetails = {"id": None, "title": "", "body": ""}
537 titleBox = gtk.HBox()
538 titleEntry = gtk.Entry()
539 titleEntry.set_text(noteDetails["title"])
540 titleBox.pack_start(titleEntry, True, True)
541 noteDeleteButton = gtk.Button(stock=gtk.STOCK_DELETE)
542 titleBox.pack_end(noteDeleteButton, False, False)
543 noteBox.pack_start(titleBox, False, True)
545 noteEntryScroll = gtk.ScrolledWindow()
546 noteEntryScroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
547 noteEntry = gtk.TextView()
548 noteEntry.set_editable(True)
549 noteEntry.set_wrap_mode(gtk.WRAP_WORD)
550 noteEntry.get_buffer().set_text(noteDetails["body"])
551 noteEntry.set_size_request(-1, 150)
552 noteEntryScroll.add(noteEntry)
553 noteBox.pack_start(noteEntryScroll, True, True)
555 self._notesBox.pack_start(noteBox, True, True)
558 note = noteDetails["id"], noteBox, titleEntry, noteDeleteButton, noteEntry
559 self._notes.append(note)
562 def _on_add_clicked(self, *args):
563 noteBox, titleEntry, noteDeleteButton, noteEntry = self._append_notebox()
564 noteDeleteButton.connect("clicked", self._on_delete_new, noteBox)
566 def _on_save_clicked(self, *args):
567 self._dialog.response(gtk.RESPONSE_OK)
569 def _on_cancel_clicked(self, *args):
570 self._dialog.response(gtk.RESPONSE_CANCEL)
572 def _on_delete_new(self, widget, noteBox):
573 self._notesBox.remove(noteBox)
574 self._notes = [note for note in self._notes if note[1] is not noteBox]
576 def _on_delete_existing(self, widget, noteId, noteBox):
577 self._notesBox.remove(noteBox)
578 self._notes = [note for note in self._notes if note[1] is not noteBox]
579 self._notesToDelete.append(noteId)
582 class EditTaskDialog(object):
584 def __init__(self, widgetTree):
585 self._projectsList = gtk.ListStore(gobject.TYPE_STRING)
587 self._dialog = widgetTree.get_widget("editTaskDialog")
588 self._projectCombo = widgetTree.get_widget("edit-targetProjectCombo")
589 self._taskName = widgetTree.get_widget("edit-taskNameEntry")
590 self._pasteTaskNameButton = widgetTree.get_widget("edit-pasteTaskNameButton")
591 self._priorityChoiceCombo = widgetTree.get_widget("edit-priorityChoiceCombo")
592 self._dueDateDisplay = widgetTree.get_widget("edit-dueDateCalendar")
593 self._clearDueDate = widgetTree.get_widget("edit-clearDueDate")
595 self._addButton = widgetTree.get_widget("edit-commitEditTaskButton")
596 self._cancelButton = widgetTree.get_widget("edit-cancelEditTaskButton")
598 self._onPasteTaskId = None
599 self._onClearDueDateId = None
601 self._onCancelId = None
603 def enable(self, todoManager):
604 self._populate_projects(todoManager)
606 self._onPasteTaskId = self._pasteTaskNameButton.connect("clicked", self._on_name_paste)
607 self._onClearDueDateId = self._clearDueDate.connect("clicked", self._on_clear_duedate)
608 self._onAddId = self._addButton.connect("clicked", self._on_add_clicked)
609 self._onCancelId = self._cancelButton.connect("clicked", self._on_cancel_clicked)
612 self._pasteTaskNameButton.disconnect(self._onPasteTaskId)
613 self._clearDueDate.disconnect(self._onClearDueDateId)
614 self._addButton.disconnect(self._onAddId)
615 self._cancelButton.disconnect(self._onCancelId)
617 self._projectsList.clear()
618 self._projectCombo.set_model(None)
620 def request_task(self, todoManager, taskId, parentWindow = None):
621 if parentWindow is not None:
622 self._dialog.set_transient_for(parentWindow)
624 taskDetails = todoManager.get_task_details(taskId)
625 originalProjectId = taskDetails["projId"]
626 originalProjectName = todoManager.get_project(originalProjectId)["name"]
627 originalName = taskDetails["name"]
628 originalPriority = str(taskDetails["priority"].get_nothrow(0))
629 if taskDetails["dueDate"].is_good():
630 originalDue = taskDetails["dueDate"].get()
634 self._dialog.set_default_response(gtk.RESPONSE_OK)
635 self._taskName.set_text(originalName)
636 self._set_active_proj(originalProjectName)
637 self._priorityChoiceCombo.set_active(int(originalPriority))
638 if originalDue is not None:
639 # Months are 0 indexed
640 self._dueDateDisplay.select_month(originalDue.month - 1, originalDue.year)
641 self._dueDateDisplay.select_day(originalDue.day)
643 now = datetime.datetime.now()
644 self._dueDateDisplay.select_month(now.month, now.year)
645 self._dueDateDisplay.select_day(0)
648 response = self._dialog.run()
649 if response != gtk.RESPONSE_OK:
650 raise RuntimeError("Edit Cancelled")
654 newProjectName = self._get_project(todoManager)
655 newName = self._taskName.get_text()
656 newPriority = self._get_priority()
657 year, month, day = self._dueDateDisplay.get_date()
659 # Months are 0 indexed
660 date = datetime.date(year, month + 1, day)
661 time = datetime.time()
662 newDueDate = datetime.datetime.combine(date, time)
666 isProjDifferent = newProjectName != originalProjectName
667 isNameDifferent = newName != originalName
668 isPriorityDifferent = newPriority != originalPriority
669 isDueDifferent = newDueDate != originalDue
672 newProjectId = todoManager.lookup_project(newProjectName)
673 todoManager.set_project(taskId, newProjectId)
676 todoManager.set_name(taskId, newName)
678 if isPriorityDifferent:
680 priority = toolbox.Optional(int(newPriority))
682 priority = toolbox.Optional()
683 todoManager.set_priority(taskId, priority)
687 due = toolbox.Optional(newDueDate)
689 due = toolbox.Optional()
691 todoManager.set_duedate(taskId, due)
695 "projId": isProjDifferent,
696 "name": isNameDifferent,
697 "priority": isPriorityDifferent,
698 "due": isDueDifferent,
701 def _populate_projects(self, todoManager):
702 for projectName in todoManager.get_projects():
703 row = (projectName["name"], )
704 self._projectsList.append(row)
705 self._projectCombo.set_model(self._projectsList)
706 cell = gtk.CellRendererText()
707 self._projectCombo.pack_start(cell, True)
708 self._projectCombo.add_attribute(cell, 'text', 0)
709 self._projectCombo.set_active(0)
711 def _set_active_proj(self, projName):
712 for i, row in enumerate(self._projectsList):
713 if row[0] == projName:
714 self._projectCombo.set_active(i)
717 raise ValueError("%s not in list" % projName)
719 def _get_project(self, todoManager):
720 name = self._projectCombo.get_active_text()
723 def _get_priority(self):
724 index = self._priorityChoiceCombo.get_active()
731 def _on_name_paste(self, *args):
732 clipboard = gtk.clipboard_get()
733 contents = clipboard.wait_for_text()
734 if contents is not None:
735 self._taskName.set_text(contents)
737 def _on_clear_duedate(self, *args):
738 self._dueDateDisplay.select_day(0)
740 def _on_add_clicked(self, *args):
741 self._dialog.response(gtk.RESPONSE_OK)
743 def _on_cancel_clicked(self, *args):
744 self._dialog.response(gtk.RESPONSE_CANCEL)
747 class PreferencesDialog(object):
749 def __init__(self, widgetTree):
750 self._backendList = gtk.ListStore(gobject.TYPE_STRING)
751 self._backendCell = gtk.CellRendererText()
753 self._dialog = widgetTree.get_widget("preferencesDialog")
754 self._backendSelector = widgetTree.get_widget("prefsBackendSelector")
755 self._applyButton = widgetTree.get_widget("applyPrefsButton")
756 self._cancelButton = widgetTree.get_widget("cancelPrefsButton")
758 self._onApplyId = None
759 self._onCancelId = None
762 self._dialog.set_default_size(800, 300)
763 self._onApplyId = self._applyButton.connect("clicked", self._on_apply_clicked)
764 self._onCancelId = self._cancelButton.connect("clicked", self._on_cancel_clicked)
766 cell = self._backendCell
767 self._backendSelector.pack_start(cell, True)
768 self._backendSelector.add_attribute(cell, 'text', 0)
769 self._backendSelector.set_model(self._backendList)
772 self._applyButton.disconnect(self._onApplyId)
773 self._cancelButton.disconnect(self._onCancelId)
775 self._backendList.clear()
776 self._backendSelector.set_model(None)
778 def run(self, app, parentWindow = None):
779 if parentWindow is not None:
780 self._dialog.set_transient_for(parentWindow)
782 self._backendList.clear()
784 for i, (uiName, ui) in enumerate(app.get_uis()):
785 self._backendList.append((uiName, ))
786 if uiName == app.get_default_ui():
788 self._backendSelector.set_active(activeIndex)
791 response = self._dialog.run()
792 if response != gtk.RESPONSE_OK:
793 raise RuntimeError("Edit Cancelled")
797 backendName = self._backendSelector.get_active_text()
798 app.switch_ui(backendName)
800 def _on_apply_clicked(self, *args):
801 self._dialog.response(gtk.RESPONSE_OK)
803 def _on_cancel_clicked(self, *args):
804 self._dialog.response(gtk.RESPONSE_CANCEL)
807 class ProjectsDialog(object):
813 def __init__(self, widgetTree):
816 self._dialog = widgetTree.get_widget("projectsDialog")
817 self._projView = widgetTree.get_widget("proj-projectView")
819 addSink = coroutines.CoSwitch(["add", "add-edit"])
820 addSink.register_sink("add", coroutines.func_sink(self._on_add))
821 addSink.register_sink("add-edit", coroutines.func_sink(self._on_add_edit))
822 self._addView = QuickAddView(widgetTree, DummyErrorDisplay(), addSink, "proj")
824 self._projList = gtk.ListStore(
825 gobject.TYPE_STRING, # id
826 gobject.TYPE_STRING, # name
827 gobject.TYPE_BOOLEAN, # is visible
829 self._visibilityColumn = gtk.TreeViewColumn('') # Complete?
830 self._visibilityCell = gtk.CellRendererToggle()
831 self._visibilityCell.set_property("activatable", True)
832 self._visibilityCell.connect("toggled", self._on_toggle_visibility)
833 self._visibilityColumn.pack_start(self._visibilityCell, False)
834 self._visibilityColumn.set_attributes(self._visibilityCell, active=self.VISIBILITY_IDX)
835 self._nameColumn = gtk.TreeViewColumn('Name')
836 self._nameCell = gtk.CellRendererText()
837 self._nameColumn.pack_start(self._nameCell, True)
838 self._nameColumn.set_attributes(self._nameCell, text=self.NAME_IDX)
839 self._nameColumn.set_expand(True)
841 self._projView.append_column(self._visibilityColumn)
842 self._projView.append_column(self._nameColumn)
843 self._projView.connect("row-activated", self._on_proj_select)
845 def enable(self, manager):
846 self._manager = manager
848 self._populate_projects()
849 self._dialog.show_all()
852 self._dialog.hide_all()
855 self._projList.clear()
856 self._projView.set_model(None)
858 def _populate_projects(self):
859 self._projList.clear()
861 projects = self._manager.get_projects()
862 for project in projects:
863 projectId = project["id"]
864 projectName = project["name"]
865 isVisible = project["isVisible"]
866 row = (projectId, projectName, isVisible)
867 self._projList.append(row)
868 self._projView.set_model(self._projList)
870 def _on_add(self, eventData):
871 eventName, projectName, = eventData
872 self._manager.add_project(projectName)
873 self._populate_projects()
875 def _on_add_edit(self, eventData):
876 self._on_add(eventData)
878 def _on_toggle_visibility(self, cell, path):
880 row = self._projList[listIndex]
881 projId = row[self.ID_IDX]
882 oldValue = row[self.VISIBILITY_IDX]
883 self._manager.set_project_visibility(projId, not oldValue)
884 row[self.VISIBILITY_IDX] = not oldValue
886 def _on_proj_select(self, *args):
887 # @todo Implement project renaming
891 if __name__ == "__main__":
894 win.set_title("Tap'N'Hold")
895 eventBox = gtk.EventBox()
898 context = ContextHandler(eventBox, coroutines.printer_sink())
900 win.connect("destroy", lambda w: gtk.main_quit())
905 cal = PopupCalendar(None, datetime.datetime.now())
906 cal._popupWindow.connect("destroy", lambda w: gtk.main_quit())