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
51 original_func(*args, **kwargs)
53 @functools.wraps(original_func)
54 def delayed_func(*args, **kwargs):
55 gobject.idle_add(execute, (args, kwargs))
60 def synchronous_gtk_message(original_func):
62 @note Idea came from http://www.aclevername.com/articles/python-webgui/
65 @functools.wraps(original_func)
66 def immediate_func(*args, **kwargs):
68 return original_func(*args, **kwargs)
73 class LoginWindow(object):
75 def __init__(self, widgetTree):
79 self._dialog = widgetTree.get_widget("loginDialog")
80 self._parentWindow = widgetTree.get_widget("mainWindow")
81 self._serviceCombo = widgetTree.get_widget("serviceCombo")
82 self._usernameEntry = widgetTree.get_widget("usernameentry")
83 self._passwordEntry = widgetTree.get_widget("passwordentry")
85 self._serviceList = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING)
86 self._serviceCombo.set_model(self._serviceList)
87 cell = gtk.CellRendererText()
88 self._serviceCombo.pack_start(cell, True)
89 self._serviceCombo.add_attribute(cell, 'text', 1)
90 self._serviceCombo.set_active(0)
93 "on_loginbutton_clicked": self._on_loginbutton_clicked,
94 "on_loginclose_clicked": self._on_loginclose_clicked,
96 widgetTree.signal_autoconnect(callbackMapping)
98 def request_credentials(self, parentWindow = None):
102 if parentWindow is None:
103 parentWindow = self._parentWindow
105 self._serviceCombo.hide()
106 self._serviceList.clear()
109 self._dialog.set_transient_for(parentWindow)
110 self._dialog.set_default_response(gtk.RESPONSE_OK)
111 response = self._dialog.run()
112 if response != gtk.RESPONSE_OK:
113 raise RuntimeError("Login Cancelled")
115 username = self._usernameEntry.get_text()
116 password = self._passwordEntry.get_text()
117 self._passwordEntry.set_text("")
121 return username, password
123 def request_credentials_from(self, services, parentWindow = None):
127 if parentWindow is None:
128 parentWindow = self._parentWindow
130 self._serviceList.clear()
131 for serviceIdserviceName in services.iteritems():
132 self._serviceList.append(serviceIdserviceName)
133 self._serviceCombo.set_active(0)
134 self._serviceCombo.show()
137 self._dialog.set_transient_for(parentWindow)
138 self._dialog.set_default_response(gtk.RESPONSE_OK)
139 response = self._dialog.run()
140 if response != gtk.RESPONSE_OK:
141 raise RuntimeError("Login Cancelled")
143 username = self._usernameEntry.get_text()
144 password = self._passwordEntry.get_text()
145 self._passwordEntry.set_text("")
149 itr = self._serviceCombo.get_active_iter()
150 serviceId = int(self._serviceList.get_value(itr, 0))
151 self._serviceList.clear()
152 return serviceId, username, password
154 def _on_loginbutton_clicked(self, *args):
155 self._dialog.response(gtk.RESPONSE_OK)
157 def _on_loginclose_clicked(self, *args):
158 self._dialog.response(gtk.RESPONSE_CANCEL)
161 class ErrorDisplay(object):
163 def __init__(self, widgetTree):
164 super(ErrorDisplay, self).__init__()
165 self.__errorBox = widgetTree.get_widget("errorEventBox")
166 self.__errorDescription = widgetTree.get_widget("errorDescription")
167 self.__errorClose = widgetTree.get_widget("errorClose")
168 self.__parentBox = self.__errorBox.get_parent()
170 self.__errorBox.connect("button_release_event", self._on_close)
173 self.__parentBox.remove(self.__errorBox)
175 def push_message_with_lock(self, message):
177 self.push_message(message)
179 def push_message(self, message):
180 if 0 < len(self.__messages):
181 self.__messages.append(message)
183 self.__show_message(message)
185 def push_exception_with_lock(self, exception = None):
187 self.push_exception(exception)
189 def push_exception(self, exception = None):
190 if exception is None:
191 userMessage = str(sys.exc_value)
192 warningMessage = str(traceback.format_exc())
194 userMessage = str(exception)
195 warningMessage = str(exception)
196 self.push_message(userMessage)
197 warnings.warn(warningMessage, stacklevel=3)
199 def pop_message(self):
200 if 0 < len(self.__messages):
201 self.__show_message(self.__messages[0])
202 del self.__messages[0]
204 self.__hide_message()
206 def _on_close(self, *args):
209 def __show_message(self, message):
210 self.__errorDescription.set_text(message)
211 self.__parentBox.pack_start(self.__errorBox, False, False)
212 self.__parentBox.reorder_child(self.__errorBox, 1)
214 def __hide_message(self):
215 self.__errorDescription.set_text("")
216 self.__parentBox.remove(self.__errorBox)
219 class DummyErrorDisplay(object):
222 super(DummyErrorDisplay, self).__init__()
226 def push_message_with_lock(self, message):
227 self.push_message(message)
229 def push_message(self, message):
230 if 0 < len(self.__messages):
231 self.__messages.append(message)
233 self.__show_message(message)
235 def push_exception(self, exception = None):
236 if exception is None:
237 warningMessage = traceback.format_exc()
239 warningMessage = exception
240 warnings.warn(warningMessage, stacklevel=3)
242 def pop_message(self):
243 if 0 < len(self.__messages):
244 self.__show_message(self.__messages[0])
245 del self.__messages[0]
247 def __show_message(self, message):
248 warnings.warn(message, stacklevel=2)
251 class MessageBox(gtk.MessageDialog):
253 def __init__(self, message):
255 gtk.MessageDialog.__init__(
258 gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
263 self.set_default_response(gtk.RESPONSE_OK)
264 self.connect('response', self._handle_clicked)
266 def _handle_clicked(self, *args):
270 class MessageBox2(gtk.MessageDialog):
272 def __init__(self, message):
274 gtk.MessageDialog.__init__(
277 gtk.DIALOG_DESTROY_WITH_PARENT,
282 self.set_default_response(gtk.RESPONSE_OK)
283 self.connect('response', self._handle_clicked)
285 def _handle_clicked(self, *args):
289 class PopupCalendar(object):
291 def __init__(self, parent, displayDate, title = ""):
292 self._displayDate = displayDate
294 self._calendar = gtk.Calendar()
295 self._calendar.select_month(self._displayDate.month, self._displayDate.year)
296 self._calendar.select_day(self._displayDate.day)
297 self._calendar.set_display_options(
298 gtk.CALENDAR_SHOW_HEADING |
299 gtk.CALENDAR_SHOW_DAY_NAMES |
300 gtk.CALENDAR_NO_MONTH_CHANGE |
303 self._calendar.connect("day-selected", self._on_day_selected)
305 self._popupWindow = gtk.Window()
306 self._popupWindow.set_title(title)
307 self._popupWindow.add(self._calendar)
308 self._popupWindow.set_transient_for(parent)
309 self._popupWindow.set_modal(True)
310 self._popupWindow.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
311 self._popupWindow.set_skip_pager_hint(True)
312 self._popupWindow.set_skip_taskbar_hint(True)
315 self._popupWindow.show_all()
317 def _on_day_selected(self, *args):
319 self._calendar.select_month(self._displayDate.month, self._displayDate.year)
320 self._calendar.select_day(self._displayDate.day)
321 except StandardError, e:
322 warnings.warn(e.message)
325 class QuickAddView(object):
327 def __init__(self, widgetTree, errorDisplay, signalSink, prefix):
328 self._errorDisplay = errorDisplay
330 self._signalSink = signalSink
332 self._clipboard = gtk.clipboard_get()
334 self._taskNameEntry = widgetTree.get_widget(prefix+"-nameEntry")
335 self._addTaskButton = widgetTree.get_widget(prefix+"-addButton")
336 self._pasteTaskNameButton = widgetTree.get_widget(prefix+"-pasteNameButton")
337 self._clearTaskNameButton = widgetTree.get_widget(prefix+"-clearNameButton")
339 self._onAddClickedId = None
340 self._onAddReleasedId = None
341 self._addToEditTimerId = None
342 self._onClearId = None
343 self._onPasteId = None
345 def enable(self, manager):
346 self._manager = manager
348 self._onAddId = self._addTaskButton.connect("clicked", self._on_add)
349 self._onAddClickedId = self._addTaskButton.connect("pressed", self._on_add_pressed)
350 self._onAddReleasedId = self._addTaskButton.connect("released", self._on_add_released)
351 self._onPasteId = self._pasteTaskNameButton.connect("clicked", self._on_paste)
352 self._onClearId = self._clearTaskNameButton.connect("clicked", self._on_clear)
357 self._addTaskButton.disconnect(self._onAddId)
358 self._addTaskButton.disconnect(self._onAddClickedId)
359 self._addTaskButton.disconnect(self._onAddReleasedId)
360 self._pasteTaskNameButton.disconnect(self._onPasteId)
361 self._clearTaskNameButton.disconnect(self._onClearId)
363 def set_addability(self, addability):
364 self._addTaskButton.set_sensitive(addability)
366 def _on_add(self, *args):
368 name = self._taskNameEntry.get_text()
369 self._taskNameEntry.set_text("")
371 self._signalSink.stage.send(("add", name))
372 except StandardError, e:
373 self._errorDisplay.push_exception()
375 def _on_add_edit(self, *args):
377 name = self._taskNameEntry.get_text()
378 self._taskNameEntry.set_text("")
380 self._signalSink.stage.send(("add-edit", name))
381 except StandardError, e:
382 self._errorDisplay.push_exception()
384 def _on_add_pressed(self, widget):
386 self._addToEditTimerId = gobject.timeout_add(1000, self._on_add_edit)
387 except StandardError, e:
388 self._errorDisplay.push_exception()
390 def _on_add_released(self, widget):
392 if self._addToEditTimerId is not None:
393 gobject.source_remove(self._addToEditTimerId)
394 self._addToEditTimerId = None
395 except StandardError, e:
396 self._errorDisplay.push_exception()
398 def _on_paste(self, *args):
400 entry = self._taskNameEntry.get_text()
401 addedText = self._clipboard.wait_for_text()
404 self._taskNameEntry.set_text(entry)
405 except StandardError, e:
406 self._errorDisplay.push_exception()
408 def _on_clear(self, *args):
410 self._taskNameEntry.set_text("")
411 except StandardError, e:
412 self._errorDisplay.push_exception()
415 if __name__ == "__main__":
418 cal = PopupCalendar(None, datetime.datetime.now())
419 cal._popupWindow.connect("destroy", lambda w: gtk.main_quit())