Merge lp:~keirangtp/cardapio/settings-helper into lp:cardapio

Proposed by Paweł Bara
Status: Merged
Merged at revision: 533
Proposed branch: lp:~keirangtp/cardapio/settings-helper
Merge into: lp:cardapio
Diff against target: 748 lines (+335/-258)
5 files modified
src/cardapio (+0/-9)
src/cardapio.py (+30/-195)
src/docky/DockySettingsHelper.py (+0/-44)
src/docky/cardapio_helper.py (+91/-10)
src/misc.py (+214/-0)
To merge this branch: bzr merge lp:~keirangtp/cardapio/settings-helper
Reviewer Review Type Date Requested Status
Thiago Teixeira Approve
Review via email: mp+41638@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Paweł Bara (keirangtp) wrote :

summary of settings changes:
- refactored Settings to a helper which looks like a dictionary
- redone the whole concept of raising errors from settings code and the handling of those errors in main Cardapio's code

summary of Docky related changes:
- better check of empty 'DockyItemCommand' setting
- added and empty DockManagerSink for Cardapio; it let's you run the helper from any environment and it will always close cleanly when Docky's closing
- moved installing and uninstalling the launcher from DockySettingsHelper and cardapio to cardapio_helper

Please note that even though I've done some changes to the helper's code, it's not removing the anchor icon's tooltip as we discussed. Docky's caches this setting when it starts so when we change it, it's too late. We could make it work after a restart of Docky but we would have to abandon the whole "remember the old setting" concept then.

Still, I think that removing this tooltip is a vital part of current helper's functionality so we should at least add a comment to the "advertisements" and informations on project's site saying something like:

Please note that you can remove the tooltip of Docky's anchor icon which will otherwise in many cases overlap Cardapio's window. To do that run: "gconftool -s /apps/docky-2/Docky/Items/DockyItem/HoverText -t string ''" from command line and restart Docky.

Revision history for this message
Thiago Teixeira (tvst) wrote :

I'll take a loot at this tonight.

Revision history for this message
Thiago Teixeira (tvst) wrote :

Ok, at first sight this looks good. I will test it in a little bit and get back to you.

One thing that seemed strange, though, is that you removed the line

self.safe_cardapio_proxy.settings = self.settings

Why did you do this?

Revision history for this message
Thiago Teixeira (tvst) wrote :

Oh, apparently that line is not longer used inside any plugin (it was used before when getting the max number of items for search results, but now we pass it as a parameter).

Ok, then :)

Revision history for this message
Thiago Teixeira (tvst) wrote :

So I don't know if you noticed but this is already on the PPA :)

