1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 Handle application configuration.
21
22 This module is global and can be used by all modules. Before accessing
23 configurations, the read function should be called. To save the current
24 configuration back to file, call the write method.
25 """
26
27
28 from ConfigParser import ConfigParser
29 from ConfigParser import DEFAULTSECT
30 import os.path
31 import sys
32
33 import wx
34
35 from timelinelib.calendar.gregorian.dateformatter import GregorianDateFormatter
36 from timelinelib.config.dateformatparser import DateFormatParser
37 from timelinelib.general.observer import Observable
38 from timelinelib.wxgui.components.font import Font
39 from timelinelib.wxgui.utils import display_information_message
40
41
42
43 SELECTED_EVENT_BOX_DRAWER = "selected_event_box_drawer"
44 WINDOW_WIDTH = "window_width"
45 WINDOW_HEIGHT = "window_height"
46 WINDOW_XPOS = "window xpos"
47 WINDOW_YPOS = "window ypos"
48 RECENT_FILES = "recent_files"
49 WEEK_START = "week_start"
50 DATE_FORMAT = "date_format"
51 DEFAULTS = {
52 SELECTED_EVENT_BOX_DRAWER: "Default Event box drawer",
53 WINDOW_WIDTH: "900",
54 WINDOW_HEIGHT: "500",
55 WINDOW_XPOS: "-1",
56 WINDOW_YPOS: "-1",
57 RECENT_FILES: "",
58 WEEK_START: "monday",
59 DATE_FORMAT: "yyyy-mm-dd",
60 }
61
62 MAX_NBR_OF_RECENT_FILES_SAVED = 5
63 ENCODING = "utf-8"
64
65
70
71
73
74 """
75 Provide read and write access to application configuration settings.
76
77 Built as a wrapper around ConfigParser: Properties exist to read and write
78 values but ConfigParser does the actual reading and writing of the
79 configuration file.
80 """
81
86
88 """Read settings from file specified in constructor."""
89 if self.path:
90 self.config_parser.read(self.path)
91
93 """
94 Write settings to file specified in constructor and raise IOError if
95 failed.
96 """
97 f = open(self.path, "w")
98 try:
99 self.config_parser.write(f)
100 finally:
101 f.close()
102
105
108
112
117
127
132
134 ro = self.config_parser.get(DEFAULTSECT, RECENT_FILES).decode(ENCODING).split(",")
135
136
137 ro_filtered = [x for x in ro if x]
138 return ro_filtered
139
141 if not self.open_recent_at_startup:
142 return False
143 else:
144 return len(self.get_recently_opened()) > 0
145
148
150 if path in [":tutorial:"]:
151
152 return
153 if isinstance(path, str):
154
155
156 path = path.decode(sys.getfilesystemencoding())
157 abs_path = os.path.abspath(path)
158 current = self.get_recently_opened()
159
160 if abs_path in current:
161 current.remove(abs_path)
162 current.insert(0, abs_path)
163 self.config_parser.set(DEFAULTSECT, RECENT_FILES,
164 (",".join(current[:MAX_NBR_OF_RECENT_FILES_SAVED])).encode(ENCODING))
165
168
170 if week_start not in ["monday", "sunday"]:
171 raise ValueError("Invalid week start.")
172 self.config_parser.set(DEFAULTSECT, WEEK_START, week_start)
173 self._notify()
174
181
183 self.config_parser.set(DEFAULTSECT, cfgid, value)
184
186 return tuple([int(x.strip()) for x in tuple_string[1:-1].split(",")])
187
189 return str(tuple_data)
190
198
201
205 date_format = property(get_date_format, set_date_format)
206
208 try:
209 return str(value)
210 except UnicodeEncodeError:
211 display_information_message(_("Warning"), _("The selected value contains invalid characters and can't be saved"))
212
213 - def _get(self, key):
214 if key in BOOLEANS:
215 return self._get_boolean(key)
216 elif key in INTS:
217 return self._get_int(key)
218 elif key in COLOURS:
219 return self._get_colour(key)
220 elif key in FONTS:
221 return self._get_font(key)
222 else:
223 return self.config_parser.get(DEFAULTSECT, key)
224
226 value = self.config_parser.get(DEFAULTSECT, key)
227 return int(value)
228
230 return self.config_parser.getboolean(DEFAULTSECT, key)
231
233 return self._string_to_tuple(self.config_parser.get(DEFAULTSECT, key))
234
236 return self.config_parser.get(DEFAULTSECT, key)
237
238 - def _set(self, key, value):
239 if key in COLOURS:
240 self._set_colour(key, value)
241 elif key in FONTS:
242 self._set_font(key, value)
243 else:
244 if self._toStr(value) is not None:
245 self.config_parser.set(DEFAULTSECT, key, self._toStr(value))
246 self._notify()
247
249 self.config_parser.set(DEFAULTSECT, key, self._tuple_to_string(value))
250
252 if self._toStr(value) is not None:
253 self.config_parser.set(DEFAULTSECT, key, value)
254 self._notify()
255
256
257
258
259 BOOLEAN_CONFIGS = (
260 {'name': 'show_toolbar', 'default': 'True'},
261 {'name': 'show_sidebar', 'default': 'True'},
262 {'name': 'show_legend', 'default': 'True'},
263 {'name': 'window_maximized', 'default': 'False'},
264 {'name': 'open_recent_at_startup', 'default': 'True'},
265 {'name': 'balloon_on_hover', 'default': 'True'},
266 {'name': 'use_inertial_scrolling', 'default': 'False'},
267 {'name': 'never_show_period_events_as_point_events', 'default': 'False'},
268 {'name': 'draw_point_events_to_right', 'default': 'False'},
269 {'name': 'event_editor_show_period', 'default': 'False'},
270 {'name': 'event_editor_show_time', 'default': 'False'},
271 {'name': 'center_event_texts', 'default': 'False'},
272 {'name': 'uncheck_time_for_new_events', 'default': 'False'},
273 {'name': 'text_below_icon', 'default': 'False'},
274 {'name': 'filtered_listbox_export', 'default': 'False'},
275 {'name': 'colorize_weekends', 'default': 'False'},
276 {'name': 'skip_s_in_decade_text', 'default': 'False'},
277 {'name': 'display_checkmark_on_events_done', 'default': 'False'},
278 {'name': 'never_use_time', 'default': 'False'},
279 {'name': 'hide_events_done', 'default': 'False'},
280 )
281 INT_CONFIGS = (
282 {'name': 'sidebar_width', 'default': '200'},
283 {'name': 'divider_line_slider_pos', 'default': '50'},
284 {'name': 'vertical_space_between_events', 'default': '5'},
285 {'name': 'legend_pos', 'default': '0'},
286 )
287 STR_CONFIGS = (
288 {'name': 'experimental_features', 'default': ''},
289 {'name': 'event_editor_tab_order', 'default': '01234:'},
290 {'name': 'fuzzy_icon', 'default': 'fuzzy.png'},
291 {'name': 'locked_icon', 'default': 'locked.png'},
292 {'name': 'hyperlink_icon', 'default': 'hyperlink.png'},
293 )
294 COLOUR_CONFIGS = (
295 {'name': 'now_line_colour', 'default': '(200, 0, 0)'},
296 {'name': 'weekend_colour', 'default': '(255, 255, 255)'},
297 {'name': 'bg_colour', 'default': '(255, 255, 255)'},
298 {'name': 'minor_strip_divider_line_colour', 'default': '(200, 200, 200)'},
299 {'name': 'major_strip_divider_line_colour', 'default': '(200, 200, 200)'},
300 )
301 FONT_CONFIGS = (
302 {'name': 'minor_strip_font', 'default': '10:74:90:90:False:Tahoma:33:(0, 0, 0, 255)'},
303 {'name': 'major_strip_font', 'default': '10:74:90:90:False:Tahoma:33:(0, 0, 0, 255)'},
304 {'name': 'legend_font', 'default': '10:74:90:90:False:Tahoma:33:(0, 0, 0, 255)'},
305 {'name': 'balloon_font', 'default': '10:74:90:90:False:Tahoma:33:(0, 0, 0, 255)'},
306 )
307 BOOLEANS = [d['name'] for d in BOOLEAN_CONFIGS]
308 INTS = [d['name'] for d in INT_CONFIGS]
309 COLOURS = [d['name'] for d in COLOUR_CONFIGS]
310 FONTS = [d['name'] for d in FONT_CONFIGS]
311
312
316
317
318 for data in BOOLEAN_CONFIGS + INT_CONFIGS + STR_CONFIGS + COLOUR_CONFIGS + FONT_CONFIGS:
319 setatt(data['name'])
320 DEFAULTS[data['name']] = data['default']
321