4 @todo Alt Views (Besides task list)
6 Using new api widgets people are developing)
7 Integrate GPS w/ fallback to default location
8 Use locations for mapping
9 Quick search (OR within a property type, and between property types)
10 Drop down for multi selecting priority
11 Drop down for multi selecting tags
12 Drop down for multi selecting locations
13 Calendar selector for choosing due date range
14 @todo Add logging support to make debugging random user issues a lot easier
17 from __future__ import with_statement
40 __pretty_app_name__ = "DoneIt"
41 __app_name__ = "doneit"
43 __app_magic__ = 0xdeadbeef
46 '/usr/lib/doneit/doneit.glade',
47 os.path.join(os.path.dirname(__file__), "doneit.glade"),
48 os.path.join(os.path.dirname(__file__), "../lib/doneit.glade"),
51 _user_data = os.path.expanduser("~/.%s/" % __app_name__)
52 _user_settings = "%s/settings.ini" % _user_data
58 self._deviceIsOnline = True
59 self._connection = None
60 self._fallbackUIName = ""
61 self._defaultUIName = ""
63 for path in self._glade_files:
64 if os.path.isfile(path):
65 self._widgetTree = gtk.glade.XML(path)
68 self.display_error_message("Cannot find doneit.glade")
71 os.makedirs(self._user_data)
76 self._clipboard = gtk.clipboard_get()
77 self.__window = self._widgetTree.get_widget("mainWindow")
78 self.__errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
81 self._isFullScreen = False
82 if hildon is not None:
83 self._app = hildon.Program()
84 self.__window = hildon.Window()
85 self._widgetTree.get_widget("mainLayout").reparent(self.__window)
86 self._app.add_window(self.__window)
87 self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
88 self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
89 self._widgetTree.get_widget("projectsCombo").get_child().set_property('hildon-input-mode', (1 << 4))
91 gtkMenu = self._widgetTree.get_widget("mainMenubar")
93 for child in gtkMenu.get_children():
95 self.__window.set_menu(menu)
98 self.__window.connect("key-press-event", self._on_key_press)
99 self.__window.connect("window-state-event", self._on_window_state_change)
101 pass # warnings.warn("No Hildon", UserWarning, 2)
104 "on_doneit_quit": self._on_close,
105 "on_paste": self._on_paste,
106 "on_about": self._on_about_activate,
108 self._widgetTree.signal_autoconnect(callbackMapping)
112 self.__window.set_title("%s" % self.__pretty_app_name__)
113 self.__window.connect("destroy", self._on_close)
114 self.__window.show_all()
116 backgroundSetup = threading.Thread(target=self._idle_setup)
117 backgroundSetup.setDaemon(True)
118 backgroundSetup.start()
120 def _idle_setup(self):
121 # Barebones UI handlers
123 with gtk_toolbox.gtk_lock():
124 nullView = null_view.GtkNull(self._widgetTree)
125 self._todoUIs[nullView.name()] = nullView
126 self._todoUI = nullView
127 self._todoUI.enable()
128 self._fallbackUIName = nullView.name()
130 # Setup maemo specifics
137 self._osso = osso.Context(DoneIt.__app_name__, DoneIt.__version__, False)
138 device = osso.DeviceState(self._osso)
139 device.set_device_state_callback(self._on_device_state_change, 0)
141 pass # warnings.warn("No OSSO", UserWarning, 2)
147 self._connection = None
148 if conic is not None:
149 self._connection = conic.Connection()
150 self._connection.connect("connection-event", self._on_connection_change, self.__app_magic__)
151 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
153 pass # warnings.warn("No Internet Connectivity API ", UserWarning)
155 # Setup costly backends
157 with gtk_toolbox.gtk_lock():
158 rtmView = rtm_view.GtkRtMilk(self._widgetTree, self.__errorDisplay)
159 self._todoUIs[rtmView.name()] = rtmView
160 self._defaultUIName = rtmView.name()
162 config = ConfigParser.SafeConfigParser()
163 config.read(self._user_settings)
164 with gtk_toolbox.gtk_lock():
165 self.load_settings(config)
167 def display_error_message(self, msg):
171 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
173 def close(dialog, response, editor):
174 editor.about_dialog = None
176 error_dialog.connect("response", close, self)
179 def load_settings(self, config):
183 for todoUI in self._todoUIs.itervalues():
185 todoUI.load_settings(config)
186 except ConfigParser.NoSectionError, e:
188 "Settings file %s is missing section %s" % (
196 activeUIName = config.get(self.__pretty_app_name__, "active")
197 except ConfigParser.NoSectionError, e:
200 "Settings file %s is missing section %s" % (
208 self._switch_ui(activeUIName)
210 self._switch_ui(self._defaultUIName)
212 def save_settings(self, config):
214 @note Thread Agnostic
216 config.add_section(self.__pretty_app_name__)
217 config.set(self.__pretty_app_name__, "active", self._todoUI.name())
219 for todoUI in self._todoUIs.itervalues():
220 todoUI.save_settings(config)
222 def _switch_ui(self, uiName):
226 newActiveUI = self._todoUIs[uiName]
230 return # User cancelled the operation
232 self._todoUI.disable()
233 self._todoUI = newActiveUI
234 self._todoUI.enable()
236 if uiName != self._fallbackUIName:
237 self._defaultUIName = uiName
239 def _save_settings(self):
241 @note Thread Agnostic
243 config = ConfigParser.SafeConfigParser()
244 self.save_settings(config)
245 with open(self._user_settings, "wb") as configFile:
246 config.write(configFile)
248 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
250 For system_inactivity, we have no background tasks to pause
252 @note Hildon specific
257 if save_unsaved_data or shutdown:
258 self._save_settings()
260 def _on_connection_change(self, connection, event, magicIdentifier):
262 @note Hildon specific
266 status = event.get_status()
267 error = event.get_error()
268 iap_id = event.get_iap_id()
269 bearer = event.get_bearer_type()
271 if status == conic.STATUS_CONNECTED:
272 self._deviceIsOnline = True
273 self._switch_ui(self._defaultUIName)
274 elif status == conic.STATUS_DISCONNECTED:
275 self._deviceIsOnline = False
276 self._switch_ui(self._fallbackUIName)
278 def _on_window_state_change(self, widget, event, *args):
280 @note Hildon specific
282 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
283 self._isFullScreen = True
285 self._isFullScreen = False
287 def _on_close(self, *args, **kwds):
289 if self._osso is not None:
292 self._save_settings()
296 def _on_paste(self, *args):
299 def _on_key_press(self, widget, event, *args):
301 @note Hildon specific
303 if event.keyval == gtk.keysyms.F6:
304 if self._isFullScreen:
305 self.__window.unfullscreen()
307 self.__window.fullscreen()
309 def _on_logout(self, *args):
310 self._todoUI.logout()
311 self._switch_ui(self._fallbackUIName)
313 def _on_about_activate(self, *args):
314 dlg = gtk.AboutDialog()
315 dlg.set_name(self.__pretty_app_name__)
316 dlg.set_version(self.__version__)
317 dlg.set_copyright("Copyright 2008 - LGPL")
320 dlg.set_authors([""])
328 failureCount, testCount = doctest.testmod()
330 print "Tests Successful"
337 gtk.gdk.threads_init()
339 if hildon is not None:
340 gtk.set_application_name(DoneIt.__pretty_app_name__)
345 class DummyOptions(object):
351 if __name__ == "__main__":
352 if len(sys.argv) > 1:
358 if optparse is not None:
359 parser = optparse.OptionParser()
360 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
361 (commandOptions, commandArgs) = parser.parse_args()
363 commandOptions = DummyOptions()
366 if commandOptions.test: