#!/usr/bin/python
+"""
+@todo Add logging support to make debugging random user issues a lot easier
+@todo See Tasque for UI ideas http://live.gnome.org/Tasque/Screenshots
+"""
-from __future__ import with_statement
+from __future__ import with_statement
import sys
import gc
import os
import threading
+import ConfigParser
+import socket
import warnings
+import gobject
import gtk
import gtk.glade
import gtk_toolbox
+socket.setdefaulttimeout(10)
+
+
+class PreferencesDialog(object):
+
+ def __init__(self, widgetTree):
+ self._backendList = gtk.ListStore(gobject.TYPE_STRING)
+ self._backendCell = gtk.CellRendererText()
+
+ self._dialog = widgetTree.get_widget("preferencesDialog")
+ self._backendSelector = widgetTree.get_widget("prefsBackendSelector")
+ self._applyButton = widgetTree.get_widget("applyPrefsButton")
+ self._cancelButton = widgetTree.get_widget("cancelPrefsButton")
+
+ self._onApplyId = None
+ self._onCancelId = None
+
+ def enable(self):
+ self._dialog.set_default_size(800, 300)
+ self._onApplyId = self._applyButton.connect("clicked", self._on_apply_clicked)
+ self._onCancelId = self._cancelButton.connect("clicked", self._on_cancel_clicked)
+
+ cell = self._backendCell
+ self._backendSelector.pack_start(cell, True)
+ self._backendSelector.add_attribute(cell, 'text', 0)
+ self._backendSelector.set_model(self._backendList)
+
+ def disable(self):
+ self._applyButton.disconnect(self._onApplyId)
+ self._cancelButton.disconnect(self._onCancelId)
+
+ self._backendList.clear()
+ self._backendSelector.set_model(None)
+
+ def run(self, app, parentWindow = None):
+ if parentWindow is not None:
+ self._dialog.set_transient_for(parentWindow)
+
+ self._backendList.clear()
+ activeIndex = 0
+ for i, (uiName, ui) in enumerate(app.get_uis()):
+ self._backendList.append((uiName, ))
+ if uiName == app.get_default_ui():
+ activeIndex = i
+ self._backendSelector.set_active(activeIndex)
+
+ try:
+ response = self._dialog.run()
+ if response != gtk.RESPONSE_OK:
+ raise RuntimeError("Edit Cancelled")
+ finally:
+ self._dialog.hide()
+
+ backendName = self._backendSelector.get_active_text()
+ app.switch_ui(backendName)
+
+ def _on_apply_clicked(self, *args):
+ self._dialog.response(gtk.RESPONSE_OK)
+
+ def _on_cancel_clicked(self, *args):
+ self._dialog.response(gtk.RESPONSE_CANCEL)
+
+
class DoneIt(object):
__pretty_app_name__ = "DoneIt"
__app_name__ = "doneit"
- __version__ = "0.3.0"
+ __version__ = "0.3.1"
__app_magic__ = 0xdeadbeef
_glade_files = [
os.path.join(os.path.dirname(__file__), "../lib/doneit.glade"),
]
- _user_data = os.path.expanduser("~/.%s/" % __app_name__)
+ _user_data = os.path.join(os.path.expanduser("~"), ".%s" % __app_name__)
_user_settings = "%s/settings.ini" % _user_data
def __init__(self):
- self._todoUIs = []
+ self._initDone = False
+ self._todoUIs = {}
self._todoUI = None
self._osso = None
self._deviceIsOnline = True
self._connection = None
+ self._fallbackUIName = ""
+ self._defaultUIName = ""
for path in self._glade_files:
if os.path.isfile(path):
self._clipboard = gtk.clipboard_get()
self.__window = self._widgetTree.get_widget("mainWindow")
self.__errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
+ self._prefsDialog = PreferencesDialog(self._widgetTree)
self._app = None
self._isFullScreen = False
if hildon is not None:
self._app = hildon.Program()
+ oldWindow = self.__window
self.__window = hildon.Window()
- self._widgetTree.get_widget("mainLayout").reparent(self.__window)
+ oldWindow.get_child().reparent(self.__window)
self._app.add_window(self.__window)
- self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
- self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
- self._widgetTree.get_widget("projectsCombo").get_child().set_property('hildon-input-mode', (1 << 4))
+
+ try:
+ self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
+ self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
+ except TypeError, e:
+ warnings.warn(e.message)
gtkMenu = self._widgetTree.get_widget("mainMenubar")
menu = gtk.Menu()
else:
pass # warnings.warn("No Hildon", UserWarning, 2)
+ if hildon is None:
+ self.__window.set_title("%s" % self.__pretty_app_name__)
+
callbackMapping = {
"on_doneit_quit": self._on_close,
- "on_paste": self._on_paste,
"on_about": self._on_about_activate,
}
self._widgetTree.signal_autoconnect(callbackMapping)
- if self.__window:
- if hildon is None:
- self.__window.set_title("%s" % self.__pretty_app_name__)
- self.__window.connect("destroy", self._on_close)
- self.__window.show_all()
+ self.__window.connect("destroy", self._on_close)
+ self.__window.show_all()
backgroundSetup = threading.Thread(target=self._idle_setup)
backgroundSetup.setDaemon(True)
def _idle_setup(self):
# Barebones UI handlers
- import gtk_null
- gtk.gdk.threads_enter()
- try:
- self._todoUIs = [
- gtk_null.GtkNull(self._widgetTree),
- ]
- finally:
- gtk.gdk.threads_leave()
+ import null_view
+ with gtk_toolbox.gtk_lock():
+ nullView = null_view.GtkNull(self._widgetTree)
+ self._todoUIs[nullView.name()] = nullView
+ self._todoUI = nullView
+ self._todoUI.enable()
+ self._fallbackUIName = nullView.name()
# Setup maemo specifics
try:
pass # warnings.warn("No Internet Connectivity API ", UserWarning)
# Setup costly backends
- import gtk_rtmilk
- gtk.gdk.threads_enter()
- try:
- self._todoUIs.extend([
- gtk_rtmilk.GtkRtMilk(self._widgetTree),
- ])
- self._todoUI = self._todoUIs[1]
- self._todoUI.enable()
- finally:
- gtk.gdk.threads_leave()
+ import rtm_view
+ with gtk_toolbox.gtk_lock():
+ rtmView = rtm_view.RtmView(self._widgetTree, self.__errorDisplay)
+ self._todoUIs[rtmView.name()] = rtmView
+
+ import file_view
+ defaultStoragePath = "%s/data.txt" % self._user_data
+ with gtk_toolbox.gtk_lock():
+ fileView = file_view.FileView(self._widgetTree, self.__errorDisplay, defaultStoragePath)
+ self._todoUIs[fileView.name()] = fileView
+
+ self._defaultUIName = fileView.name()
+
+ config = ConfigParser.SafeConfigParser()
+ config.read(self._user_settings)
+ with gtk_toolbox.gtk_lock():
+ self.load_settings(config)
+ self._widgetTree.get_widget("connectMenuItem").connect("activate", lambda *args: self.switch_ui(self._defaultUIName))
+ self._widgetTree.get_widget("preferencesMenuItem").connect("activate", self._on_prefs)
+
+ self._initDone = True
def display_error_message(self, msg):
+ """
+ @note UI Thread
+ """
error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
def close(dialog, response, editor):
error_dialog.connect("response", close, self)
error_dialog.run()
+ def load_settings(self, config):
+ """
+ @note UI Thread
+ """
+ for todoUI in self._todoUIs.itervalues():
+ try:
+ todoUI.load_settings(config)
+ except ConfigParser.NoSectionError, e:
+ warnings.warn(
+ "Settings file %s is missing section %s" % (
+ self._user_settings,
+ e.section,
+ ),
+ stacklevel=2
+ )
+
+ try:
+ activeUIName = config.get(self.__pretty_app_name__, "active")
+ except ConfigParser.NoSectionError, e:
+ activeUIName = ""
+ warnings.warn(
+ "Settings file %s is missing section %s" % (
+ self._user_settings,
+ e.section,
+ ),
+ stacklevel=2
+ )
+
+ try:
+ self.switch_ui(activeUIName)
+ except KeyError, e:
+ self.switch_ui(self._defaultUIName)
+
+ def save_settings(self, config):
+ """
+ @note Thread Agnostic
+ """
+ config.add_section(self.__pretty_app_name__)
+ config.set(self.__pretty_app_name__, "active", self._todoUI.name())
+
+ for todoUI in self._todoUIs.itervalues():
+ todoUI.save_settings(config)
+
+ def get_uis(self):
+ return (ui for ui in self._todoUIs.iteritems())
+
+ def get_default_ui(self):
+ return self._defaultUIName
+
+ def switch_ui(self, uiName):
+ """
+ @note UI Thread
+ """
+ newActiveUI = self._todoUIs[uiName]
+ try:
+ newActiveUI.login()
+ except RuntimeError:
+ return # User cancelled the operation
+
+ self._todoUI.disable()
+ self._todoUI = newActiveUI
+ self._todoUI.enable()
+
+ if uiName != self._fallbackUIName:
+ self._defaultUIName = uiName
+
+ def _save_settings(self):
+ """
+ @note Thread Agnostic
+ """
+ config = ConfigParser.SafeConfigParser()
+ self.save_settings(config)
+ with open(self._user_settings, "wb") as configFile:
+ config.write(configFile)
+
def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
"""
For system_inactivity, we have no background tasks to pause
gc.collect()
if save_unsaved_data or shutdown:
- pass
+ self._save_settings()
def _on_connection_change(self, connection, event, magicIdentifier):
"""
if status == conic.STATUS_CONNECTED:
self._deviceIsOnline = True
+ if self._initDone:
+ self.switch_ui(self._defaultUIName)
elif status == conic.STATUS_DISCONNECTED:
self._deviceIsOnline = False
+ if self._initDone:
+ self.switch_ui(self._fallbackUIName)
def _on_window_state_change(self, widget, event, *args):
"""
self._isFullScreen = False
def _on_close(self, *args, **kwds):
- if self._osso is not None:
- self._osso.close()
-
try:
- pass
+ if self._osso is not None:
+ self._osso.close()
+
+ if self._initDone:
+ self._save_settings()
finally:
gtk.main_quit()
- def _on_paste(self, *args):
- pass
-
def _on_key_press(self, widget, event, *args):
"""
@note Hildon specific
else:
self.__window.fullscreen()
+ def _on_logout(self, *args):
+ if not self._initDone:
+ return
+
+ self._todoUI.logout()
+ self.switch_ui(self._fallbackUIName)
+
+ def _on_prefs(self, *args):
+ if not self._initDone:
+ return
+
+ self._prefsDialog.enable()
+ try:
+ self._prefsDialog.run(self)
+ finally:
+ self._prefsDialog.disable()
+
def _on_about_activate(self, *args):
dlg = gtk.AboutDialog()
dlg.set_name(self.__pretty_app_name__)
dlg.set_version(self.__version__)
dlg.set_copyright("Copyright 2008 - LGPL")
dlg.set_comments("")
- dlg.set_website("")
- dlg.set_authors([""])
+ dlg.set_website("http://doneit.garage.maemo.org")
+ dlg.set_authors(["Ed Page"])
dlg.run()
dlg.destroy()