3 from __future__ import with_statement
15 @contextlib.contextmanager
17 gtk.gdk.threads_enter()
21 gtk.gdk.threads_leave()
26 Decorator that makes a generator-function into a function that will continue execution on next call
30 @functools.wraps(func)
31 def decorated_func(*args, **kwds):
33 a.append(func(*args, **kwds))
44 def asynchronous_gtk_message(original_func):
46 @note Idea came from http://www.aclevername.com/articles/python-webgui/
50 args, kwargs = allArgs
52 original_func(*args, **kwargs)
55 @functools.wraps(original_func)
56 def delayed_func(*args, **kwargs):
57 gobject.idle_add(execute, (args, kwargs))
62 def synchronous_gtk_message(original_func):
64 @note Idea came from http://www.aclevername.com/articles/python-webgui/
67 @functools.wraps(original_func)
68 def immediate_func(*args, **kwargs):
70 return original_func(*args, **kwargs)
75 class LoginWindow(object):
77 def __init__(self, widgetTree):
81 self._dialog = widgetTree.get_widget("loginDialog")
82 self._parentWindow = widgetTree.get_widget("mainWindow")
83 self._serviceCombo = widgetTree.get_widget("serviceCombo")
84 self._usernameEntry = widgetTree.get_widget("usernameentry")
85 self._passwordEntry = widgetTree.get_widget("passwordentry")
87 self._serviceList = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING)
88 self._serviceCombo.set_model(self._serviceList)
89 cell = gtk.CellRendererText()
90 self._serviceCombo.pack_start(cell, True)
91 self._serviceCombo.add_attribute(cell, 'text', 1)
92 self._serviceCombo.set_active(0)
95 "on_loginbutton_clicked": self._on_loginbutton_clicked,
96 "on_loginclose_clicked": self._on_loginclose_clicked,
98 widgetTree.signal_autoconnect(callbackMapping)
100 def request_credentials(self, parentWindow = None):
104 if parentWindow is None:
105 parentWindow = self._parentWindow
107 self._serviceCombo.hide()
108 self._serviceList.clear()
111 self._dialog.set_transient_for(parentWindow)
112 self._dialog.set_default_response(gtk.RESPONSE_OK)
113 response = self._dialog.run()
114 if response != gtk.RESPONSE_OK:
115 raise RuntimeError("Login Cancelled")
117 username = self._usernameEntry.get_text()
118 password = self._passwordEntry.get_text()
119 self._passwordEntry.set_text("")
123 return username, password
125 def request_credentials_from(self,
128 defaultCredentials = ("", "")
133 if parentWindow is None:
134 parentWindow = self._parentWindow
136 self._serviceList.clear()
137 for serviceIdserviceName in services.iteritems():
138 self._serviceList.append(serviceIdserviceName)
139 self._serviceCombo.set_active(0)
140 self._serviceCombo.show()
142 self._usernameEntry.set_text(defaultCredentials[0])
143 self._passwordEntry.set_text(defaultCredentials[1])
146 self._dialog.set_transient_for(parentWindow)
147 self._dialog.set_default_response(gtk.RESPONSE_OK)
148 response = self._dialog.run()
149 if response != gtk.RESPONSE_OK:
150 raise RuntimeError("Login Cancelled")
152 username = self._usernameEntry.get_text()
153 password = self._passwordEntry.get_text()
157 itr = self._serviceCombo.get_active_iter()
158 serviceId = int(self._serviceList.get_value(itr, 0))
159 self._serviceList.clear()
160 return serviceId, username, password
162 def _on_loginbutton_clicked(self, *args):
163 self._dialog.response(gtk.RESPONSE_OK)
165 def _on_loginclose_clicked(self, *args):
166 self._dialog.response(gtk.RESPONSE_CANCEL)
169 class ErrorDisplay(object):
171 def __init__(self, widgetTree):
172 super(ErrorDisplay, self).__init__()
173 self.__errorBox = widgetTree.get_widget("errorEventBox")
174 self.__errorDescription = widgetTree.get_widget("errorDescription")
175 self.__errorClose = widgetTree.get_widget("errorClose")
176 self.__parentBox = self.__errorBox.get_parent()
178 self.__errorBox.connect("button_release_event", self._on_close)
181 self.__parentBox.remove(self.__errorBox)
183 def push_message_with_lock(self, message):
185 self.push_message(message)
187 def push_message(self, message):
188 if 0 < len(self.__messages):
189 self.__messages.append(message)
191 self.__show_message(message)
193 def push_exception_with_lock(self, exception = None):
195 self.push_exception(exception)
197 def push_exception(self, exception = None):
198 if exception is None:
199 userMessage = str(sys.exc_value)
200 warningMessage = str(traceback.format_exc())
202 userMessage = str(exception)
203 warningMessage = str(exception)
204 self.push_message(userMessage)
205 warnings.warn(warningMessage, stacklevel=3)
207 def pop_message(self):
208 if 0 < len(self.__messages):
209 self.__show_message(self.__messages[0])
210 del self.__messages[0]
212 self.__hide_message()
214 def _on_close(self, *args):
217 def __show_message(self, message):
218 self.__errorDescription.set_text(message)
219 self.__parentBox.pack_start(self.__errorBox, False, False)
220 self.__parentBox.reorder_child(self.__errorBox, 1)
222 def __hide_message(self):
223 self.__errorDescription.set_text("")
224 self.__parentBox.remove(self.__errorBox)
227 class DummyErrorDisplay(object):
230 super(DummyErrorDisplay, self).__init__()
234 def push_message_with_lock(self, message):
235 self.push_message(message)
237 def push_message(self, message):
238 if 0 < len(self.__messages):
239 self.__messages.append(message)
241 self.__show_message(message)
243 def push_exception(self, exception = None):
244 if exception is None:
245 warningMessage = traceback.format_exc()
247 warningMessage = exception
248 warnings.warn(warningMessage, stacklevel=3)
250 def pop_message(self):
251 if 0 < len(self.__messages):
252 self.__show_message(self.__messages[0])
253 del self.__messages[0]
255 def __show_message(self, message):
256 warnings.warn(message, stacklevel=2)
259 class MessageBox(gtk.MessageDialog):
261 def __init__(self, message):
263 gtk.MessageDialog.__init__(
266 gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
271 self.set_default_response(gtk.RESPONSE_OK)
272 self.connect('response', self._handle_clicked)
274 def _handle_clicked(self, *args):
278 class MessageBox2(gtk.MessageDialog):
280 def __init__(self, message):
282 gtk.MessageDialog.__init__(
285 gtk.DIALOG_DESTROY_WITH_PARENT,
290 self.set_default_response(gtk.RESPONSE_OK)
291 self.connect('response', self._handle_clicked)
293 def _handle_clicked(self, *args):
297 class PopupCalendar(object):
299 def __init__(self, parent, displayDate, title = ""):
300 self._displayDate = displayDate
302 self._calendar = gtk.Calendar()
303 self._calendar.select_month(self._displayDate.month, self._displayDate.year)
304 self._calendar.select_day(self._displayDate.day)
305 self._calendar.set_display_options(
306 gtk.CALENDAR_SHOW_HEADING |
307 gtk.CALENDAR_SHOW_DAY_NAMES |
308 gtk.CALENDAR_NO_MONTH_CHANGE |
311 self._calendar.connect("day-selected", self._on_day_selected)
313 self._popupWindow = gtk.Window()
314 self._popupWindow.set_title(title)
315 self._popupWindow.add(self._calendar)
316 self._popupWindow.set_transient_for(parent)
317 self._popupWindow.set_modal(True)
318 self._popupWindow.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
319 self._popupWindow.set_skip_pager_hint(True)
320 self._popupWindow.set_skip_taskbar_hint(True)
323 self._popupWindow.show_all()
325 def _on_day_selected(self, *args):
327 self._calendar.select_month(self._displayDate.month, self._displayDate.year)
328 self._calendar.select_day(self._displayDate.day)
329 except StandardError, e:
330 warnings.warn(e.message)
333 class QuickAddView(object):
335 def __init__(self, widgetTree, errorDisplay, signalSink, prefix):
336 self._errorDisplay = errorDisplay
338 self._signalSink = signalSink
340 self._clipboard = gtk.clipboard_get()
342 self._taskNameEntry = widgetTree.get_widget(prefix+"-nameEntry")
343 self._addTaskButton = widgetTree.get_widget(prefix+"-addButton")
344 self._pasteTaskNameButton = widgetTree.get_widget(prefix+"-pasteNameButton")
345 self._clearTaskNameButton = widgetTree.get_widget(prefix+"-clearNameButton")
347 self._onAddClickedId = None
348 self._onAddReleasedId = None
349 self._addToEditTimerId = None
350 self._onClearId = None
351 self._onPasteId = None
353 def enable(self, manager):
354 self._manager = manager
356 self._onAddId = self._addTaskButton.connect("clicked", self._on_add)
357 self._onAddClickedId = self._addTaskButton.connect("pressed", self._on_add_pressed)
358 self._onAddReleasedId = self._addTaskButton.connect("released", self._on_add_released)
359 self._onPasteId = self._pasteTaskNameButton.connect("clicked", self._on_paste)
360 self._onClearId = self._clearTaskNameButton.connect("clicked", self._on_clear)
365 self._addTaskButton.disconnect(self._onAddId)
366 self._addTaskButton.disconnect(self._onAddClickedId)
367 self._addTaskButton.disconnect(self._onAddReleasedId)
368 self._pasteTaskNameButton.disconnect(self._onPasteId)
369 self._clearTaskNameButton.disconnect(self._onClearId)
371 def set_addability(self, addability):
372 self._addTaskButton.set_sensitive(addability)
374 def _on_add(self, *args):
376 name = self._taskNameEntry.get_text()
377 self._taskNameEntry.set_text("")
379 self._signalSink.stage.send(("add", name))
380 except StandardError, e:
381 self._errorDisplay.push_exception()
383 def _on_add_edit(self, *args):
385 name = self._taskNameEntry.get_text()
386 self._taskNameEntry.set_text("")
388 self._signalSink.stage.send(("add-edit", name))
389 except StandardError, e:
390 self._errorDisplay.push_exception()
392 def _on_add_pressed(self, widget):
394 self._addToEditTimerId = gobject.timeout_add(1000, self._on_add_edit)
395 except StandardError, e:
396 self._errorDisplay.push_exception()
398 def _on_add_released(self, widget):
400 if self._addToEditTimerId is not None:
401 gobject.source_remove(self._addToEditTimerId)
402 self._addToEditTimerId = None
403 except StandardError, e:
404 self._errorDisplay.push_exception()
406 def _on_paste(self, *args):
408 entry = self._taskNameEntry.get_text()
409 addedText = self._clipboard.wait_for_text()
412 self._taskNameEntry.set_text(entry)
413 except StandardError, e:
414 self._errorDisplay.push_exception()
416 def _on_clear(self, *args):
418 self._taskNameEntry.set_text("")
419 except StandardError, e:
420 self._errorDisplay.push_exception()
423 if __name__ == "__main__":
426 cal = PopupCalendar(None, datetime.datetime.now())
427 cal._popupWindow.connect("destroy", lambda w: gtk.main_quit())