Package Gnumed :: Package timelinelib :: Package config :: Module dotfile
[frames] | no frames]

Source Code for Module Gnumed.timelinelib.config.dotfile

  1  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg 
  2  # 
  3  # This file is part of Timeline. 
  4  # 
  5  # Timeline is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # Timeline is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>. 
 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  # Name used in ConfigParser 
 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  # Some settings 
 59  MAX_NBR_OF_RECENT_FILES_SAVED = 5 
 60  ENCODING = "utf-8" 
 61   
 62   
63 -def read_config(path):
64 config = Config(path) 65 config.read() 66 return config
67 68
69 -class Config(Observable):
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
79 - def __init__(self, path):
80 Observable.__init__(self) 81 self.path = path 82 self.config_parser = ConfigParser(DEFAULTS)
83
84 - def read(self):
85 """Read settings from file specified in constructor.""" 86 if self.path: 87 self.config_parser.read(self.path)
88
89 - def write(self):
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
101 return self.config_parser.get(DEFAULTSECT, SELECTED_EVENT_BOX_DRAWER)
102
103 - def set_selected_event_box_drawer(self, selected):
104 self.config_parser.set(DEFAULTSECT, SELECTED_EVENT_BOX_DRAWER, str(selected.encode("utf-8")))
105
106 - def get_window_size(self):
107 return (self.config_parser.getint(DEFAULTSECT, WINDOW_WIDTH), 108 self.config_parser.getint(DEFAULTSECT, WINDOW_HEIGHT))
109
110 - def set_window_size(self, size):
111 width, height = size 112 self.config_parser.set(DEFAULTSECT, WINDOW_WIDTH, str(width)) 113 self.config_parser.set(DEFAULTSECT, WINDOW_HEIGHT, str(height))
114
115 - def get_window_pos(self):
116 width, _ = self.get_window_size() 117 # Make sure that some area of the window is visible on the screen 118 # Some part of the titlebar must be visible 119 xpos = max(-width + 100, 120 self.config_parser.getint(DEFAULTSECT, WINDOW_XPOS)) 121 # Titlebar must not be above the upper screen border 122 ypos = max(0, self.config_parser.getint(DEFAULTSECT, WINDOW_YPOS)) 123 return (xpos, ypos)
124
125 - def set_window_pos(self, pos):
126 xpos, ypos = pos 127 self.config_parser.set(DEFAULTSECT, WINDOW_XPOS, str(xpos)) 128 self.config_parser.set(DEFAULTSECT, WINDOW_YPOS, str(ypos))
129
130 - def get_recently_opened(self):
131 ro = self.config_parser.get(DEFAULTSECT, RECENT_FILES).split(",") 132 # Filter out empty elements: "".split(",") will return [""] but we want 133 # the empty list 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
144 return self.get_recently_opened()[0]
145
146 - def append_recently_opened(self, path):
147 if path in [":tutorial:", ":numtutorial:"]: 148 # Special timelines should not be saved 149 return 150 if isinstance(path, bytes): 151 # This path might have come from the command line so we need to convert 152 # it to unicode 153 path = path.decode(sys.getfilesystemencoding()) 154 abs_path = os.path.abspath(path) 155 current = self.get_recently_opened() 156 # Just keep one entry of the same path in the list 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
163 - def get_week_start(self):
164 return self.config_parser.get(DEFAULTSECT, WEEK_START)
165
166 - def set_week_start(self, week_start):
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
172 - def get_shortcut_key(self, cfgid, default):
173 try: 174 return self.config_parser.get(DEFAULTSECT, cfgid) 175 except: 176 self.set_shortcut_key(cfgid, default) 177 return default
178
179 - def set_shortcut_key(self, cfgid, value):
180 self.config_parser.set(DEFAULTSECT, cfgid, value)
181
182 - def _string_to_tuple(self, tuple_string):
183 return tuple([int(x.strip()) for x in tuple_string[1:-1].split(",")])
184
185 - def _tuple_to_string(self, tuple_data):
186 return str(tuple_data)
187
188 - def get_date_formatter(self):
189 parser = DateFormatParser().parse(self.get_date_format()) 190 date_formatter = GregorianDateFormatter() 191 date_formatter.set_defaults(self.use_date_default_values, self.default_year, self.default_month, self.default_day) 192 date_formatter.set_separators(*parser.get_separators()) 193 date_formatter.set_region_order(*parser.get_region_order()) 194 date_formatter.use_abbreviated_name_for_month(parser.use_abbreviated_month_names()) 195 return date_formatter
196
197 - def get_date_format(self):
198 return self.config_parser.get(DEFAULTSECT, DATE_FORMAT)
199
200 - def set_date_format(self, date_format):
201 self.config_parser.set(DEFAULTSECT, DATE_FORMAT, date_format) 202 self._notify()
203 date_format = property(get_date_format, set_date_format) 204
205 - def _toStr(self, value):
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
223 - def _get_int(self, key):
224 value = self.config_parser.get(DEFAULTSECT, key) 225 return int(value)
226
227 - def _get_boolean(self, key):
228 return self.config_parser.getboolean(DEFAULTSECT, key)
229
230 - def _get_colour(self, key):
231 return self._string_to_tuple(self.config_parser.get(DEFAULTSECT, key))
232
233 - def _get_font(self, key):
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
246 - def _set_colour(self, key, value):
247 self.config_parser.set(DEFAULTSECT, key, self._tuple_to_string(value))
248
249 - def _set_font(self, key, value):
250 if self._toStr(value) is not None: 251 self.config_parser.set(DEFAULTSECT, key, value) 252 self._notify()
253 254 255 # To add a new boolean, integer, colour or string configuration item 256 # you only have to add that item to one of the dictionaries below. 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
318 -def setatt(name):
319 setattr(Config, name, property(lambda self: self._get(name), 320 lambda self, value: self._set(name, str(value))))
321 322 # Create properties dynamically 323 for data in BOOLEAN_CONFIGS + INT_CONFIGS + STR_CONFIGS + COLOUR_CONFIGS + FONT_CONFIGS: 324 setatt(data['name']) 325 DEFAULTS[data['name']] = data['default'] 326