10 @contextlib.contextmanager
12 gtk.gdk.threads_enter()
16 gtk.gdk.threads_leave()
19 class LoginWindow(object):
21 def __init__(self, widgetTree):
25 self._dialog = widgetTree.get_widget("loginDialog")
26 self._parentWindow = widgetTree.get_widget("mainWindow")
27 self._serviceCombo = widgetTree.get_widget("serviceCombo")
28 self._usernameEntry = widgetTree.get_widget("usernameentry")
29 self._passwordEntry = widgetTree.get_widget("passwordentry")
31 self._serviceList = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING)
32 self._serviceCombo.set_model(self._serviceList)
33 cell = gtk.CellRendererText()
34 self._serviceCombo.pack_start(cell, True)
35 self._serviceCombo.add_attribute(cell, 'text', 1)
36 self._serviceCombo.set_active(0)
39 "on_loginbutton_clicked": self._on_loginbutton_clicked,
40 "on_loginclose_clicked": self._on_loginclose_clicked,
42 widgetTree.signal_autoconnect(callbackMapping)
44 def request_credentials(self, parentWindow = None):
48 if parentWindow is None:
49 parentWindow = self._parentWindow
51 self._serviceCombo.hide()
52 self._serviceList.clear()
55 self._dialog.set_transient_for(parentWindow)
56 self._dialog.set_default_response(gtk.RESPONSE_OK)
57 response = self._dialog.run()
58 if response != gtk.RESPONSE_OK:
59 raise RuntimeError("Login Cancelled")
61 username = self._usernameEntry.get_text()
62 password = self._passwordEntry.get_text()
63 self._passwordEntry.set_text("")
67 return username, password
69 def request_credentials_from(self, services, parentWindow = None):
73 if parentWindow is None:
74 parentWindow = self._parentWindow
76 self._serviceList.clear()
77 for serviceIdserviceName in services.iteritems():
78 self._serviceList.append(serviceIdserviceName)
79 self._serviceCombo.set_active(0)
80 self._serviceCombo.show()
83 self._dialog.set_transient_for(parentWindow)
84 self._dialog.set_default_response(gtk.RESPONSE_OK)
85 response = self._dialog.run()
86 if response != gtk.RESPONSE_OK:
87 raise RuntimeError("Login Cancelled")
89 username = self._usernameEntry.get_text()
90 password = self._passwordEntry.get_text()
91 self._passwordEntry.set_text("")
95 itr = self._serviceCombo.get_active_iter()
96 serviceId = int(self._serviceList.get_value(itr, 0))
97 self._serviceList.clear()
98 return serviceId, username, password
100 def _on_loginbutton_clicked(self, *args):
101 self._dialog.response(gtk.RESPONSE_OK)
103 def _on_loginclose_clicked(self, *args):
104 self._dialog.response(gtk.RESPONSE_CANCEL)
107 class ErrorDisplay(object):
109 def __init__(self, widgetTree):
110 super(ErrorDisplay, self).__init__()
111 self.__errorBox = widgetTree.get_widget("errorEventBox")
112 self.__errorDescription = widgetTree.get_widget("errorDescription")
113 self.__errorClose = widgetTree.get_widget("errorClose")
114 self.__parentBox = self.__errorBox.get_parent()
116 self.__errorBox.connect("button_release_event", self._on_close)
119 self.__parentBox.remove(self.__errorBox)
121 def push_message_with_lock(self, message):
122 gtk.gdk.threads_enter()
124 self.push_message(message)
126 gtk.gdk.threads_leave()
128 def push_message(self, message):
129 if 0 < len(self.__messages):
130 self.__messages.append(message)
132 self.__show_message(message)
134 def push_exception(self, exception):
135 self.push_message(exception.message)
136 warnings.warn(exception, stacklevel=3)
138 def pop_message(self):
139 if 0 < len(self.__messages):
140 self.__show_message(self.__messages[0])
141 del self.__messages[0]
143 self.__hide_message()
145 def _on_close(self, *args):
148 def __show_message(self, message):
149 self.__errorDescription.set_text(message)
150 self.__parentBox.pack_start(self.__errorBox, False, False)
151 self.__parentBox.reorder_child(self.__errorBox, 1)
153 def __hide_message(self):
154 self.__errorDescription.set_text("")
155 self.__parentBox.remove(self.__errorBox)
158 class MessageBox(gtk.MessageDialog):
160 def __init__(self, message):
162 gtk.MessageDialog.__init__(
165 gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
170 self.set_default_response(gtk.RESPONSE_OK)
171 self.connect('response', self._handle_clicked)
173 def _handle_clicked(self, *args):
177 class MessageBox2(gtk.MessageDialog):
179 def __init__(self, message):
181 gtk.MessageDialog.__init__(
184 gtk.DIALOG_DESTROY_WITH_PARENT,
189 self.set_default_response(gtk.RESPONSE_OK)
190 self.connect('response', self._handle_clicked)
192 def _handle_clicked(self, *args):
196 class PopupCalendar(object):
198 def __init__(self, parent):
199 self.__calendar = gtk.Calendar()
200 self.__calendar.connect("day-selected-double-click", self._on_date_select)
202 self.__popupWindow = gtk.Window(type = gtk.WINDOW_POPUP)
203 self.__popupWindow.set_title("")
204 self.__popupWindow.add(self.__calendar)
205 self.__popupWindow.set_transient_for(parent)
206 self.__popupWindow.set_modal(True)
209 year, month, day = self.__calendar.get_date()
210 month += 1 # Seems to be 0 indexed
211 return year, month, day
214 self.__popupWindow.show_all()
216 def _on_date_select(self, *args):
217 self.__popupWindow.hide()
224 class EditTaskDialog(object):
226 def __init__(self, widgetTree):
227 self._projectsList = gtk.ListStore(gobject.TYPE_STRING)
229 self._dialog = widgetTree.get_widget("editTaskDialog")
230 self._projectCombo = widgetTree.get_widget("edit-targetProjectCombo")
231 self._taskName = widgetTree.get_widget("edit-taskNameEntry")
232 self._pasteTaskNameButton = widgetTree.get_widget("edit-pasteTaskNameButton")
233 self._priorityChoiceCombo = widgetTree.get_widget("edit-priorityChoiceCombo")
234 self._dueDateDisplay = widgetTree.get_widget("edit-dueDateDisplay")
235 self._dueDateProperties = widgetTree.get_widget("edit-dueDateProperties")
236 self._clearDueDate = widgetTree.get_widget("edit-clearDueDate")
238 self._addButton = widgetTree.get_widget("edit-commitEditTaskButton")
239 self._cancelButton = widgetTree.get_widget("edit-cancelEditTaskButton")
241 self._popupCalendar = PopupCalendar(self._dialog)
243 def enable(self, todoManager):
244 self._populate_projects(todoManager)
245 self._pasteTaskNameButton.connect("clicked", self._on_name_paste)
246 self._dueDateProperties.connect("clicked", self._on_choose_duedate)
247 self._clearDueDate.connect("clicked", self._on_clear_duedate)
249 self._addButton.connect("clicked", self._on_add_clicked)
250 self._cancelButton.connect("clicked", self._on_cancel_clicked)
252 self._popupCalendar.callback = self._update_duedate
255 self._popupCalendar.callback = lambda: None
257 self._pasteTaskNameButton.disconnect("clicked", self._on_name_paste)
258 self._dueDateProperties.disconnect("clicked", self._on_choose_duedate)
259 self._clearDueDate.disconnect("clicked", self._on_clear_duedate)
260 self._projectsList.clear()
261 self._projectCombo.set_model(None)
263 def request_task(self, todoManager, taskId, parentWindow = None):
264 if parentWindow is not None:
265 self._dialog.set_transient_for(parentWindow)
267 taskDetails = todoManager.get_task_details(taskId)
268 originalProjectId = taskDetails["projId"]
269 originalProjectName = todoManager.get_project(originalProjectId)["name"]
270 originalName = taskDetails["name"]
272 originalPriority = int(taskDetails["priority"])
275 originalDue = taskDetails["due"]
277 self._dialog.set_default_response(gtk.RESPONSE_OK)
278 self._taskName.set_text(originalName)
279 self._set_active_proj(originalProjectName)
280 self._priorityChoiceCombo.set_active(originalPriority)
281 self._dueDateDisplay.set_text(originalDue)
284 response = self._dialog.run()
285 if response != gtk.RESPONSE_OK:
286 raise RuntimeError("Edit Cancelled")
290 newProjectName = self._get_project(todoManager)
291 newName = self._taskName.get_text()
292 newPriority = self._get_priority()
293 newDueDate = self._dueDateDisplay.get_text()
295 isProjDifferent = newProjectName != originalProjectName
296 isNameDifferent = newName != originalName
297 isPriorityDifferent = newPriority != originalPriority
298 isDueDifferent = newDueDate != originalDue
301 newProjectId = todoManager.lookup_project(newProjectName)
302 todoManager.set_project(taskId, newProjectId)
305 todoManager.set_name(taskId, newName)
307 if isPriorityDifferent:
308 todoManager.set_priority(taskId, newPriority)
311 todoManager.set_duedate(taskId, newDueDate)
315 "projId": isProjDifferent,
316 "name": isNameDifferent,
317 "priority": isPriorityDifferent,
318 "due": isDueDifferent,
321 def _populate_projects(self, todoManager):
322 for projectName in todoManager.get_projects():
323 row = (projectName["name"], )
324 self._projectsList.append(row)
325 self._projectCombo.set_model(self._projectsList)
326 cell = gtk.CellRendererText()
327 self._projectCombo.pack_start(cell, True)
328 self._projectCombo.add_attribute(cell, 'text', 0)
329 self._projectCombo.set_active(0)
331 def _set_active_proj(self, projName):
332 for i, row in enumerate(self._projectsList):
333 if row[0] == projName:
334 self._projectCombo.set_active(i)
337 raise ValueError("%s not in list" % projName)
339 def _get_project(self, todoManager):
340 name = self._projectCombo.get_active_text()
343 def _get_priority(self):
344 index = self._priorityChoiceCombo.get_active()
352 # @bug The date is not used in a consistent manner causing ... issues
353 dateParts = self._popupCalendar.get_date()
354 dateParts = (str(part) for part in dateParts)
355 return "-".join(dateParts)
357 def _update_duedate(self):
358 self._dueDateDisplay.set_text(self._get_date())
360 def _on_name_paste(self, *args):
361 clipboard = gtk.clipboard_get()
362 contents = clipboard.wait_for_text()
363 if contents is not None:
364 self._taskName.set_text(contents)
366 def _on_choose_duedate(self, *args):
367 self._popupCalendar.run()
369 def _on_clear_duedate(self, *args):
370 self._dueDateDisplay.set_text("")
372 def _on_add_clicked(self, *args):
373 self._dialog.response(gtk.RESPONSE_OK)
375 def _on_cancel_clicked(self, *args):
376 self._dialog.response(gtk.RESPONSE_CANCEL)