1#    Copyright (C) 2005 Jeremy S. Sanders
2#    Email: Jeremy Sanders <jeremy@jeremysanders.net>
3#
4#    This program is free software; you can redistribute it and/or modify
5#    it under the terms of the GNU General Public License as published by
6#    the Free Software Foundation; either version 2 of the License, or
7#    (at your option) any later version.
8#
9#    This program is distributed in the hope that it will be useful,
10#    but WITHOUT ANY WARRANTY; without even the implied warranty of
11#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12#    GNU General Public License for more details.
13#
14#    You should have received a copy of the GNU General Public License along
15#    with this program; if not, write to the Free Software Foundation, Inc.,
16#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17###############################################################################
18
19"""A database for default values of settings."""
20
21from __future__ import division, print_function
22import sys
23import datetime
24
25import numpy as N
26from .. import qtall as qt
27
28def _(text, disambiguation=None, context="Preferences"):
29    """Translate text."""
30    return qt.QCoreApplication.translate(context, text, disambiguation)
31
32# default values to some settings in case the user does not have these
33defaultValues = {
34    # export options
35    'export_DPI': 100,
36    'export_DPI_PDF': 150,
37    'export_DPI_SVG': 96,
38    'export_color': True,
39    'export_antialias': True,
40    'export_quality': 85,
41    'export_background': '#ffffff00',
42    'export_SVG_text_as_text': False,
43
44    # plot options
45    'plot_updatepolicy': -1, # update on document changed
46    'plot_antialias': True,
47    'plot_numthreads': 2,
48
49    # recent files list
50    'main_recentfiles': [],
51
52    # default stylesheet
53    'stylesheet_default': '',
54    # default custom definitons
55    'custom_default': '',
56
57    # colors (isdefault, 'notdefaultcolor')
58    'color_page': (True, 'white'),
59    'color_error': (True, 'red'),
60    'color_command': (True, 'blue'),
61    'color_cntrlline': (True, 'blue'),
62    'color_cntrlcorner': (True, 'black'),
63
64    # document theme
65    'colortheme_default': 'default-latest',
66
67    # further ui options
68    'toolbar_size': 24,
69
70    # if set to true, do UI formatting in US/English
71    'ui_english': False,
72
73    # use cwd as starting directory
74    'dirname_usecwd': False,
75
76    # use document directory for export
77    'dirname_export_location': 'doc',
78
79    # export templates
80    'export_template_single': '%DOCNAME%',
81    'export_template_multi': '%DOCNAME%_%PAGE00%',
82
83    # add import paths
84    'docfile_addimportpaths': True,
85
86    # ask tutorial before?
87    'ask_tutorial': False,
88
89    # log picked points to clipboard or to console
90    'picker_to_clipboard': False,
91    'picker_to_console': True,
92    'picker_sig_figs': 5,
93
94    # add these directories to the python path (colon-separated)
95    'external_pythonpath': '',
96
97    # location of ghostscript (or empty to search)
98    'external_ghostscript': '',
99
100    # translation file to load
101    'translation_file': '',
102
103    # user has disabled version update checks
104    # (packagers: please don't disable here, see disableVersionChecks in
105    #  veusz/utils/version.py)
106    'vercheck_disabled': False,
107    'vercheck_asked_user': False,
108    'vercheck_last_done': (2000,1,1),
109    'vercheck_latest': '',
110
111    # whether to send feedback about usage
112    # (packagers, please don't disable here but in veusz/setting/feedback.py)
113    'feedback_disabled': False,
114    'feedback_asked_user': False,
115
116    # locations considered secure to load
117    'secure_dirs': [],
118    'secure_unsaved': True,
119}
120
121class _SettingDB(object):
122    """A class which provides access to a persistant settings database.
123
124    Items are accesses as a dict, with items as key=value
125    """
126
127    # list of colors
128    colors = ('page', 'error', 'command', 'cntrlline', 'cntrlcorner')
129    # default colors if isdefault is set in the setting
130    color_defaults = {
131        'page': 'LightBase',
132        'error': 'red',
133        'command': 'blue',
134        'cntrlline': 'blue',
135        'cntrlcorner': 'black',
136        }
137
138    def __init__(self):
139        """Initialise the object, reading the settings."""
140
141        # This domain name is fictional!
142        self.domain = 'veusz.org'
143        self.product = 'veusz'
144        self.database = {}
145        self.sepchars = "%%%"
146
147        # read settings using QSettings
148        self.readSettings()
149
150    def color(self, name):
151        """Get a color setting as a QColor."""
152
153        val = self.database['color_' + name]
154        if val[0]:
155            default = self.color_defaults[name]
156            if default == 'LightBase':
157                base = qt.qApp.palette().color(qt.QPalette.Base)
158                if base.value() < 127:
159                    base = qt.QColor(qt.Qt.white)
160                return base
161
162            return qt.QColor(default)
163        else:
164            return qt.QColor(val[1])
165
166    def readSettings(self):
167        """Read the settings using QSettings.
168
169        Entries have / replaced with set of characters self.sepchars
170        This is because it greatly simplifies the logic as QSettings
171        has special meaning for /
172
173        The only issues are that the key may be larger than 255 characters
174        We should probably check for this
175        """
176
177        s = qt.QSettings(self.domain, self.product)
178
179        for key in s.childKeys():
180            val = s.value(key)
181            realkey = key.replace(self.sepchars, '/')
182
183            try:
184                self.database[realkey] = eval(val)
185            except:
186                print('Error interpreting item "%s" in '
187                      'settings file' % realkey, file=sys.stderr)
188
189        # set any defaults which haven't been set
190        for key in defaultValues:
191            if key not in self.database:
192                self.database[key] = defaultValues[key]
193
194        # keep install date for reminders, etc
195        if 'install_date' not in self.database:
196            today = datetime.date.today()
197            self.database['install_date'] = (today.year, today.month, today.day)
198
199    def writeSettings(self):
200        """Write the settings using QSettings.
201
202        This is called by the mainwindow on close
203        """
204
205        s = qt.QSettings(self.domain, self.product)
206
207        # write each entry, keeping track of which ones haven't been written
208        cleankeys = []
209        for key in self.database:
210            cleankey = key.replace('/', self.sepchars)
211            cleankeys.append(cleankey)
212
213            s.setValue(cleankey, repr(self.database[key]))
214
215        # now remove all the values which have been removed
216        for key in list(s.childKeys()):
217            if key not in cleankeys:
218                s.remove(key)
219
220    def get(self, key, defaultval=None):
221        """Return key if it is in database, else defaultval."""
222        return self.database.get(key, defaultval)
223
224    def __getitem__(self, key):
225        """Get the item from the database."""
226        return self.database[key]
227
228    def __setitem__(self, key, value):
229        """Set the value in the database."""
230        self.database[key] = value
231
232    def __delitem__(self, key):
233        """Remove the key from the database."""
234        del self.database[key]
235
236    def __contains__(self, key):
237        """Is the key in the database."""
238        return key in self.database
239
240# create the SettingDB singleton
241settingdb = _SettingDB()
242
243# a normal dict for non-persistent settings
244transient_settings = {
245    # disable safety checks on evaluated code
246    'unsafe_mode': False,
247}
248
249def updateUILocale():
250    """Update locale to one given in preferences."""
251    global uilocale
252
253    if settingdb['ui_english']:
254        uilocale = qt.QLocale.c()
255    else:
256        uilocale = qt.QLocale.system()
257    uilocale.setNumberOptions(qt.QLocale.OmitGroupSeparator)
258
259    qt.QLocale.setDefault(uilocale)
260
261def ui_floattostring(f, maxdp=14):
262    """Convert float to string with more precision."""
263    if not N.isfinite(f):
264        if N.isnan(f):
265            return 'nan'
266        if f < 0:
267            return '-inf'
268        return 'inf'
269    elif 1e-4 <= abs(f) <= 1e5 or f == 0:
270        s = ('%.'+str(maxdp)+'g') % f
271        # strip excess zeros to right
272        if s.find('.') >= 0:
273            s = s.rstrip('0').rstrip('.')
274    else:
275        s = ('%.'+str(maxdp)+'e') % f
276        # split into mantissa/exponent and strip extra zeros, etc
277        mant, expon = s.split('e')
278        mant = mant.rstrip('0').rstrip('.')
279        expon = int(expon)
280        s = '%se%i' % (mant, expon)
281    # make decimal point correct for local
282    s = s.replace('.', uilocale.decimalPoint())
283    return s
284
285def ui_stringtofloat(s):
286    """Convert string to float, allowing for decimal point in different
287    locale."""
288    s = s.replace(uilocale.decimalPoint(), '.')
289    return float(s)
290
291updateUILocale()
292