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
11 Using an adjustable version of the following RTM quiery 'dueBefore:Today OR dueWithin:"1 week of today" OR (priority:3 AND dueWithin"2 weeks of today") OR (priority:2 AND dueWithin"3 weeks of today") OR (priority:1 AND dueWithin"4 weeks of today")')
12 Quick search (OR within a property type, and between property types)
13 Drop down for multi selecting priority
14 Drop down for multi selecting tags
15 Drop down for multi selecting locations
16 Calendar selector for choosing due date range
17 @todo Add logging support to make debugging random user issues a lot easier
20 from __future__ import with_statement
43 __pretty_app_name__ = "DoneIt"
44 __app_name__ = "doneit"
46 __app_magic__ = 0xdeadbeef
49 '/usr/lib/doneit/doneit.glade',
50 os.path.join(os.path.dirname(__file__), "doneit.glade"),
51 os.path.join(os.path.dirname(__file__), "../lib/doneit.glade"),
54 _user_data = os.path.expanduser("~/.%s/" % __app_name__)
55 _user_settings = "%s/settings.ini" % _user_data
61 self._deviceIsOnline = True
62 self._connection = None
63 self._fallbackUIName = ""
64 self._defaultUIName = ""
66 for path in self._glade_files:
67 if os.path.isfile(path):
68 self._widgetTree = gtk.glade.XML(path)
71 self.display_error_message("Cannot find doneit.glade")
74 os.makedirs(self._user_data)
79 self._clipboard = gtk.clipboard_get()
80 self.__window = self._widgetTree.get_widget("mainWindow")
81 self.__errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
84 self._isFullScreen = False
85 if hildon is not None:
86 self._app = hildon.Program()
87 self.__window = hildon.Window()
88 self._widgetTree.get_widget("mainLayout").reparent(self.__window)
89 self._app.add_window(self.__window)
90 self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
91 self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
92 self._widgetTree.get_widget("projectsCombo").get_child().set_property('hildon-input-mode', (1 << 4))
94 gtkMenu = self._widgetTree.get_widget("mainMenubar")
96 for child in gtkMenu.get_children():
98 self.__window.set_menu(menu)
101 self.__window.connect("key-press-event", self._on_key_press)
102 self.__window.connect("window-state-event", self._on_window_state_change)
104 pass # warnings.warn("No Hildon", UserWarning, 2)
107 "on_doneit_quit": self._on_close,
108 "on_paste": self._on_paste,
109 "on_about": self._on_about_activate,
111 self._widgetTree.signal_autoconnect(callbackMapping)
115 self.__window.set_title("%s" % self.__pretty_app_name__)
116 self.__window.connect("destroy", self._on_close)
117 self.__window.show_all()
119 backgroundSetup = threading.Thread(target=self._idle_setup)
120 backgroundSetup.setDaemon(True)
121 backgroundSetup.start()
123 def _idle_setup(self):
124 # Barebones UI handlers
126 with gtk_toolbox.gtk_lock():
127 nullView = null_view.GtkNull(self._widgetTree)
128 self._todoUIs[nullView.name()] = nullView
129 self._todoUI = nullView
130 self._todoUI.enable()
131 self._fallbackUIName = nullView.name()
133 # Setup maemo specifics
140 self._osso = osso.Context(DoneIt.__app_name__, DoneIt.__version__, False)
141 device = osso.DeviceState(self._osso)
142 device.set_device_state_callback(self._on_device_state_change, 0)
144 pass # warnings.warn("No OSSO", UserWarning, 2)
150 self._connection = None
151 if conic is not None:
152 self._connection = conic.Connection()
153 self._connection.connect("connection-event", self._on_connection_change, self.__app_magic__)
154 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
156 pass # warnings.warn("No Internet Connectivity API ", UserWarning)
158 # Setup costly backends
160 with gtk_toolbox.gtk_lock():
161 rtmView = rtm_view.GtkRtMilk(self._widgetTree)
162 self._todoUIs[rtmView.name()] = rtmView
163 self._defaultUIName = rtmView.name()
165 config = ConfigParser.SafeConfigParser()
166 config.read(self._user_settings)
167 with gtk_toolbox.gtk_lock():
168 self.load_settings(config)
170 def display_error_message(self, msg):
174 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
176 def close(dialog, response, editor):
177 editor.about_dialog = None
179 error_dialog.connect("response", close, self)
182 def load_settings(self, config):
186 for todoUI in self._todoUIs.itervalues():
188 todoUI.load_settings(config)
189 except ConfigParser.NoSectionError, e:
191 "Settings file %s is missing section %s" % (
199 activeUIName = config.get(self.__pretty_app_name__, "active")
200 except ConfigParser.NoSectionError, e:
203 "Settings file %s is missing section %s" % (
211 self._switch_ui(activeUIName)
213 self._switch_ui(self._defaultUIName)
215 def save_settings(self, config):
217 @note Thread Agnostic
219 config.add_section(self.__pretty_app_name__)
220 config.set(self.__pretty_app_name__, "active", self._todoUI.name())
222 for todoUI in self._todoUIs.itervalues():
223 todoUI.save_settings(config)
225 def _switch_ui(self, uiName):
229 newActiveUI = self._todoUIs[uiName]
233 return # User cancelled the operation
235 self._todoUI.disable()
236 self._todoUI = newActiveUI
237 self._todoUI.enable()
239 if uiName != self._fallbackUIName:
240 self._defaultUIName = uiName
242 def _save_settings(self):
244 @note Thread Agnostic
246 config = ConfigParser.SafeConfigParser()
247 self.save_settings(config)
248 with open(self._user_settings, "wb") as configFile:
249 config.write(configFile)
251 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
253 For system_inactivity, we have no background tasks to pause
255 @note Hildon specific
260 if save_unsaved_data or shutdown:
261 self._save_settings()
263 def _on_connection_change(self, connection, event, magicIdentifier):
265 @note Hildon specific
269 status = event.get_status()
270 error = event.get_error()
271 iap_id = event.get_iap_id()
272 bearer = event.get_bearer_type()
274 if status == conic.STATUS_CONNECTED:
275 self._deviceIsOnline = True
276 self._switch_ui(self._defaultUIName)
277 elif status == conic.STATUS_DISCONNECTED:
278 self._deviceIsOnline = False
279 self._switch_ui(self._fallbackUIName)
281 def _on_window_state_change(self, widget, event, *args):
283 @note Hildon specific
285 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
286 self._isFullScreen = True
288 self._isFullScreen = False
290 def _on_close(self, *args, **kwds):
292 if self._osso is not None:
295 self._save_settings()
299 def _on_paste(self, *args):
302 def _on_key_press(self, widget, event, *args):
304 @note Hildon specific
306 if event.keyval == gtk.keysyms.F6:
307 if self._isFullScreen:
308 self.__window.unfullscreen()
310 self.__window.fullscreen()
312 def _on_logout(self, *args):
313 self._todoUI.logout()
314 self._switch_ui(self._fallbackUIName)
316 def _on_about_activate(self, *args):
317 dlg = gtk.AboutDialog()
318 dlg.set_name(self.__pretty_app_name__)
319 dlg.set_version(self.__version__)
320 dlg.set_copyright("Copyright 2008 - LGPL")
323 dlg.set_authors([""])
331 failureCount, testCount = doctest.testmod()
333 print "Tests Successful"
340 gtk.gdk.threads_init()
342 if hildon is not None:
343 gtk.set_application_name(DoneIt.__pretty_app_name__)
348 class DummyOptions(object):
354 if __name__ == "__main__":
355 if len(sys.argv) > 1:
361 if optparse is not None:
362 parser = optparse.OptionParser()
363 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
364 (commandOptions, commandArgs) = parser.parse_args()
366 commandOptions = DummyOptions()
369 if commandOptions.test: