Renaming files similar to how I run DialCentral
[doneit] / src / doneit_glade.py
1 #!/usr/bin/python
2
3
4 from __future__ import with_statement
5
6
7 import sys
8 import gc
9 import os
10 import threading
11 import warnings
12 import ConfigParser
13
14 import gtk
15 import gtk.glade
16
17 try:
18         import hildon
19 except ImportError:
20         hildon = None
21
22 import gtk_toolbox
23
24
25 class DoneIt(object):
26
27         __pretty_app_name__ = "DoneIt"
28         __app_name__ = "doneit"
29         __version__ = "0.3.0"
30         __app_magic__ = 0xdeadbeef
31
32         _glade_files = [
33                 '/usr/lib/doneit/doneit.glade',
34                 os.path.join(os.path.dirname(__file__), "doneit.glade"),
35                 os.path.join(os.path.dirname(__file__), "../lib/doneit.glade"),
36         ]
37
38         _user_data = os.path.expanduser("~/.%s/" % __app_name__)
39         _user_settings = "%s/settings.ini" % _user_data
40
41         def __init__(self):
42                 self._todoUIs = {}
43                 self._todoUI = None
44                 self._osso = None
45                 self._deviceIsOnline = True
46                 self._connection = None
47                 self._fallbackUIName = ""
48                 self._defaultUIName = ""
49
50                 for path in self._glade_files:
51                         if os.path.isfile(path):
52                                 self._widgetTree = gtk.glade.XML(path)
53                                 break
54                 else:
55                         self.display_error_message("Cannot find doneit.glade")
56                         gtk.main_quit()
57                 try:
58                         os.makedirs(self._user_data)
59                 except OSError, e:
60                         if e.errno != 17:
61                                 raise
62
63                 self._clipboard = gtk.clipboard_get()
64                 self.__window = self._widgetTree.get_widget("mainWindow")
65                 self.__errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
66
67                 self._app = None
68                 self._isFullScreen = False
69                 if hildon is not None:
70                         self._app = hildon.Program()
71                         self.__window = hildon.Window()
72                         self._widgetTree.get_widget("mainLayout").reparent(self.__window)
73                         self._app.add_window(self.__window)
74                         self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7)
75                         self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29))
76                         self._widgetTree.get_widget("projectsCombo").get_child().set_property('hildon-input-mode', (1 << 4))
77
78                         gtkMenu = self._widgetTree.get_widget("mainMenubar")
79                         menu = gtk.Menu()
80                         for child in gtkMenu.get_children():
81                                 child.reparent(menu)
82                         self.__window.set_menu(menu)
83                         gtkMenu.destroy()
84
85                         self.__window.connect("key-press-event", self._on_key_press)
86                         self.__window.connect("window-state-event", self._on_window_state_change)
87                 else:
88                         pass # warnings.warn("No Hildon", UserWarning, 2)
89
90                 callbackMapping = {
91                         "on_doneit_quit": self._on_close,
92                         "on_paste": self._on_paste,
93                         "on_about": self._on_about_activate,
94                 }
95                 self._widgetTree.signal_autoconnect(callbackMapping)
96
97                 if self.__window:
98                         if hildon is None:
99                                 self.__window.set_title("%s" % self.__pretty_app_name__)
100                         self.__window.connect("destroy", self._on_close)
101                         self.__window.show_all()
102
103                 backgroundSetup = threading.Thread(target=self._idle_setup)
104                 backgroundSetup.setDaemon(True)
105                 backgroundSetup.start()
106
107         def _idle_setup(self):
108                 # Barebones UI handlers
109                 import null_view
110                 with gtk_toolbox.gtk_lock():
111                         nullView = null_view.GtkNull(self._widgetTree)
112                         self._todoUIs[nullView.name()] = nullView
113                         self._todoUI = nullView
114                         self._todoUI.enable()
115                         self._fallbackUIName = nullView.name()
116
117                 # Setup maemo specifics
118                 try:
119                         import osso
120                 except ImportError:
121                         osso = None
122                 self._osso = None
123                 if osso is not None:
124                         self._osso = osso.Context(DoneIt.__app_name__, DoneIt.__version__, False)
125                         device = osso.DeviceState(self._osso)
126                         device.set_device_state_callback(self._on_device_state_change, 0)
127                 else:
128                         pass # warnings.warn("No OSSO", UserWarning, 2)
129
130                 try:
131                         import conic
132                 except ImportError:
133                         conic = None
134                 self._connection = None
135                 if conic is not None:
136                         self._connection = conic.Connection()
137                         self._connection.connect("connection-event", self._on_connection_change, self.__app_magic__)
138                         self._connection.request_connection(conic.CONNECT_FLAG_NONE)
139                 else:
140                         pass # warnings.warn("No Internet Connectivity API ", UserWarning)
141
142                 # Setup costly backends
143                 import rtm_view
144                 with gtk_toolbox.gtk_lock():
145                         rtmView = rtm_view.GtkRtMilk(self._widgetTree)
146                 self._todoUIs[rtmView.name()] = rtmView
147                 self._defaultUIName = rtmView.name()
148
149                 config = ConfigParser.SafeConfigParser()
150                 config.read(self._user_settings)
151                 with gtk_toolbox.gtk_lock():
152                         self.load_settings(config)
153
154         def display_error_message(self, msg):
155                 """
156                 @note UI Thread
157                 """
158                 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
159
160                 def close(dialog, response, editor):
161                         editor.about_dialog = None
162                         dialog.destroy()
163                 error_dialog.connect("response", close, self)
164                 error_dialog.run()
165
166         def load_settings(self, config):
167                 """
168                 @note UI Thread
169                 """
170                 for todoUI in self._todoUIs.itervalues():
171                         try:
172                                 todoUI.load_settings(config)
173                         except ConfigParser.NoSectionError, e:
174                                 warnings.warn(
175                                         "Settings file %s is missing section %s" % (
176                                                 self._user_settings,
177                                                 e.section,
178                                         ),
179                                         stacklevel=2
180                                 )
181
182                 try:
183                         activeUIName = config.get(self.__pretty_app_name__, "active")
184                 except ConfigParser.NoSectionError, e:
185                         activeUIName = ""
186                         warnings.warn(
187                                 "Settings file %s is missing section %s" % (
188                                         self._user_settings,
189                                         e.section,
190                                 ),
191                                 stacklevel=2
192                         )
193
194                 try:
195                         self._switch_ui(activeUIName)
196                 except KeyError, e:
197                         self._switch_ui(self._defaultUIName)
198
199         def save_settings(self, config):
200                 """
201                 @note Thread Agnostic
202                 """
203                 config.add_section(self.__pretty_app_name__)
204                 config.set(self.__pretty_app_name__, "active", self._todoUI.name())
205
206                 for todoUI in self._todoUIs.itervalues():
207                         todoUI.save_settings(config)
208
209         def _switch_ui(self, uiName):
210                 """
211                 @note UI Thread
212                 """
213                 newActiveUI = self._todoUIs[uiName]
214                 try:
215                         newActiveUI.login()
216                 except RuntimeError:
217                         return # User cancelled the operation
218
219                 self._todoUI.disable()
220                 self._todoUI = newActiveUI
221                 self._todoUI.enable()
222
223                 if uiName != self._fallbackUIName:
224                         self._defaultUIName = uiName
225
226         def _save_settings(self):
227                 """
228                 @note Thread Agnostic
229                 """
230                 config = ConfigParser.SafeConfigParser()
231                 self.save_settings(config)
232                 with open(self._user_settings, "wb") as configFile:
233                         config.write(configFile)
234
235         def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
236                 """
237                 For system_inactivity, we have no background tasks to pause
238
239                 @note Hildon specific
240                 """
241                 if memory_low:
242                         gc.collect()
243
244                 if save_unsaved_data or shutdown:
245                         self._save_settings()
246
247         def _on_connection_change(self, connection, event, magicIdentifier):
248                 """
249                 @note Hildon specific
250                 """
251                 import conic
252
253                 status = event.get_status()
254                 error = event.get_error()
255                 iap_id = event.get_iap_id()
256                 bearer = event.get_bearer_type()
257
258                 if status == conic.STATUS_CONNECTED:
259                         self._deviceIsOnline = True
260                         self._switch_ui(self._defaultUIName)
261                 elif status == conic.STATUS_DISCONNECTED:
262                         self._deviceIsOnline = False
263                         self._switch_ui(self._fallbackUIName)
264
265         def _on_window_state_change(self, widget, event, *args):
266                 """
267                 @note Hildon specific
268                 """
269                 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
270                         self._isFullScreen = True
271                 else:
272                         self._isFullScreen = False
273
274         def _on_close(self, *args, **kwds):
275                 try:
276                         if self._osso is not None:
277                                 self._osso.close()
278
279                         self._save_settings()
280                 finally:
281                         gtk.main_quit()
282
283         def _on_paste(self, *args):
284                 pass
285
286         def _on_key_press(self, widget, event, *args):
287                 """
288                 @note Hildon specific
289                 """
290                 if event.keyval == gtk.keysyms.F6:
291                         if self._isFullScreen:
292                                 self.__window.unfullscreen()
293                         else:
294                                 self.__window.fullscreen()
295
296         def _on_logout(self, *args):
297                 self._todoUI.logout()
298                 self._switch_ui(self._fallbackUIName)
299
300         def _on_about_activate(self, *args):
301                 dlg = gtk.AboutDialog()
302                 dlg.set_name(self.__pretty_app_name__)
303                 dlg.set_version(self.__version__)
304                 dlg.set_copyright("Copyright 2008 - LGPL")
305                 dlg.set_comments("")
306                 dlg.set_website("")
307                 dlg.set_authors([""])
308                 dlg.run()
309                 dlg.destroy()
310
311
312 def run_doctest():
313         import doctest
314
315         failureCount, testCount = doctest.testmod()
316         if not failureCount:
317                 print "Tests Successful"
318                 sys.exit(0)
319         else:
320                 sys.exit(1)
321
322
323 def run_doneit():
324         gtk.gdk.threads_init()
325
326         if hildon is not None:
327                 gtk.set_application_name(DoneIt.__pretty_app_name__)
328         handle = DoneIt()
329         gtk.main()
330
331
332 class DummyOptions(object):
333
334         def __init__(self):
335                 self.test = False
336
337
338 if __name__ == "__main__":
339         if len(sys.argv) > 1:
340                 try:
341                         import optparse
342                 except ImportError:
343                         optparse = None
344
345                 if optparse is not None:
346                         parser = optparse.OptionParser()
347                         parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
348                         (commandOptions, commandArgs) = parser.parse_args()
349         else:
350                 commandOptions = DummyOptions()
351                 commandArgs = []
352
353         if commandOptions.test:
354                 run_doctest()
355         else:
356                 run_doneit()