Also, good job with the getter/setter approach to replacing the settings dict. This way there's almost no change to the code required :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/cardapio'
2--- src/cardapio 2010-11-21 22:19:26 +0000
3+++ src/cardapio 2010-11-23 18:22:35 +0000
4@@ -84,15 +84,6 @@
5 show_hide = bus.get_object(Cardapio.bus_name_str, Cardapio.bus_obj_str).get_dbus_method('show_hide')
6 show_hide()
7
8- elif sys.argv[1] == 'docky-install':
9-
10- from docky.DockySettingsHelper import install_cardapio_launcher
11- install_cardapio_launcher()
12-
13- elif sys.argv[1] == 'docky-uninstall':
14-
15- from docky.DockySettingsHelper import remove_cardapio_launcher
16- remove_cardapio_launcher()
17
18 elif sys.argv[1] == 'docky-open':
19
20
21=== modified file 'src/cardapio.py'
22--- src/cardapio.py 2010-11-21 22:35:56 +0000
23+++ src/cardapio.py 2010-11-23 18:22:35 +0000
24@@ -22,9 +22,9 @@
25 # TODO: add "most recent" and "most frequent" with a zeitgeist plugin
26 # plus other TODO's elsewhere in the code...
27
28-from misc import *
29-
30 try:
31+ from misc import *
32+
33 import gc
34 import os
35 import re
36@@ -39,6 +39,7 @@
37 import logging
38 import platform
39 import keybinder
40+ import traceback
41 import subprocess
42 import dbus, dbus.service
43
44@@ -183,8 +184,19 @@
45
46 self.home_folder_path = os.path.abspath(os.path.expanduser('~'))
47
48- self.read_config_file()
49-
50+ logging.info('Loading settings...')
51+ try:
52+ self.settings = SettingsHelper(self.config_folder_path)
53+ except Exception, ex:
54+ msg = 'unable to read settings: ' + str(ex)
55+
56+ logging.error(msg)
57+ fatal_error('Settings error', msg)
58+ traceback.print_exc()
59+
60+ sys.exit(1)
61+ logging.info(' ...done loading!')
62+
63 self.panel_applet = panel_applet
64 self.panel_button = panel_button
65 self.last_visibility_toggle = 0
66@@ -216,8 +228,6 @@
67 self.bookmark_monitor = None
68 self.volume_monitor = None
69
70- self.icon_extension_types = re.compile('.*\.(png|xpm|svg)$')
71-
72 self.sys_tree = gmenu.lookup_tree('gnomecc.menu')
73 self.have_control_center = (self.sys_tree.root is not None)
74
75@@ -254,10 +264,6 @@
76 if show == Cardapio.SHOW_NEAR_MOUSE: self.show_hide_near_mouse()
77 elif show == Cardapio.SHOW_CENTERED : self.show()
78
79- # this is useful so that the user can edit the config file on first-run
80- # without need to quit cardapio first:
81- self.save_config_file()
82-
83 if gnome_program_init is not None:
84 gnome_program_init('', self.version) # Prints a warning to the screen. Ignore it.
85 client = gnome_ui_master_client()
86@@ -279,14 +285,18 @@
87 Saves the current state and quits
88 """
89
90- self.save_config_file()
91+ try:
92+ self.settings.save()
93+ except Exception, ex:
94+ logging.error('error while saving settings: %s' % ex)
95 self.quit()
96
97
98- def quit(self):
99- """
100- Quits without saving the current state
101- """
102+ def quit(self, *dummy):
103+ """
104+ Quits without saving the current state.
105+ """
106+
107 logging.info('Exiting...')
108 gtk.main_quit()
109
110@@ -563,179 +573,6 @@
111 self.quit()
112
113
114- def get_config_file(self, mode):
115- """
116- Returns a file handler to Cardapio's config file.
117- """
118-
119- old_config_file_path = os.path.join(self.config_folder_path, 'config.ini')
120- config_file_path = os.path.join(self.config_folder_path, 'config.json')
121-
122- if not os.path.exists(config_file_path):
123-
124- if os.path.exists(old_config_file_path):
125- os.rename(old_config_file_path, config_file_path)
126- os.remove(os.path.join(self.config_folder_path, 'cardapio.log'))
127- else:
128- open(config_file_path, 'w+')
129-
130- elif not os.path.isfile(config_file_path):
131- logging.error('Error! Cannot create file "%s" because a folder with that name already exists!' % config_file_path)
132- self.quit()
133-
134- try:
135- config_file = open(config_file_path, mode)
136-
137- except Exception, exception:
138- logging.error('Could not read config file "%s":' % config_file_path)
139- logging.error(exception)
140- config_file = None
141-
142- return config_file
143-
144-
145- def read_config_file(self):
146- """
147- Reads Cardapio's config file and builds the self.settings dict
148- """
149-
150- config_file = self.get_config_file('r')
151-
152- self.settings = {}
153- s = {}
154-
155- try:
156- s = json.load(config_file)
157-
158- except Exception, exception:
159- logging.error('Could not read config file:')
160- logging.error(exception)
161-
162- finally:
163- config_file.close()
164-
165- default_side_pane_items = []
166- path = which('software-center')
167- if path is not None:
168- default_side_pane_items.append(
169- {
170- 'name' : _('Ubuntu Software Center'),
171- 'icon name' : 'softwarecenter',
172- 'tooltip' : _('Lets you choose from thousands of free applications available for Ubuntu'),
173- 'type' : 'raw',
174- 'command' : 'software-center',
175- })
176-
177- default_side_pane_items.append(
178- {
179- 'name' : _('Help and Support'),
180- 'icon name' : 'help-contents',
181- 'tooltip' : _('Get help with %(distro_name)s') % {'distro_name':Cardapio.distro_name},
182- 'type' : 'raw',
183- 'command' : 'gnome-help',
184- })
185-
186- self.read_config_option(s, 'window size' , None ) # format: [px, px]
187- self.read_config_option(s, 'mini mode' , False ) # bool
188- self.read_config_option(s, 'splitter position' , 0 ) # int, position in pixels
189- self.read_config_option(s, 'show session buttons' , False ) # bool
190- self.read_config_option(s, 'keep search results' , False ) # bool
191- self.read_config_option(s, 'open on hover' , False ) # bool
192- self.read_config_option(s, 'open categories on hover' , False ) # bool
193- self.read_config_option(s, 'min search string length' , 3 ) # int, number of characters
194- self.read_config_option(s, 'menu rebuild delay' , 3 , force_update_from_version = [0,9,96]) # seconds
195- self.read_config_option(s, 'search results limit' , 5 ) # int, number of results
196- self.read_config_option(s, 'long search results limit' , 15 ) # int, number of results
197- self.read_config_option(s, 'local search update delay' , 100 , force_update_from_version = [0,9,96]) # msec
198- self.read_config_option(s, 'remote search update delay' , 250 , force_update_from_version = [0,9,96]) # msec
199- self.read_config_option(s, 'local search timeout' , 3000 ) # msec
200- self.read_config_option(s, 'remote search timeout' , 5000 ) # msec
201- self.read_config_option(s, 'autohide delay' , 250 ) # msec
202- self.read_config_option(s, 'keybinding' , '<Super>space' ) # the user should use gtk.accelerator_parse('<Super>space') to see if the string is correct!
203- self.read_config_option(s, 'applet label' , _('Menu') ) # string
204- self.read_config_option(s, 'applet icon' , 'start-here' , override_empty_str = True) # string (either a path to the icon, or an icon name)
205- self.read_config_option(s, 'pinned items' , [] )
206- self.read_config_option(s, 'side pane items' , default_side_pane_items )
207- self.read_config_option(s, 'active plugins' , ['pinned', 'places', 'applications', 'tracker', 'google', 'command_launcher'])
208- self.read_config_option(s, 'plugin settings' , {} )
209-
210- # these are a bit of a hack:
211- self.read_config_option(s, 'handler for ftp paths' , r"nautilus '%s'" ) # a command line using %s
212- self.read_config_option(s, 'handler for sftp paths' , r"nautilus '%s'" ) # a command line using %s
213- self.read_config_option(s, 'handler for smb paths' , r"nautilus '%s'" ) # a command line using %s
214- # (see https://bugs.launchpad.net/bugs/593141)
215-
216- self.settings['cardapio version'] = self.version
217-
218-
219- # clean up the config file whenever options are changed between versions
220-
221- # 'side pane' used to be called 'system pane'
222- if 'system pane' in self.settings:
223- self.settings['side pane'] = self.settings['system pane']
224- self.settings.pop('system pane')
225-
226- # 'None' used to be the 'applications' plugin
227- if None in self.settings['active plugins']:
228- i = self.settings['active plugins'].index(None)
229- self.settings['active plugins'][i] = 'applications'
230-
231- # make sure required plugins are in the plugin list
232- for required_plugin in self.required_plugins:
233-
234- if required_plugin not in self.settings['active plugins']:
235- self.settings['active plugins'] = [required_plugin] + self.settings['active plugins']
236-
237- # make sure plugins only appear once in the plugin list
238- for plugin_name in self.settings['active plugins']:
239-
240- while len([basename for basename in self.settings['active plugins'] if basename == plugin_name]) > 1:
241- self.settings['active plugins'].remove(plugin_name)
242-
243-
244- def read_config_option(self, user_settings, key, val, override_empty_str = False, force_update_from_version = None):
245- """
246- Reads the config option 'key' from a the 'user_settings' dict, using
247- 'val' as a fallback.
248- """
249-
250- if key in user_settings:
251- if override_empty_str and len(user_settings[key]) == 0:
252- self.settings[key] = val
253- else:
254- self.settings[key] = user_settings[key]
255- else:
256- self.settings[key] = val
257-
258- if force_update_from_version is not None:
259-
260- if 'cardapio version' in user_settings:
261- settings_version = [int(i) for i in user_settings['cardapio version'].split('.')]
262-
263- else:
264- settings_version = 0
265-
266- if settings_version <= force_update_from_version:
267-
268- self.settings[key] = val
269-
270-
271- def save_config_file(self):
272- """
273- Saves the self.settings dict into the config file
274- """
275-
276- config_file = self.get_config_file('w')
277-
278- if config_file is None:
279- logging.error('Could not load config file for saving settings')
280- return
281-
282- logging.info('Saving config file...')
283- json.dump(self.settings, config_file, sort_keys = True, indent = 4)
284- logging.info(' ...done!')
285-
286-
287 def setup_base_ui(self):
288 """
289 Reads the GTK Builder interface file and sets up some UI details
290@@ -871,7 +708,6 @@
291 """
292
293 self.safe_cardapio_proxy = Cardapio.SafeCardapioProxy()
294- self.safe_cardapio_proxy.settings = self.settings
295 self.safe_cardapio_proxy.write_to_log = self.plugin_write_to_log
296 self.safe_cardapio_proxy.handle_search_result = self.plugin_handle_search_result
297 self.safe_cardapio_proxy.handle_search_error = self.plugin_handle_search_error
298@@ -1254,7 +1090,11 @@
299 """
300
301 self.options_dialog.hide()
302- self.save_config_file()
303+ try:
304+ self.settings.save()
305+ except Exception, ex:
306+ logging.error('error while saving settings: %s' % ex)
307+
308 return True
309
310
311@@ -4271,9 +4111,6 @@
312 The constructor is given a single parameter, which is an object used to
313 communicate with Cardapio. This object has the following members:
314
315- - settings - this is a dict containing the same things that you will
316- find in the config.json
317-
318 - write_to_log - this is a function that lets you write to Cardapio's
319 log file, like this: write_to_log(self, 'hi there')
320
321@@ -4290,8 +4127,6 @@
322 internal databases, though, so this is not always applicable. This
323 is used, for example, with the software_center plugin. (see
324 on_reload_permission_granted below for more info)
325-
326- Note: DO NOT WRITE ANYTHING IN THE settings DICT!!
327 """
328 pass
329
330
331=== modified file 'src/docky/DockySettingsHelper.py'
332--- src/docky/DockySettingsHelper.py 2010-11-21 22:11:55 +0000
333+++ src/docky/DockySettingsHelper.py 2010-11-23 18:22:35 +0000
334@@ -1,6 +1,5 @@
335 import gconf
336 import gtk
337-from misc import which
338
339 class DockySettingsHelper:
340 """
341@@ -157,49 +156,6 @@
342 force_anchor_right = True
343
344 return x, y, force_anchor_right, force_anchor_bottom
345-
346-
347-def install_cardapio_launcher():
348- """
349- Sets Docky up so that Cardapio is launched whenever the dock icon is clicked.
350- """
351- gconf_client = gconf.client_get_default()
352- new_command = which('cardapio') + ' docky-open'
353-
354- current_command = gconf_client.get_string(DockySettingsHelper.docky_gconf_root + '/Items/DockyItem/DockyItemCommand')
355- if current_command == new_command: return
356-
357- if current_command is not None:
358- if 'cardapio' not in current_command:
359- gconf_client.set_string(DockySettingsHelper.docky_gconf_root + '/Items/DockyItem/OldDockyItemCommand', current_command)
360- else:
361- gconf_client.set_string(DockySettingsHelper.docky_gconf_root + '/Items/DockyItem/OldDockyItemCommand', '')
362-
363- try:
364- gconf_client.set_string(DockySettingsHelper.docky_gconf_root + '/Items/DockyItem/DockyItemCommand', new_command)
365- except:
366- pass
367-
368-
369-def remove_cardapio_launcher():
370- """
371- Resets Docky to its initial state, before Cardapio ever loaded.
372- """
373- gconf_client = gconf.client_get_default()
374-
375- current_command = gconf_client.get_string(DockySettingsHelper.docky_gconf_root + '/Items/DockyItem/DockyItemCommand')
376- if current_command != which('cardapio') + ' docky-open': return
377-
378- old_command = gconf_client.get_string(DockySettingsHelper.docky_gconf_root + '/Items/DockyItem/OldDockyItemCommand')
379- if old_command is not None and old_command != '':
380- gconf_client.set_string(DockySettingsHelper.docky_gconf_root + '/Items/DockyItem/DockyItemCommand', old_command)
381- else:
382- gconf_client.set_string(DockySettingsHelper.docky_gconf_root + '/Items/DockyItem/DockyItemCommand', '')
383-
384- try:
385- gconf_client.unset(DockySettingsHelper.docky_gconf_root + '/Items/DockyItem/OldDockyItemCommand')
386- except:
387- pass
388
389
390 class MainDockError(Exception):
391
392=== modified file 'src/docky/cardapio_helper.py'
393--- src/docky/cardapio_helper.py 2010-11-22 10:25:42 +0000
394+++ src/docky/cardapio_helper.py 2010-11-23 18:22:35 +0000
395@@ -17,27 +17,108 @@
396 # along with this program. If not, see <http://www.gnu.org/licenses/>.
397 #
398
399-from sys import exit
400-import gobject
401-import os
402-import subprocess
403-
404 try:
405+ from dockmanager.dockmanager import DockManagerSink
406+
407+ from sys import exit
408 from signal import signal, SIGTERM
409+
410+ import atexit
411+ import gobject
412+ import subprocess
413+ import os
414+ import gconf
415 except ImportError, e:
416 exit()
417
418+
419+# note - this is duplicated in misc.py
420+def which(filename):
421+ """
422+ Searches the folders in the OS's PATH variable, looking for a file called
423+ "filename". If found, returns the full path. Otherwise, returns None.
424+ """
425+
426+ for path in os.environ["PATH"].split(os.pathsep):
427+ if os.access(os.path.join(path, filename), os.X_OK):
428+ return "%s/%s" % (path, filename)
429+ return None
430+
431+
432+docky_item_gconf_root = '/apps/docky-2/Docky/Items/DockyItem'
433+
434+
435+def install_cardapio_launcher():
436+ """
437+ Sets Docky up so that Cardapio is launched whenever the dock icon is clicked.
438+ """
439+
440+ gconf_client = gconf.client_get_default()
441+
442+ new_command = which('cardapio') + ' docky-open'
443+
444+ current_command = gconf_client.get_string(docky_item_gconf_root + '/DockyItemCommand')
445+ if current_command == new_command:
446+ return
447+
448+ if current_command is not None and current_command != '':
449+ if 'cardapio' not in current_command:
450+ gconf_client.set_string(docky_item_gconf_root + '/OldDockyItemCommand', current_command)
451+
452+ try:
453+ gconf_client.set_string(docky_item_gconf_root + '/DockyItemCommand', new_command)
454+ except:
455+ pass
456+
457+
458+def remove_cardapio_launcher():
459+ """
460+ Resets Docky to its initial state, before Cardapio ever loaded.
461+ """
462+
463+ gconf_client = gconf.client_get_default()
464+
465+ current_command = gconf_client.get_string(docky_item_gconf_root + '/DockyItemCommand')
466+ if current_command != which('cardapio') + ' docky-open':
467+ return
468+
469+ old_command = gconf_client.get_string(docky_item_gconf_root + '/OldDockyItemCommand')
470+ if old_command is not None and old_command != '':
471+ gconf_client.set_string(docky_item_gconf_root + '/DockyItemCommand', old_command)
472+ else:
473+ gconf_client.set_string(docky_item_gconf_root + '/DockyItemCommand', '')
474+
475+ try:
476+ gconf_client.unset(docky_item_gconf_root + '/OldDockyItemCommand')
477+ except:
478+ pass
479+
480+
481+class CardapioSink(DockManagerSink):
482+ """
483+ This is not attaching any helpers - just waiting for the signal
484+ to close.
485+ """
486+
487+ def item_path_found(self, pathtoitem, item):
488+ pass
489+
490+
491+cardapio_sink = CardapioSink()
492+
493+
494 def cleanup():
495- subprocess.Popen('cardapio docky-uninstall', shell = True)
496+ remove_cardapio_launcher()
497+ cardapio_sink.dispose()
498+
499
500 if __name__ == "__main__":
501- subprocess.Popen('cardapio docky-install', shell = True)
502- subprocess.Popen('cardapio hidden', shell = True)
503+ install_cardapio_launcher()
504+ subprocess.Popen('cardapio hidden', shell=True)
505
506 mainloop = gobject.MainLoop(is_running=True)
507
508 atexit.register (cleanup)
509 signal(SIGTERM, lambda signum, stack_frame: exit(0))
510
511- mainloop.run()
512-
513+ mainloop.run()
514\ No newline at end of file
515
516=== modified file 'src/misc.py'
517--- src/misc.py 2010-11-17 13:20:29 +0000
518+++ src/misc.py 2010-11-23 18:22:35 +0000
519@@ -16,10 +16,15 @@
520 #
521
522 try:
523+
524+ import cardapio
525+
526 import re
527 import os
528 import gtk
529+ import json
530 import gio
531+ import sys
532 import logging
533 import commands
534 from xdg import BaseDirectory
535@@ -234,3 +239,212 @@
536 self._listener()
537
538
539+class SettingsHelper:
540+
541+ def __init__(self, config_folder_path):
542+ """
543+ Reads Cardapio's config file and builds the settings dictionary.
544+ """
545+
546+ self.config_folder_path = config_folder_path
547+
548+ self.settings = {}
549+ s = {}
550+
551+ with self.get_config_file('r') as config_file:
552+ # if the file is empty, we assume it's the first run and
553+ # we'll fill it while saving the settings for the first time
554+ if(os.path.getsize(config_file.name) > 0):
555+ s = json.load(config_file)
556+
557+ default_side_pane_items = []
558+ path = which('software-center')
559+ if path is not None:
560+ default_side_pane_items.append(
561+ {
562+ 'name' : _('Ubuntu Software Center'),
563+ 'icon name' : 'softwarecenter',
564+ 'tooltip' : _('Lets you choose from thousands of free applications available for Ubuntu'),
565+ 'type' : 'raw',
566+ 'command' : 'software-center',
567+ })
568+
569+ default_side_pane_items.append(
570+ {
571+ 'name' : _('Help and Support'),
572+ 'icon name' : 'help-contents',
573+ 'tooltip' : _('Get help with %(distro_name)s') % { 'distro_name': cardapio.Cardapio.distro_name },
574+ 'type' : 'raw',
575+ 'command' : 'gnome-help',
576+ })
577+
578+ self.read_config_option(s, 'window size' , None ) # format: [px, px]
579+ self.read_config_option(s, 'mini mode' , False ) # bool
580+ self.read_config_option(s, 'splitter position' , 0 ) # int, position in pixels
581+ self.read_config_option(s, 'show session buttons' , False ) # bool
582+ self.read_config_option(s, 'keep search results' , False ) # bool
583+ self.read_config_option(s, 'open on hover' , False ) # bool
584+ self.read_config_option(s, 'open categories on hover' , False ) # bool
585+ self.read_config_option(s, 'min search string length' , 3 ) # int, number of characters
586+ self.read_config_option(s, 'menu rebuild delay' , 3 , force_update_from_version = [0,9,96]) # seconds
587+ self.read_config_option(s, 'search results limit' , 5 ) # int, number of results
588+ self.read_config_option(s, 'long search results limit' , 15 ) # int, number of results
589+ self.read_config_option(s, 'local search update delay' , 100 , force_update_from_version = [0,9,96]) # msec
590+ self.read_config_option(s, 'remote search update delay' , 250 , force_update_from_version = [0,9,96]) # msec
591+ self.read_config_option(s, 'local search timeout' , 3000 ) # msec
592+ self.read_config_option(s, 'remote search timeout' , 5000 ) # msec
593+ self.read_config_option(s, 'autohide delay' , 250 ) # msec
594+ self.read_config_option(s, 'keybinding' , '<Super>space' ) # the user should use gtk.accelerator_parse('<Super>space') to see if the string is correct!
595+ self.read_config_option(s, 'applet label' , _('Menu') ) # string
596+ self.read_config_option(s, 'applet icon' , 'start-here' , override_empty_str = True) # string (either a path to the icon, or an icon name)
597+ self.read_config_option(s, 'pinned items' , [] )
598+ self.read_config_option(s, 'side pane items' , default_side_pane_items )
599+ self.read_config_option(s, 'active plugins' , ['pinned', 'places', 'applications', 'tracker', 'google', 'command_launcher'])
600+ self.read_config_option(s, 'plugin settings' , {} )
601+
602+ # these are a bit of a hack:
603+ self.read_config_option(s, 'handler for ftp paths' , r"nautilus '%s'" ) # a command line using %s
604+ self.read_config_option(s, 'handler for sftp paths' , r"nautilus '%s'" ) # a command line using %s
605+ self.read_config_option(s, 'handler for smb paths' , r"nautilus '%s'" ) # a command line using %s
606+ # see https://bugs.launchpad.net/bugs/593141
607+
608+ self.settings['cardapio version'] = cardapio.Cardapio.version
609+
610+ # clean up the config file whenever options are changed between versions
611+
612+ # 'side pane' used to be called 'system pane'
613+ if 'system pane' in self.settings:
614+ self.settings['side pane'] = self.settings['system pane']
615+ self.settings.pop('system pane')
616+
617+ # 'None' used to be the 'applications' plugin
618+ if None in self.settings['active plugins']:
619+ i = self.settings['active plugins'].index(None)
620+ self.settings['active plugins'][i] = 'applications'
621+
622+ # make sure required plugins are in the plugin list
623+ for required_plugin in cardapio.Cardapio.required_plugins:
624+ if required_plugin not in self.settings['active plugins']:
625+ self.settings['active plugins'] = [required_plugin] + self.settings['active plugins']
626+
627+ # make sure plugins only appear once in the plugin list
628+ for plugin_name in self.settings['active plugins']:
629+ while len([basename for basename in self.settings['active plugins'] if basename == plugin_name]) > 1:
630+ self.settings['active plugins'].remove(plugin_name)
631+
632+ # this saves the loaded config file (useful on the first run)
633+ self.save()
634+
635+
636+ def assert_config_file_exists(self):
637+ """
638+ If this doesn't throw any exceptions, after the invocation the caller
639+ might be sure that the config file "config.json" exists at and can be
640+ used further.
641+
642+ The method returns a file path to the config file.
643+
644+ It might raise FatalSettingsError if "config.json" exists but is
645+ a directory.
646+ """
647+
648+ old_config_file_path = os.path.join(self.config_folder_path, 'config.ini')
649+ config_file_path = os.path.join(self.config_folder_path, 'config.json')
650+
651+ if not os.path.exists(config_file_path):
652+
653+ # maybe it's not there because we're migrating from version
654+ # that's using the old file extension (".ini")?
655+ if os.path.exists(old_config_file_path):
656+ # change the extension
657+ os.rename(old_config_file_path, config_file_path)
658+ # also, let's remove the old log file while we're at it...
659+ os.remove(os.path.join(self.config_folder_path, 'cardapio.log'))
660+ else:
661+ # create and close an empty file
662+ with open(config_file_path, 'w+') as new_file:
663+ pass
664+
665+ elif not os.path.isfile(config_file_path):
666+ raise FatalSettingsError('cannot create file "%s" because a folder with that name already exists!' % config_file_path)
667+
668+ return config_file_path
669+
670+
671+ def get_config_file(self, mode):
672+ """
673+ Returns a file handler to Cardapio's config file. The caller is
674+ responsible for closing the file.
675+ """
676+
677+ return open(self.assert_config_file_exists(), mode)
678+
679+
680+ def save(self):
681+ """
682+ Saves this settings object to a config file.
683+ """
684+
685+ with self.get_config_file('w') as config_file:
686+ logging.info('Saving config file...')
687+ json.dump(self.settings, config_file, sort_keys = True, indent = 4)
688+ logging.info(' ...done!')
689+
690+
691+ def read_config_option(self, user_settings, key, val, override_empty_str = False, force_update_from_version = None):
692+ """
693+ Sets on itself the config option 'key' from the 'user_settings'
694+ dictionary using 'val' as a fallback value.
695+
696+ Will override found but empty settings when the override_empty_str flag
697+ is set to true.
698+
699+ Will update the setting 'key' to the 'val' value if user is migrating
700+ from version force_update_from_version.
701+ """
702+
703+ if key in user_settings:
704+
705+ if override_empty_str and len(user_settings[key]) == 0:
706+ self.settings[key] = val
707+ else:
708+ self.settings[key] = user_settings[key]
709+
710+ else:
711+
712+ self.settings[key] = val
713+
714+ if force_update_from_version is not None:
715+
716+ if 'cardapio version' in user_settings:
717+ settings_version = [int(i) for i in user_settings['cardapio version'].split('.')]
718+ else:
719+ settings_version = 0
720+
721+ if settings_version <= force_update_from_version:
722+ self.settings[key] = val
723+
724+
725+ def __getitem__(self, name):
726+ """
727+ Returns the value of the setting named 'name'.
728+ """
729+
730+ return self.settings[name];
731+
732+
733+ def __setitem__(self, name, value):
734+ """
735+ Sets the value of the setting named 'name' to 'value'.
736+ """
737+
738+ self.settings[name] = value;
739+
740+
741+
742+class FatalSettingsError(Exception):
743+ """
744+ Indicates unrecoverable SettingsHelper error.
745+ """
746+
747+ pass
748\ No newline at end of file

Subscribers

People subscribed via source and target branches