cmds: provide $DIRNAME in the environment for guitool commands
[git-cola.git] / cola / settings.py
blob0356b3074c753c46000a2996f25dff1bdc7b1649
1 """Save settings, bookmarks, etc.
2 """
3 from __future__ import division, absolute_import, unicode_literals
4 import json
5 import os
6 import sys
8 from . import core
9 from . import git
10 from . import resources
13 def mkdict(obj):
14 if type(obj) is dict:
15 return obj
16 else:
17 return {}
20 def mklist(obj):
21 if type(obj) is list:
22 return obj
23 else:
24 return []
27 def read_json(path):
28 try:
29 with core.xopen(path, 'rt') as fp:
30 return mkdict(json.load(fp))
31 except: # bad path or json
32 return {}
35 def write_json(values, path):
36 try:
37 parent = os.path.dirname(path)
38 if not core.isdir(parent):
39 core.makedirs(parent)
40 with core.xopen(path, 'wt') as fp:
41 json.dump(values, fp, indent=4)
42 except:
43 sys.stderr.write('git-cola: error writing "%s"\n' % path)
46 class Settings(object):
47 _file = resources.config_home('settings')
48 bookmarks = property(lambda self: mklist(self.values['bookmarks']))
49 gui_state = property(lambda self: mkdict(self.values['gui_state']))
50 recent = property(lambda self: mklist(self.values['recent']))
52 def __init__(self, verify=git.is_git_worktree):
53 """Load existing settings if they exist"""
54 self.values = {
55 'bookmarks': [],
56 'gui_state': {},
57 'recent': [],
59 self.verify = verify
61 def remove_missing(self):
62 missing_bookmarks = []
63 missing_recent = []
65 for bookmark in self.bookmarks:
66 if not self.verify(bookmark['path']):
67 missing_bookmarks.append(bookmark)
69 for bookmark in missing_bookmarks:
70 try:
71 self.bookmarks.remove(bookmark)
72 except:
73 pass
75 for recent in self.recent:
76 if not self.verify(recent['path']):
77 missing_recent.append(recent)
79 for recent in missing_recent:
80 try:
81 self.recent.remove(recent)
82 except:
83 pass
85 def add_bookmark(self, path, name):
86 """Adds a bookmark to the saved settings"""
87 bookmark = {'path': path, 'name': name}
88 if bookmark not in self.bookmarks:
89 self.bookmarks.append(bookmark)
91 def remove_bookmark(self, path, name):
92 """Remove a bookmark"""
93 bookmark = {'path': path, 'name': name}
94 try:
95 self.bookmarks.remove(bookmark)
96 except ValueError:
97 pass
99 def rename_entry(self, entries, path, name, new_name):
100 entry = {'name': name, 'path': path}
101 try:
102 index = entries.index(entry)
103 except ValueError:
104 return False
106 if all([item['name'] != new_name for item in entries]):
107 entries[index]['name'] = new_name
108 return True
110 return False
112 def rename_bookmark(self, path, name, new_name):
113 return self.rename_entry(self.bookmarks, path, name, new_name)
115 def add_recent(self, path):
116 try:
117 index = [recent['path'] for recent in self.recent].index(path)
118 entry = self.recent.pop(index)
119 except (IndexError, ValueError):
120 entry = {
121 'name': os.path.basename(path),
122 'path': path,
124 self.recent.insert(0, entry)
126 if len(self.recent) >= 8:
127 self.recent.pop()
129 def remove_recent(self, path):
130 """Removes an item from the recent items list"""
131 try:
132 index = [recent.get('path', '') for recent in self.recent].index(path)
133 except ValueError:
134 return
135 try:
136 self.recent.pop(index)
137 except IndexError:
138 return
140 def rename_recent(self, path, name, new_name):
141 return self.rename_entry(self.recent, path, name, new_name)
143 def path(self):
144 return self._file
146 def save(self):
147 write_json(self.values, self.path())
149 def load(self):
150 self.values.update(self.asdict())
151 self.upgrade_settings()
152 self.remove_missing()
154 def upgrade_settings(self):
155 """Upgrade git-cola settings"""
156 # Upgrade bookmarks to the new dict-based bookmarks format.
157 if self.bookmarks and not isinstance(self.bookmarks[0], dict):
158 bookmarks = [dict(name=os.path.basename(path), path=path)
159 for path in self.bookmarks]
160 self.values['bookmarks'] = bookmarks
162 if self.recent and not isinstance(self.recent[0], dict):
163 recent = [dict(name=os.path.basename(path), path=path)
164 for path in self.recent]
165 self.values['recent'] = recent
167 def asdict(self):
168 path = self.path()
169 if core.exists(path):
170 return read_json(path)
171 # We couldn't find ~/.config/git-cola, try ~/.cola
172 values = {}
173 path = os.path.join(core.expanduser('~'), '.cola')
174 if core.exists(path):
175 json_values = read_json(path)
176 # Keep only the entries we care about
177 for key in self.values:
178 try:
179 values[key] = json_values[key]
180 except KeyError:
181 pass
182 return values
184 def reload_recent(self):
185 values = self.asdict()
186 self.values['recent'] = mklist(values.get('recent', []))
188 def save_gui_state(self, gui):
189 """Saves settings for a cola view"""
190 name = gui.name()
191 self.gui_state[name] = mkdict(gui.export_state())
192 self.save()
194 def get_gui_state(self, gui):
195 """Returns the saved state for a gui"""
196 try:
197 state = mkdict(self.gui_state[gui.name()])
198 except KeyError:
199 state = self.gui_state[gui.name()] = {}
200 return state
203 class Session(Settings):
204 """Store per-session settings"""
206 _sessions_dir = resources.config_home('sessions')
208 repo = property(lambda self: self.values['repo'])
210 def __init__(self, session_id, repo=None):
211 Settings.__init__(self)
212 self.session_id = session_id
213 self.values.update({'repo': repo})
215 def path(self):
216 return os.path.join(self._sessions_dir, self.session_id)
218 def load(self):
219 path = self.path()
220 if core.exists(path):
221 self.values.update(read_json(path))
222 try:
223 os.unlink(path)
224 except:
225 pass
226 return True
227 return False