#!/usr/bin/python
+"""
+@todo Implement a custom label that encodes random info (Not set in stone, just throwing these out there)
+ Background color based on Location
+ Text intensity based on time estimate
+ Short/long version with long including tags colored specially
+"""
+
import warnings
+import contextlib
+import datetime
import gobject
import gtk
+import toolbox
+import coroutines
+
+
+@contextlib.contextmanager
+def gtk_lock():
+ gtk.gdk.threads_enter()
+ try:
+ yield
+ finally:
+ gtk.gdk.threads_leave()
+
+
+class ContextHandler(object):
+
+ HOLD_TIMEOUT = 1000
+ MOVE_THRESHHOLD = 10
+
+ def __init__(self, actionWidget, activationTarget = coroutines.null_sink()):
+ self._actionWidget = actionWidget
+ self._activationTarget = activationTarget
+
+ self._actionPressId = None
+ self._actionReleaseId = None
+ self._motionNotifyId = None
+ self._popupMenuId = None
+ self._holdTimerId = None
+
+ self._respondOnRelease = False
+ self._startPosition = None
+
+ def enable(self):
+ self._actionPressId = self._actionWidget.connect("button-press-event", self._on_press)
+ self._actionReleaseId = self._actionWidget.connect("button-release-event", self._on_release)
+ self._motionNotifyId = self._actionWidget.connect("motion-notify-event", self._on_motion)
+ self._popupMenuId = self._actionWidget.connect("popup-menu", self._on_popup)
+
+ def disable(self):
+ self._clear()
+ self._actionWidget.disconnect(self._actionPressId)
+ self._actionWidget.disconnect(self._actionReleaseId)
+ self._actionWidget.disconnect(self._motionNotifyId)
+ self._actionWidget.disconnect(self._popupMenuId)
+
+ def _respond(self, position):
+ widgetPosition = 0, 0 # @todo Figure out how to get a widgets root position
+ responsePosition = (
+ widgetPosition[0] + position[0],
+ widgetPosition[1] + position[1],
+ )
+ self._activationTarget.send((self._actionWidget, responsePosition))
+
+ def _clear(self):
+ if self._holdTimerId is not None:
+ gobject.source_remove(self._holdTimerId)
+ self._respondOnRelease = False
+ self._startPosition = None
+
+ def _is_cleared(self):
+ return self._startPosition is None
+
+ def _on_press(self, widget, event):
+ if not self._is_cleared():
+ return
+
+ self._startPosition = event.get_coords()
+ if event.button == 1:
+ # left click
+ self._holdTimerId = gobject.timeout_add(self.HOLD_TIMEOUT, self._on_hold_timeout)
+ else:
+ # any other click
+ self._respondOnRelease = True
+
+ def _on_release(self, widget, event):
+ if self._is_cleared():
+ return
+
+ if self._respondOnRelease:
+ position = self._startPosition
+ self._clear()
+ self._respond(position)
+ else:
+ self._clear()
+
+ def _on_hold_timeout(self):
+ assert not self._is_cleared()
+ gobject.source_remove(self._holdTimerId)
+ self._holdTimerId = None
+
+ position = self._startPosition
+ self._clear()
+ self._respond(position)
+
+ def _on_motion(self, widget, event):
+ if self._is_cleared():
+ return
+ curPosition = event.get_coords()
+ dx, dy = (
+ curPosition[1] - self._startPosition[1],
+ curPosition[1] - self._startPosition[1],
+ )
+ delta = (dx ** 2 + dy ** 2) ** (0.5)
+ if self.MOVE_THRESHHOLD <= delta:
+ self._clear()
+
+ def _on_popup(self, widget):
+ self._clear()
+ position = 0, 0
+ self._respond(position)
+
class LoginWindow(object):
self.__popupWindow.set_transient_for(parent)
self.__popupWindow.set_modal(True)
+ self.callback = lambda: None
+
def get_date(self):
year, month, day = self.__calendar.get_date()
month += 1 # Seems to be 0 indexed
- return year, month, day
+ return datetime.date(year, month, day)
def run(self):
self.__popupWindow.show_all()
originalProjectId = taskDetails["projId"]
originalProjectName = todoManager.get_project(originalProjectId)["name"]
originalName = taskDetails["name"]
- try:
- originalPriority = int(taskDetails["priority"])
- except ValueError:
- originalPriority = 0
- originalDue = taskDetails["due"]
+ originalPriority = str(taskDetails["priority"].get_nothrow(0))
+ if taskDetails["dueDate"].is_good():
+ originalDue = taskDetails["dueDate"].get().strftime("%Y-%m-%d %H:%M:%S")
+ else:
+ originalDue = ""
self._dialog.set_default_response(gtk.RESPONSE_OK)
self._taskName.set_text(originalName)
self._set_active_proj(originalProjectName)
- self._priorityChoiceCombo.set_active(originalPriority)
+ self._priorityChoiceCombo.set_active(int(originalPriority))
self._dueDateDisplay.set_text(originalDue)
try:
todoManager.set_name(taskId, newName)
print "NAME CHANGE"
if isPriorityDifferent:
- todoManager.set_priority(taskId, newPriority)
+ try:
+ priority = toolbox.Optional(int(newPriority))
+ except ValueError:
+ priority = toolbox.Optional()
+ todoManager.set_priority(taskId, priority)
print "PRIO CHANGE"
if isDueDifferent:
- todoManager.set_duedate(taskId, newDueDate)
+ if newDueDate:
+ due = datetime.datetime.strptime(newDueDate, "%Y-%m-%d %H:%M:%S")
+ due = toolbox.Optional(due)
+ else:
+ due = toolbox.Optional()
+
+ todoManager.set_duedate(taskId, due)
print "DUE CHANGE"
return {
else:
return str(index)
- def _get_date(self):
- # @bug The date is not used in a consistent manner causing ... issues
- dateParts = self._popupCalendar.get_date()
- dateParts = (str(part) for part in dateParts)
- return "-".join(dateParts)
-
def _update_duedate(self):
- self._dueDateDisplay.set_text(self._get_date())
+ date = self._popupCalendar.get_date()
+ time = datetime.time()
+ dueDate = datetime.datetime.combine(date, time)
+
+ formttedDate = dueDate.strftime("%Y-%m-%d %H:%M:%S")
+ self._dueDateDisplay.set_text(formttedDate)
def _on_name_paste(self, *args):
clipboard = gtk.clipboard_get()
def _on_cancel_clicked(self, *args):
self._dialog.response(gtk.RESPONSE_CANCEL)
+
+if __name__ == "__main__":
+ if True:
+ win = gtk.Window()
+ win.set_title("Tap'N'Hold")
+ eventBox = gtk.EventBox()
+ win.add(eventBox)
+
+ context = ContextHandler(eventBox, coroutines.printer_sink())
+ context.enable()
+ win.connect("destroy", lambda w: gtk.main_quit())
+
+ win.show_all()
+ gtk.main()