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, services, parentWindow = None):
129 if parentWindow is None:
130 parentWindow = self._parentWindow
132 self._serviceList.clear()
133 for serviceIdserviceName in services.iteritems():
134 self._serviceList.append(serviceIdserviceName)
135 self._serviceCombo.set_active(0)
136 self._serviceCombo.show()
139 self._dialog.set_transient_for(parentWindow)
140 self._dialog.set_default_response(gtk.RESPONSE_OK)
141 response = self._dialog.run()
142 if response != gtk.RESPONSE_OK:
143 raise RuntimeError("Login Cancelled")
145 username = self._usernameEntry.get_text()
146 password = self._passwordEntry.get_text()
147 self._passwordEntry.set_text("")
151 itr = self._serviceCombo.get_active_iter()
152 serviceId = int(self._serviceList.get_value(itr, 0))
153 self._serviceList.clear()
154 return serviceId, username, password
156 def _on_loginbutton_clicked(self, *args):
157 self._dialog.response(gtk.RESPONSE_OK)
159 def _on_loginclose_clicked(self, *args):
160 self._dialog.response(gtk.RESPONSE_CANCEL)
163 class ErrorDisplay(object):
165 def __init__(self, widgetTree):
166 super(ErrorDisplay, self).__init__()
167 self.__errorBox = widgetTree.get_widget("errorEventBox")
168 self.__errorDescription = widgetTree.get_widget("errorDescription")
169 self.__errorClose = widgetTree.get_widget("errorClose")
170 self.__parentBox = self.__errorBox.get_parent()
172 self.__errorBox.connect("button_release_event", self._on_close)
175 self.__parentBox.remove(self.__errorBox)
177 def push_message_with_lock(self, message):
179 self.push_message(message)
181 def push_message(self, message):
182 if 0 < len(self.__messages):
183 self.__messages.append(message)
185 self.__show_message(message)
187 def push_exception_with_lock(self, exception = None):
189 self.push_exception(exception)
191 def push_exception(self, exception = None):
192 if exception is None:
193 userMessage = str(sys.exc_value)
194 warningMessage = str(traceback.format_exc())
196 userMessage = str(exception)
197 warningMessage = str(exception)
198 self.push_message(userMessage)
199 warnings.warn(warningMessage, stacklevel=3)
201 def pop_message(self):
202 if 0 < len(self.__messages):
203 self.__show_message(self.__messages[0])
204 del self.__messages[0]
206 self.__hide_message()
208 def _on_close(self, *args):
211 def __show_message(self, message):
212 self.__errorDescription.set_text(message)
213 self.__parentBox.pack_start(self.__errorBox, False, False)
214 self.__parentBox.reorder_child(self.__errorBox, 1)
216 def __hide_message(self):
217 self.__errorDescription.set_text("")
218 self.__parentBox.remove(self.__errorBox)
221 class DummyErrorDisplay(object):
224 super(DummyErrorDisplay, self).__init__()
228 def push_message_with_lock(self, message):
229 self.push_message(message)
231 def push_message(self, message):
232 if 0 < len(self.__messages):
233 self.__messages.append(message)
235 self.__show_message(message)
237 def push_exception(self, exception = None):
238 if exception is None:
239 warningMessage = traceback.format_exc()
241 warningMessage = exception
242 warnings.warn(warningMessage, stacklevel=3)
244 def pop_message(self):
245 if 0 < len(self.__messages):
246 self.__show_message(self.__messages[0])
247 del self.__messages[0]
249 def __show_message(self, message):
250 warnings.warn(message, stacklevel=2)
253 class MessageBox(gtk.MessageDialog):
255 def __init__(self, message):
257 gtk.MessageDialog.__init__(
260 gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
265 self.set_default_response(gtk.RESPONSE_OK)
266 self.connect('response', self._handle_clicked)
268 def _handle_clicked(self, *args):
272 class MessageBox2(gtk.MessageDialog):
274 def __init__(self, message):
276 gtk.MessageDialog.__init__(
279 gtk.DIALOG_DESTROY_WITH_PARENT,
284 self.set_default_response(gtk.RESPONSE_OK)
285 self.connect('response', self._handle_clicked)
287 def _handle_clicked(self, *args):
291 class PopupCalendar(object):
293 def __init__(self, parent, displayDate, title = ""):
294 self._displayDate = displayDate
296 self._calendar = gtk.Calendar()
297 self._calendar.select_month(self._displayDate.month, self._displayDate.year)
298 self._calendar.select_day(self._displayDate.day)
299 self._calendar.set_display_options(
300 gtk.CALENDAR_SHOW_HEADING |
301 gtk.CALENDAR_SHOW_DAY_NAMES |
302 gtk.CALENDAR_NO_MONTH_CHANGE |
305 self._calendar.connect("day-selected", self._on_day_selected)
307 self._popupWindow = gtk.Window()
308 self._popupWindow.set_title(title)
309 self._popupWindow.add(self._calendar)
310 self._popupWindow.set_transient_for(parent)
311 self._popupWindow.set_modal(True)
312 self._popupWindow.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
313 self._popupWindow.set_skip_pager_hint(True)
314 self._popupWindow.set_skip_taskbar_hint(True)
317 self._popupWindow.show_all()
319 def _on_day_selected(self, *args):
321 self._calendar.select_month(self._displayDate.month, self._displayDate.year)
322 self._calendar.select_day(self._displayDate.day)
323 except StandardError, e:
324 warnings.warn(e.message)
327 class QuickAddView(object):
329 def __init__(self, widgetTree, errorDisplay, signalSink, prefix):
330 self._errorDisplay = errorDisplay
332 self._signalSink = signalSink
334 self._clipboard = gtk.clipboard_get()
336 self._taskNameEntry = widgetTree.get_widget(prefix+"-nameEntry")
337 self._addTaskButton = widgetTree.get_widget(prefix+"-addButton")
338 self._pasteTaskNameButton = widgetTree.get_widget(prefix+"-pasteNameButton")
339 self._clearTaskNameButton = widgetTree.get_widget(prefix+"-clearNameButton")
341 self._onAddClickedId = None
342 self._onAddReleasedId = None
343 self._addToEditTimerId = None
344 self._onClearId = None
345 self._onPasteId = None
347 def enable(self, manager):
348 self._manager = manager
350 self._onAddId = self._addTaskButton.connect("clicked", self._on_add)
351 self._onAddClickedId = self._addTaskButton.connect("pressed", self._on_add_pressed)
352 self._onAddReleasedId = self._addTaskButton.connect("released", self._on_add_released)
353 self._onPasteId = self._pasteTaskNameButton.connect("clicked", self._on_paste)
354 self._onClearId = self._clearTaskNameButton.connect("clicked", self._on_clear)
359 self._addTaskButton.disconnect(self._onAddId)
360 self._addTaskButton.disconnect(self._onAddClickedId)
361 self._addTaskButton.disconnect(self._onAddReleasedId)
362 self._pasteTaskNameButton.disconnect(self._onPasteId)
363 self._clearTaskNameButton.disconnect(self._onClearId)
365 def set_addability(self, addability):
366 self._addTaskButton.set_sensitive(addability)
368 def _on_add(self, *args):
370 name = self._taskNameEntry.get_text()
371 self._taskNameEntry.set_text("")
373 self._signalSink.stage.send(("add", name))
374 except StandardError, e:
375 self._errorDisplay.push_exception()
377 def _on_add_edit(self, *args):
379 name = self._taskNameEntry.get_text()
380 self._taskNameEntry.set_text("")
382 self._signalSink.stage.send(("add-edit", name))
383 except StandardError, e:
384 self._errorDisplay.push_exception()
386 def _on_add_pressed(self, widget):
388 self._addToEditTimerId = gobject.timeout_add(1000, self._on_add_edit)
389 except StandardError, e:
390 self._errorDisplay.push_exception()
392 def _on_add_released(self, widget):
394 if self._addToEditTimerId is not None:
395 gobject.source_remove(self._addToEditTimerId)
396 self._addToEditTimerId = None
397 except StandardError, e:
398 self._errorDisplay.push_exception()
400 def _on_paste(self, *args):
402 entry = self._taskNameEntry.get_text()
403 addedText = self._clipboard.wait_for_text()
406 self._taskNameEntry.set_text(entry)
407 except StandardError, e:
408 self._errorDisplay.push_exception()
410 def _on_clear(self, *args):
412 self._taskNameEntry.set_text("")
413 except StandardError, e:
414 self._errorDisplay.push_exception()
417 if __name__ == "__main__":
420 cal = PopupCalendar(None, datetime.datetime.now())
421 cal._popupWindow.connect("destroy", lambda w: gtk.main_quit())