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
19 from __future__ import with_statement
42 __pretty_app_name__ = "DoneIt"
43 __app_name__ = "doneit"
45 __app_magic__ = 0xdeadbeef
48 '/usr/lib/doneit/doneit.glade',
49 os.path.join(os.path.dirname(__file__), "doneit.glade"),
50 os.path.join(os.path.dirname(__file__), "../lib/doneit.glade"),
53 _user_data = os.path.expanduser("~/.%s/" % __app_name__)
54 _user_settings = "%s/settings.ini" % _user_data
60 self._deviceIsOnline = True
61 self._connection = None
62 self._fallbackUIName = ""
63 self._defaultUIName = ""
65 for path in self._glade_files:
66 if os.path.isfile(path):
67 self._widgetTree = gtk.glade.XML(path)
70 self.display_error_message("Cannot find doneit.glade")
73 os.makedirs(self._user_data)
78 self._clipboard = gtk.clipboard_get()
79 self.__window = self._widgetTree.get_widget("mainWindow")
80 self.__errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
83 self._isFullScreen = False
84 if hildon is not None:
85 self._app = hildon.Program()
86 self.__window = hildon.Window()
87 self._widgetTree.get_widget("mainLayout").reparent(self.__window)
88 self._app.add_window(self.__window)
89 self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
90 self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
91 self._widgetTree.get_widget("projectsCombo").get_child().set_property('hildon-input-mode', (1 << 4))
93 gtkMenu = self._widgetTree.get_widget("mainMenubar")
95 for child in gtkMenu.get_children():
97 self.__window.set_menu(menu)
100 self.__window.connect("key-press-event", self._on_key_press)
101 self.__window.connect("window-state-event", self._on_window_state_change)
103 pass # warnings.warn("No Hildon", UserWarning, 2)
106 "on_doneit_quit": self._on_close,
107 "on_paste": self._on_paste,
108 "on_about": self._on_about_activate,
110 self._widgetTree.signal_autoconnect(callbackMapping)
114 self.__window.set_title("%s" % self.__pretty_app_name__)
115 self.__window.connect("destroy", self._on_close)
116 self.__window.show_all()
118 backgroundSetup = threading.Thread(target=self._idle_setup)
119 backgroundSetup.setDaemon(True)
120 backgroundSetup.start()
122 def _idle_setup(self):
123 # Barebones UI handlers
125 with gtk_toolbox.gtk_lock():
126 nullView = null_view.GtkNull(self._widgetTree)
127 self._todoUIs[nullView.name()] = nullView
128 self._todoUI = nullView
129 self._todoUI.enable()
130 self._fallbackUIName = nullView.name()
132 # Setup maemo specifics
139 self._osso = osso.Context(DoneIt.__app_name__, DoneIt.__version__, False)
140 device = osso.DeviceState(self._osso)
141 device.set_device_state_callback(self._on_device_state_change, 0)
143 pass # warnings.warn("No OSSO", UserWarning, 2)
149 self._connection = None
150 if conic is not None:
151 self._connection = conic.Connection()
152 self._connection.connect("connection-event", self._on_connection_change, self.__app_magic__)
153 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
155 pass # warnings.warn("No Internet Connectivity API ", UserWarning)
157 # Setup costly backends
159 with gtk_toolbox.gtk_lock():
160 rtmView = rtm_view.GtkRtMilk(self._widgetTree)
161 self._todoUIs[rtmView.name()] = rtmView
162 self._defaultUIName = rtmView.name()
164 config = ConfigParser.SafeConfigParser()
165 config.read(self._user_settings)
166 with gtk_toolbox.gtk_lock():
167 self.load_settings(config)
169 def display_error_message(self, msg):
173 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
175 def close(dialog, response, editor):
176 editor.about_dialog = None
178 error_dialog.connect("response", close, self)
181 def load_settings(self, config):
185 for todoUI in self._todoUIs.itervalues():
187 todoUI.load_settings(config)
188 except ConfigParser.NoSectionError, e:
190 "Settings file %s is missing section %s" % (
198 activeUIName = config.get(self.__pretty_app_name__, "active")
199 except ConfigParser.NoSectionError, e:
202 "Settings file %s is missing section %s" % (
210 self._switch_ui(activeUIName)
212 self._switch_ui(self._defaultUIName)
214 def save_settings(self, config):
216 @note Thread Agnostic
218 config.add_section(self.__pretty_app_name__)
219 config.set(self.__pretty_app_name__, "active", self._todoUI.name())
221 for todoUI in self._todoUIs.itervalues():
222 todoUI.save_settings(config)
224 def _switch_ui(self, uiName):
228 newActiveUI = self._todoUIs[uiName]
232 return # User cancelled the operation
234 self._todoUI.disable()
235 self._todoUI = newActiveUI
236 self._todoUI.enable()
238 if uiName != self._fallbackUIName:
239 self._defaultUIName = uiName
241 def _save_settings(self):
243 @note Thread Agnostic
245 config = ConfigParser.SafeConfigParser()
246 self.save_settings(config)
247 with open(self._user_settings, "wb") as configFile:
248 config.write(configFile)
250 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
252 For system_inactivity, we have no background tasks to pause
254 @note Hildon specific
259 if save_unsaved_data or shutdown:
260 self._save_settings()
262 def _on_connection_change(self, connection, event, magicIdentifier):
264 @note Hildon specific
268 status = event.get_status()
269 error = event.get_error()
270 iap_id = event.get_iap_id()
271 bearer = event.get_bearer_type()
273 if status == conic.STATUS_CONNECTED:
274 self._deviceIsOnline = True
275 self._switch_ui(self._defaultUIName)
276 elif status == conic.STATUS_DISCONNECTED:
277 self._deviceIsOnline = False
278 self._switch_ui(self._fallbackUIName)
280 def _on_window_state_change(self, widget, event, *args):
282 @note Hildon specific
284 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
285 self._isFullScreen = True
287 self._isFullScreen = False
289 def _on_close(self, *args, **kwds):
291 if self._osso is not None:
294 self._save_settings()
298 def _on_paste(self, *args):
301 def _on_key_press(self, widget, event, *args):
303 @note Hildon specific
305 if event.keyval == gtk.keysyms.F6:
306 if self._isFullScreen:
307 self.__window.unfullscreen()
309 self.__window.fullscreen()
311 def _on_logout(self, *args):
312 self._todoUI.logout()
313 self._switch_ui(self._fallbackUIName)
315 def _on_about_activate(self, *args):
316 dlg = gtk.AboutDialog()
317 dlg.set_name(self.__pretty_app_name__)
318 dlg.set_version(self.__version__)
319 dlg.set_copyright("Copyright 2008 - LGPL")
322 dlg.set_authors([""])
330 failureCount, testCount = doctest.testmod()
332 print "Tests Successful"
339 gtk.gdk.threads_init()
341 if hildon is not None:
342 gtk.set_application_name(DoneIt.__pretty_app_name__)
347 class DummyOptions(object):
353 if __name__ == "__main__":
354 if len(sys.argv) > 1:
360 if optparse is not None:
361 parser = optparse.OptionParser()
362 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
363 (commandOptions, commandArgs) = parser.parse_args()
365 commandOptions = DummyOptions()
368 if commandOptions.test: