4 @todo Add logging support to make debugging random user issues a lot easier
7 from __future__ import with_statement
30 socket.setdefaulttimeout(10)
33 class PreferencesDialog(object):
35 def __init__(self, widgetTree):
36 self._backendList = gtk.ListStore(gobject.TYPE_STRING)
37 self._backendCell = gtk.CellRendererText()
39 self._dialog = widgetTree.get_widget("preferencesDialog")
40 self._backendSelector = widgetTree.get_widget("prefsBackendSelector")
41 self._applyButton = widgetTree.get_widget("applyPrefsButton")
42 self._cancelButton = widgetTree.get_widget("cancelPrefsButton")
44 self._onApplyId = None
45 self._onCancelId = None
48 self._dialog.set_default_size(800, 300)
49 self._onApplyId = self._applyButton.connect("clicked", self._on_apply_clicked)
50 self._onCancelId = self._cancelButton.connect("clicked", self._on_cancel_clicked)
52 cell = self._backendCell
53 self._backendSelector.pack_start(cell, True)
54 self._backendSelector.add_attribute(cell, 'text', 0)
55 self._backendSelector.set_model(self._backendList)
58 self._applyButton.disconnect(self._onApplyId)
59 self._cancelButton.disconnect(self._onCancelId)
61 self._backendList.clear()
62 self._backendSelector.set_model(None)
64 def run(self, app, parentWindow = None):
65 if parentWindow is not None:
66 self._dialog.set_transient_for(parentWindow)
68 self._backendList.clear()
70 for i, (uiName, ui) in enumerate(app.get_uis()):
71 self._backendList.append((uiName, ))
72 if uiName == app.get_default_ui():
74 self._backendSelector.set_active(activeIndex)
77 response = self._dialog.run()
78 if response != gtk.RESPONSE_OK:
79 raise RuntimeError("Edit Cancelled")
83 backendName = self._backendSelector.get_active_text()
84 app.switch_ui(backendName)
86 def _on_apply_clicked(self, *args):
87 self._dialog.response(gtk.RESPONSE_OK)
89 def _on_cancel_clicked(self, *args):
90 self._dialog.response(gtk.RESPONSE_CANCEL)
95 __pretty_app_name__ = "DoneIt"
96 __app_name__ = "doneit"
98 __app_magic__ = 0xdeadbeef
101 '/usr/lib/doneit/doneit.glade',
102 os.path.join(os.path.dirname(__file__), "doneit.glade"),
103 os.path.join(os.path.dirname(__file__), "../lib/doneit.glade"),
106 _user_data = os.path.expanduser("~/.%s/" % __app_name__)
107 _user_settings = "%s/settings.ini" % _user_data
110 self._initDone = False
114 self._deviceIsOnline = True
115 self._connection = None
116 self._fallbackUIName = ""
117 self._defaultUIName = ""
119 for path in self._glade_files:
120 if os.path.isfile(path):
121 self._widgetTree = gtk.glade.XML(path)
124 self.display_error_message("Cannot find doneit.glade")
127 os.makedirs(self._user_data)
132 self._clipboard = gtk.clipboard_get()
133 self.__window = self._widgetTree.get_widget("mainWindow")
134 self.__errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
135 self._prefsDialog = PreferencesDialog(self._widgetTree)
138 self._isFullScreen = False
139 if hildon is not None:
140 self._app = hildon.Program()
141 self.__window = hildon.Window()
142 self._widgetTree.get_widget("mainLayout").reparent(self.__window)
143 self._app.add_window(self.__window)
144 self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
145 self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
147 gtkMenu = self._widgetTree.get_widget("mainMenubar")
149 for child in gtkMenu.get_children():
151 self.__window.set_menu(menu)
154 self.__window.connect("key-press-event", self._on_key_press)
155 self.__window.connect("window-state-event", self._on_window_state_change)
157 pass # warnings.warn("No Hildon", UserWarning, 2)
160 "on_doneit_quit": self._on_close,
161 "on_about": self._on_about_activate,
163 self._widgetTree.signal_autoconnect(callbackMapping)
167 self.__window.set_title("%s" % self.__pretty_app_name__)
168 self.__window.connect("destroy", self._on_close)
169 self.__window.show_all()
171 backgroundSetup = threading.Thread(target=self._idle_setup)
172 backgroundSetup.setDaemon(True)
173 backgroundSetup.start()
175 def _idle_setup(self):
176 # Barebones UI handlers
178 with gtk_toolbox.gtk_lock():
179 nullView = null_view.GtkNull(self._widgetTree)
180 self._todoUIs[nullView.name()] = nullView
181 self._todoUI = nullView
182 self._todoUI.enable()
183 self._fallbackUIName = nullView.name()
185 # Setup maemo specifics
192 self._osso = osso.Context(DoneIt.__app_name__, DoneIt.__version__, False)
193 device = osso.DeviceState(self._osso)
194 device.set_device_state_callback(self._on_device_state_change, 0)
196 pass # warnings.warn("No OSSO", UserWarning, 2)
202 self._connection = None
203 if conic is not None:
204 self._connection = conic.Connection()
205 self._connection.connect("connection-event", self._on_connection_change, self.__app_magic__)
206 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
208 pass # warnings.warn("No Internet Connectivity API ", UserWarning)
210 # Setup costly backends
212 with gtk_toolbox.gtk_lock():
213 rtmView = rtm_view.RtmView(self._widgetTree, self.__errorDisplay)
214 self._todoUIs[rtmView.name()] = rtmView
215 self._defaultUIName = rtmView.name()
217 config = ConfigParser.SafeConfigParser()
218 config.read(self._user_settings)
219 with gtk_toolbox.gtk_lock():
220 self.load_settings(config)
221 self._widgetTree.get_widget("connectMenuItem").connect("activate", lambda *args: self.switch_ui(self._defaultUIName))
222 self._widgetTree.get_widget("preferencesMenuItem").connect("activate", self._on_prefs)
224 self._initDone = True
226 def display_error_message(self, msg):
230 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
232 def close(dialog, response, editor):
233 editor.about_dialog = None
235 error_dialog.connect("response", close, self)
238 def load_settings(self, config):
242 for todoUI in self._todoUIs.itervalues():
244 todoUI.load_settings(config)
245 except ConfigParser.NoSectionError, e:
247 "Settings file %s is missing section %s" % (
255 activeUIName = config.get(self.__pretty_app_name__, "active")
256 except ConfigParser.NoSectionError, e:
259 "Settings file %s is missing section %s" % (
267 self.switch_ui(activeUIName)
269 self.switch_ui(self._defaultUIName)
271 def save_settings(self, config):
273 @note Thread Agnostic
275 config.add_section(self.__pretty_app_name__)
276 config.set(self.__pretty_app_name__, "active", self._todoUI.name())
278 for todoUI in self._todoUIs.itervalues():
279 todoUI.save_settings(config)
282 return (ui for ui in self._todoUIs.iteritems())
284 def get_default_ui(self):
285 return self._defaultUIName
287 def switch_ui(self, uiName):
291 newActiveUI = self._todoUIs[uiName]
295 return # User cancelled the operation
297 self._todoUI.disable()
298 self._todoUI = newActiveUI
299 self._todoUI.enable()
301 if uiName != self._fallbackUIName:
302 self._defaultUIName = uiName
304 def _save_settings(self):
306 @note Thread Agnostic
308 config = ConfigParser.SafeConfigParser()
309 self.save_settings(config)
310 with open(self._user_settings, "wb") as configFile:
311 config.write(configFile)
313 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
315 For system_inactivity, we have no background tasks to pause
317 @note Hildon specific
322 if save_unsaved_data or shutdown:
323 self._save_settings()
325 def _on_connection_change(self, connection, event, magicIdentifier):
327 @note Hildon specific
331 status = event.get_status()
332 error = event.get_error()
333 iap_id = event.get_iap_id()
334 bearer = event.get_bearer_type()
336 if status == conic.STATUS_CONNECTED:
337 self._deviceIsOnline = True
339 self.switch_ui(self._defaultUIName)
340 elif status == conic.STATUS_DISCONNECTED:
341 self._deviceIsOnline = False
343 self.switch_ui(self._fallbackUIName)
345 def _on_window_state_change(self, widget, event, *args):
347 @note Hildon specific
349 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
350 self._isFullScreen = True
352 self._isFullScreen = False
354 def _on_close(self, *args, **kwds):
356 if self._osso is not None:
360 self._save_settings()
364 def _on_key_press(self, widget, event, *args):
366 @note Hildon specific
368 if event.keyval == gtk.keysyms.F6:
369 if self._isFullScreen:
370 self.__window.unfullscreen()
372 self.__window.fullscreen()
374 def _on_logout(self, *args):
375 if not self._initDone:
378 self._todoUI.logout()
379 self.switch_ui(self._fallbackUIName)
381 def _on_prefs(self, *args):
382 if not self._initDone:
385 self._prefsDialog.enable()
387 self._prefsDialog.run(self)
389 self._prefsDialog.disable()
391 def _on_about_activate(self, *args):
392 dlg = gtk.AboutDialog()
393 dlg.set_name(self.__pretty_app_name__)
394 dlg.set_version(self.__version__)
395 dlg.set_copyright("Copyright 2008 - LGPL")
397 dlg.set_website("http://doneit.garage.maemo.org")
398 dlg.set_authors(["Ed Page"])
406 failureCount, testCount = doctest.testmod()
408 print "Tests Successful"
415 gtk.gdk.threads_init()
417 if hildon is not None:
418 gtk.set_application_name(DoneIt.__pretty_app_name__)
423 class DummyOptions(object):
429 if __name__ == "__main__":
430 if len(sys.argv) > 1:
436 if optparse is not None:
437 parser = optparse.OptionParser()
438 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
439 (commandOptions, commandArgs) = parser.parse_args()
441 commandOptions = DummyOptions()
444 if commandOptions.test: