4 @todo Add logging support to make debugging random user issues a lot easier
7 from __future__ import with_statement
29 socket.setdefaulttimeout(10)
34 __pretty_app_name__ = "DoneIt"
35 __app_name__ = "doneit"
37 __app_magic__ = 0xdeadbeef
40 '/usr/lib/doneit/doneit.glade',
41 os.path.join(os.path.dirname(__file__), "doneit.glade"),
42 os.path.join(os.path.dirname(__file__), "../lib/doneit.glade"),
45 _user_data = os.path.expanduser("~/.%s/" % __app_name__)
46 _user_settings = "%s/settings.ini" % _user_data
49 self._initDone = False
53 self._deviceIsOnline = True
54 self._connection = None
55 self._fallbackUIName = ""
56 self._defaultUIName = ""
58 for path in self._glade_files:
59 if os.path.isfile(path):
60 self._widgetTree = gtk.glade.XML(path)
63 self.display_error_message("Cannot find doneit.glade")
66 os.makedirs(self._user_data)
71 self._clipboard = gtk.clipboard_get()
72 self.__window = self._widgetTree.get_widget("mainWindow")
73 self.__errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
74 self._prefsDialog = gtk_toolbox.PreferencesDialog(self._widgetTree)
77 self._isFullScreen = False
78 if hildon is not None:
79 self._app = hildon.Program()
80 self.__window = hildon.Window()
81 self._widgetTree.get_widget("mainLayout").reparent(self.__window)
82 self._app.add_window(self.__window)
83 self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
84 self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
86 gtkMenu = self._widgetTree.get_widget("mainMenubar")
88 for child in gtkMenu.get_children():
90 self.__window.set_menu(menu)
93 self.__window.connect("key-press-event", self._on_key_press)
94 self.__window.connect("window-state-event", self._on_window_state_change)
96 pass # warnings.warn("No Hildon", UserWarning, 2)
99 "on_doneit_quit": self._on_close,
100 "on_about": self._on_about_activate,
102 self._widgetTree.signal_autoconnect(callbackMapping)
106 self.__window.set_title("%s" % self.__pretty_app_name__)
107 self.__window.connect("destroy", self._on_close)
108 self.__window.show_all()
110 backgroundSetup = threading.Thread(target=self._idle_setup)
111 backgroundSetup.setDaemon(True)
112 backgroundSetup.start()
114 def _idle_setup(self):
115 # Barebones UI handlers
117 with gtk_toolbox.gtk_lock():
118 nullView = null_view.GtkNull(self._widgetTree)
119 self._todoUIs[nullView.name()] = nullView
120 self._todoUI = nullView
121 self._todoUI.enable()
122 self._fallbackUIName = nullView.name()
124 # Setup maemo specifics
131 self._osso = osso.Context(DoneIt.__app_name__, DoneIt.__version__, False)
132 device = osso.DeviceState(self._osso)
133 device.set_device_state_callback(self._on_device_state_change, 0)
135 pass # warnings.warn("No OSSO", UserWarning, 2)
141 self._connection = None
142 if conic is not None:
143 self._connection = conic.Connection()
144 self._connection.connect("connection-event", self._on_connection_change, self.__app_magic__)
145 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
147 pass # warnings.warn("No Internet Connectivity API ", UserWarning)
149 # Setup costly backends
151 with gtk_toolbox.gtk_lock():
152 rtmView = rtm_view.GtkRtMilk(self._widgetTree, self.__errorDisplay)
153 self._todoUIs[rtmView.name()] = rtmView
154 self._defaultUIName = rtmView.name()
156 config = ConfigParser.SafeConfigParser()
157 config.read(self._user_settings)
158 with gtk_toolbox.gtk_lock():
159 self.load_settings(config)
160 self._widgetTree.get_widget("connectMenuItem").connect("activate", lambda *args: self.switch_ui(self._defaultUIName))
161 self._widgetTree.get_widget("preferencesMenuItem").connect("activate", self._on_prefs)
163 self._initDone = True
165 def display_error_message(self, msg):
169 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
171 def close(dialog, response, editor):
172 editor.about_dialog = None
174 error_dialog.connect("response", close, self)
177 def load_settings(self, config):
181 for todoUI in self._todoUIs.itervalues():
183 todoUI.load_settings(config)
184 except ConfigParser.NoSectionError, e:
186 "Settings file %s is missing section %s" % (
194 activeUIName = config.get(self.__pretty_app_name__, "active")
195 except ConfigParser.NoSectionError, e:
198 "Settings file %s is missing section %s" % (
206 self.switch_ui(activeUIName)
208 self.switch_ui(self._defaultUIName)
210 def save_settings(self, config):
212 @note Thread Agnostic
214 config.add_section(self.__pretty_app_name__)
215 config.set(self.__pretty_app_name__, "active", self._todoUI.name())
217 for todoUI in self._todoUIs.itervalues():
218 todoUI.save_settings(config)
221 return (ui for ui in self._todoUIs.iteritems())
223 def get_default_ui(self):
224 return self._defaultUIName
226 def switch_ui(self, uiName):
230 newActiveUI = self._todoUIs[uiName]
234 return # User cancelled the operation
236 self._todoUI.disable()
237 self._todoUI = newActiveUI
238 self._todoUI.enable()
240 if uiName != self._fallbackUIName:
241 self._defaultUIName = uiName
243 def _save_settings(self):
245 @note Thread Agnostic
247 config = ConfigParser.SafeConfigParser()
248 self.save_settings(config)
249 with open(self._user_settings, "wb") as configFile:
250 config.write(configFile)
252 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
254 For system_inactivity, we have no background tasks to pause
256 @note Hildon specific
261 if save_unsaved_data or shutdown:
262 self._save_settings()
264 def _on_connection_change(self, connection, event, magicIdentifier):
266 @note Hildon specific
270 status = event.get_status()
271 error = event.get_error()
272 iap_id = event.get_iap_id()
273 bearer = event.get_bearer_type()
275 if status == conic.STATUS_CONNECTED:
276 self._deviceIsOnline = True
278 self.switch_ui(self._defaultUIName)
279 elif status == conic.STATUS_DISCONNECTED:
280 self._deviceIsOnline = False
282 self.switch_ui(self._fallbackUIName)
284 def _on_window_state_change(self, widget, event, *args):
286 @note Hildon specific
288 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
289 self._isFullScreen = True
291 self._isFullScreen = False
293 def _on_close(self, *args, **kwds):
295 if self._osso is not None:
299 self._save_settings()
303 def _on_key_press(self, widget, event, *args):
305 @note Hildon specific
307 if event.keyval == gtk.keysyms.F6:
308 if self._isFullScreen:
309 self.__window.unfullscreen()
311 self.__window.fullscreen()
313 def _on_logout(self, *args):
314 if not self._initDone:
317 self._todoUI.logout()
318 self.switch_ui(self._fallbackUIName)
320 def _on_prefs(self, *args):
321 if not self._initDone:
324 self._prefsDialog.enable()
326 self._prefsDialog.run(self)
328 self._prefsDialog.disable()
330 def _on_about_activate(self, *args):
331 dlg = gtk.AboutDialog()
332 dlg.set_name(self.__pretty_app_name__)
333 dlg.set_version(self.__version__)
334 dlg.set_copyright("Copyright 2008 - LGPL")
336 dlg.set_website("http://doneit.garage.maemo.org")
337 dlg.set_authors(["Ed Page"])
345 failureCount, testCount = doctest.testmod()
347 print "Tests Successful"
354 gtk.gdk.threads_init()
356 if hildon is not None:
357 gtk.set_application_name(DoneIt.__pretty_app_name__)
362 class DummyOptions(object):
368 if __name__ == "__main__":
369 if len(sys.argv) > 1:
375 if optparse is not None:
376 parser = optparse.OptionParser()
377 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
378 (commandOptions, commandArgs) = parser.parse_args()
380 commandOptions = DummyOptions()
383 if commandOptions.test: