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 from timelinelib.calendar.gregorian.dateformatter import GregorianDateFormatter
34 from timelinelib.config.dateformatparser import DateFormatParser
35 from timelinelib.general.observer import Observable
36 from timelinelib.wxgui.utils import display_information_message
37
38
39
40 SELECTED_EVENT_BOX_DRAWER = "selected_event_box_drawer"
41 WINDOW_WIDTH = "window_width"
42 WINDOW_HEIGHT = "window_height"
43 WINDOW_XPOS = "window xpos"
44 WINDOW_YPOS = "window ypos"
45 RECENT_FILES = "recent_files"
46 WEEK_START = "week_start"
47 DATE_FORMAT = "date_format"
48 DEFAULTS = {
49 SELECTED_EVENT_BOX_DRAWER: "Default Event box drawer",
50 WINDOW_WIDTH: "900",
51 WINDOW_HEIGHT: "500",
52 WINDOW_XPOS: "-1",
53 WINDOW_YPOS: "-1",
54 RECENT_FILES: "",
55 WEEK_START: "monday",
56 DATE_FORMAT: "yyyy-mm-dd",
57 }
58
59 MAX_NBR_OF_RECENT_FILES_SAVED = 5
60 ENCODING = "utf-8"
61
62
67
68
70
71 """
72 Provide read and write access to application configuration settings.
73
74 Built as a wrapper around ConfigParser: Properties exist to read and write
75 values but ConfigParser does the actual reading and writing of the
76 configuration file.
77 """
78
83
85 """Read settings from file specified in constructor."""
86 if self.path:
87 self.config_parser.read(self.path)
88
90 """
91 Write settings to file specified in constructor and raise IOError if
92 failed.
93 """
94 f = open(self.path, "w")
95 try:
96 self.config_parser.write(f)
97 finally:
98 f.close()
99
102
105
109
114
124
129
131 ro = self.config_parser.get(DEFAULTSECT, RECENT_FILES).split(",")
132
133
134 ro_filtered = [x for x in ro if x]
135 return ro_filtered
136
138 if not self.open_recent_at_startup:
139 return False
140 else:
141 return len(self.get_recently_opened()) > 0
142
145
147 if path in [":tutorial:", ":numtutorial:"]:
148
149 return
150 if isinstance(path, bytes):
151
152
153 path = path.decode(sys.getfilesystemencoding())
154 abs_path = os.path.abspath(path)
155 current = self.get_recently_opened()
156
157 if abs_path in current:
158 current.remove(abs_path)
159 current.insert(0, abs_path)
160 self.config_parser.set(DEFAULTSECT, RECENT_FILES,
161 (",".join(current[:MAX_NBR_OF_RECENT_FILES_SAVED])))
162
165
167 if week_start not in ["monday", "sunday"]:
168 raise ValueError("Invalid week start.")
169 self.config_parser.set(DEFAULTSECT, WEEK_START, week_start)
170 self._notify()
171
178
180 self.config_parser.set(DEFAULTSECT, cfgid, value)
181
183 return tuple([int(x.strip()) for x in tuple_string[1:-1].split(",")])
184
186 return str(tuple_data)
187
196
199
203 date_format = property(get_date_format, set_date_format)
204
206 try:
207 return str(value)
208 except UnicodeEncodeError:
209 display_information_message(_("Warning"), _("The selected value contains invalid characters and can't be saved"))
210
211 - def _get(self, key):
212 if key in BOOLEANS:
213 return self._get_boolean(key)
214 elif key in INTS:
215 return self._get_int(key)
216 elif key in COLOURS:
217 return self._get_colour(key)
218 elif key in FONTS:
219 return self._get_font(key)
220 else:
221 return self.config_parser.get(DEFAULTSECT, key)
222
224 value = self.config_parser.get(DEFAULTSECT, key)
225 return int(value)
226
228 return self.config_parser.getboolean(DEFAULTSECT, key)
229
231 return self._string_to_tuple(self.config_parser.get(DEFAULTSECT, key))
232
234 return self.config_parser.get(DEFAULTSECT, key)
235
236 - def _set(self, key, value):
237 if key in COLOURS:
238 self._set_colour(key, value)
239 elif key in FONTS:
240 self._set_font(key, value)
241 else:
242 if self._toStr(value) is not None:
243 self.config_parser.set(DEFAULTSECT, key, self._toStr(value))
244 self._notify()
245
247 self.config_parser.set(DEFAULTSECT, key, self._tuple_to_string(value))
248
250 if self._toStr(value) is not None:
251 self.config_parser.set(DEFAULTSECT, key, value)
252 self._notify()
253
254
255
256
257 BOOLEAN_CONFIGS = (
258 {'name': 'show_toolbar', 'default': 'True'},
259 {'name': 'show_sidebar', 'default': 'True'},
260 {'name': 'show_legend', 'default': 'True'},
261 {'name': 'window_maximized', 'default': 'False'},
262 {'name': 'open_recent_at_startup', 'default': 'True'},
263 {'name': 'balloon_on_hover', 'default': 'True'},
264 {'name': 'use_inertial_scrolling', 'default': 'False'},
265 {'name': 'never_show_period_events_as_point_events', 'default': 'False'},
266 {'name': 'draw_point_events_to_right', 'default': 'False'},
267 {'name': 'event_editor_show_period', 'default': 'False'},
268 {'name': 'event_editor_show_time', 'default': 'False'},
269 {'name': 'center_event_texts', 'default': 'False'},
270 {'name': 'uncheck_time_for_new_events', 'default': 'False'},
271 {'name': 'text_below_icon', 'default': 'False'},
272 {'name': 'filtered_listbox_export', 'default': 'False'},
273 {'name': 'colorize_weekends', 'default': 'False'},
274 {'name': 'use_bold_nowline', 'default': 'False'},
275 {'name': 'skip_s_in_decade_text', 'default': 'False'},
276 {'name': 'display_checkmark_on_events_done', 'default': 'False'},
277 {'name': 'never_use_time', 'default': 'False'},
278 {'name': 'use_second', 'default': 'False'},
279 {'name': 'use_date_default_values', 'default': 'False'},
280 {'name': 'hide_events_done', 'default': 'False'},
281 )
282 INT_CONFIGS = (
283 {'name': 'sidebar_width', 'default': '200'},
284 {'name': 'divider_line_slider_pos', 'default': '50'},
285 {'name': 'vertical_space_between_events', 'default': '5'},
286 {'name': 'legend_pos', 'default': '0'},
287 {'name': 'time_scale_pos', 'default': '1'},
288 )
289 STR_CONFIGS = (
290 {'name': 'experimental_features', 'default': ''},
291 {'name': 'event_editor_tab_order', 'default': '01234:'},
292 {'name': 'fuzzy_icon', 'default': 'fuzzy.png'},
293 {'name': 'locked_icon', 'default': 'locked.png'},
294 {'name': 'hyperlink_icon', 'default': 'hyperlink.png'},
295 {'name': 'default_year', 'default': '2020'},
296 {'name': 'default_month', 'default': '02'},
297 {'name': 'default_day', 'default': '03'},
298 )
299 COLOUR_CONFIGS = (
300 {'name': 'now_line_colour', 'default': '(200, 0, 0)'},
301 {'name': 'weekend_colour', 'default': '(255, 255, 255)'},
302 {'name': 'bg_colour', 'default': '(255, 255, 255)'},
303 {'name': 'minor_strip_divider_line_colour', 'default': '(200, 200, 200)'},
304 {'name': 'major_strip_divider_line_colour', 'default': '(200, 200, 200)'},
305 )
306 FONT_CONFIGS = (
307 {'name': 'minor_strip_font', 'default': '10:74:90:90:False:Tahoma:33:(0, 0, 0, 255)'},
308 {'name': 'major_strip_font', 'default': '10:74:90:90:False:Tahoma:33:(0, 0, 0, 255)'},
309 {'name': 'legend_font', 'default': '10:74:90:90:False:Tahoma:33:(0, 0, 0, 255)'},
310 {'name': 'balloon_font', 'default': '10:74:90:90:False:Tahoma:33:(0, 0, 0, 255)'},
311 )
312 BOOLEANS = [d['name'] for d in BOOLEAN_CONFIGS]
313 INTS = [d['name'] for d in INT_CONFIGS]
314 COLOURS = [d['name'] for d in COLOUR_CONFIGS]
315 FONTS = [d['name'] for d in FONT_CONFIGS]
316
317
321
322
323
324 for data in BOOLEAN_CONFIGS + INT_CONFIGS + STR_CONFIGS + COLOUR_CONFIGS + FONT_CONFIGS:
325 setatt(data['name'])
326 DEFAULTS[data['name']] = data['default']
327