startup: add support for XDG user config dirs · mypaint/mypaint@cf723b7
@@ -10,8 +10,9 @@
1010import gettext
1111import os, sys
1212from os.path import join
13-import gtk, gobject
14-gdk = gtk.gdk
13+import gobject
14+import gtk
15+from gtk import gdk
1516from lib import brush, helpers, mypaintlib
1617import filehandling, keyboard, brushmanager, windowing, document, layout
1718import brushmodifier, linemode
@@ -29,37 +30,51 @@ class Application: # singleton
2930 initialization, called by main.py or by the testing scripts.
3031 """
313232-def __init__(self, datapath, extradata, confpath, filenames):
33+def __init__(self, filenames, app_datapath, app_extradatapath,
34+user_datapath, user_confpath, fullscreen=False):
3335"""Construct, but do not run.
343635- :`datapath`:
36- Usually ``$PREFIX/share/mypaint``. Where MyPaint should find its
37- app-specific read-only data, e.g. UI definition XML, backgrounds
38- and brush defintions.
39- :`extradata`:
40- Where to find the defaults for MyPaint's themeable UI icons. This
41- will be effectively used in addition to ``$XDG_DATA_DIRS`` for the
42- purposes of icon lookup. Normally it's ``$PREFIX/share``, to support
43- unusual installations outside the usual locations. It should contain
44- an ``icons/`` subdirectory.
45- :`confpath`:
46- Where the user's configuration is stored. ``$HOME/.mypaint`` is
47- typical on Unix-like OSes.
37+ :params filenames: The list of files to load.
38+ Note: only the first is used.
39+ :param app_datapath: App-specific read-only data area.
40+ Path used for UI definition XML, and the default sets of backgrounds,
41+ palettes, and brush defintions. Often $PREFIX/share/.
42+ :param app_extradatapath: Extra search path for themeable UI icons.
43+ This will be used in addition to $XDG_DATA_DIRS for the purposes of
44+ icon lookup. Normally it's $PREFIX/share, to support unusual
45+ installations outside the usual locations. It should contain an
46+ icons/ subdirectory.
47+ :param user_datapath: Location of the user's app-specific data.
48+ For MyPaint, this means the user's brushes, backgrounds, and
49+ scratchpads. Commonly $XDG_DATA_HOME/mypaint, i.e.
50+ ~/.local/share/mypaint
51+ :param user_confpath: Location of the user's app-specific config area.
52+ This is where MyPaint will save user preferences data and the
53+ keyboard accelerator map. Commonly $XDG_CONFIG_HOME/mypaint, i.e.
54+ ~/.config/mypaint
55+ :param fullscreen: Go fullscreen after starting.
56+4857 """
49-self.confpath = confpath
50-self.datapath = datapath
58+59+self.user_confpath = user_confpath #: User configs (see __init__)
60+self.user_datapath = user_datapath #: User data (see __init__)
61+62+self.datapath = app_datapath
51635264# create config directory, and subdirs where the user might drop files
53-# TODO make scratchpad dir something pulled from preferences #PALETTE1
54-for d in ['', 'backgrounds', 'brushes', 'scratchpads']:
55-d = os.path.join(self.confpath, d)
56-if not os.path.isdir(d):
57-os.mkdir(d)
58-print 'Created', d
65+for basedir in [self.user_confpath, self.user_datapath]:
66+if not os.path.isdir(basedir):
67+os.mkdir(basedir)
68+print 'Created basedir', basedir
69+for datasubdir in ['backgrounds', 'brushes', 'scratchpads']:
70+datadir = os.path.join(self.user_datapath, datasubdir)
71+if not os.path.isdir(datadir):
72+os.mkdir(datadir)
73+print 'Created data subdir', datadir
59746075# Default location for our icons. The user's theme can override these.
6176icon_theme = gtk.icon_theme_get_default()
62-icon_theme.append_search_path(join(extradata, "icons"))
77+icon_theme.append_search_path(join(app_extradatapath, "icons"))
63786479# Icon sanity check
6580if not icon_theme.has_icon('mypaint') \
@@ -77,7 +92,7 @@ def __init__(self, datapath, extradata, confpath, filenames):
7792gtk.window_set_default_icon_name('mypaint')
78937994# Stock items, core actions, and menu structure
80-builder_xml = join(datapath, "gui", "mypaint.xml")
95+builder_xml = join(self.datapath, "gui", "mypaint.xml")
8196self.builder = gtk.Builder()
8297self.builder.set_translation_domain("mypaint")
8398self.builder.add_from_file(builder_xml)
@@ -113,7 +128,10 @@ def __init__(self, datapath, extradata, confpath, filenames):
113128signal_callback_objs.append(self.doc)
114129signal_callback_objs.append(self.doc.modes)
115130self.scratchpad_doc = document.Document(self, leader=self.doc)
116-self.brushmanager = brushmanager.BrushManager(join(datapath, 'brushes'), join(confpath, 'brushes'), self)
131+self.brushmanager = brushmanager.BrushManager(
132+join(app_datapath, 'brushes'),
133+join(user_datapath, 'brushes'),
134+self)
117135self.filehandler = filehandling.FileHandler(self)
118136signal_callback_objs.append(self.filehandler)
119137self.brushmodifier = brushmodifier.BrushModifier(self)
@@ -131,7 +149,7 @@ def __init__(self, datapath, extradata, confpath, filenames):
131149132150self.brush_color_manager = BrushColorManager(self)
133151self.brush_color_manager.set_picker_cursor(self.cursor_color_picker)
134-self.brush_color_manager.set_data_path(datapath)
152+self.brush_color_manager.set_data_path(self.datapath)
135153136154self.init_brush_adjustments()
137155@@ -151,7 +169,8 @@ def __init__(self, datapath, extradata, confpath, filenames):
151169self.kbm.start_listening()
152170self.filehandler.doc = self.doc
153171self.filehandler.filename = None
154-pygtkcompat.gtk.accel_map_load(join(self.confpath, 'accelmap.conf'))
172+pygtkcompat.gtk.accel_map_load(join(self.user_confpath,
173+'accelmap.conf'))
155174156175# Load the background settings window.
157176# FIXME: this line shouldn't be needed, but we need to load this up
@@ -170,34 +189,41 @@ def at_application_start(*junk):
170189if filenames:
171190# Open only the first file, no matter how many has been specified
172191# If the file does not exist just set it as the file to save to
173-fn = filenames[0].replace('file:///', '/') # some filebrowsers do this (should only happen with outdated mypaint.desktop)
192+fn = filenames[0].replace('file:///', '/')
193+# ^ some filebrowsers do this (should only happen with outdated
194+# mypaint.desktop)
174195if not os.path.exists(fn):
175196self.filehandler.filename = fn
176197else:
177198self.filehandler.open_file(fn)
178199179200# Load last scratchpad
180201if not self.preferences["scratchpad.last_opened_scratchpad"]:
181-self.preferences["scratchpad.last_opened_scratchpad"] = self.filehandler.get_scratchpad_autosave()
182-self.scratchpad_filename = self.preferences["scratchpad.last_opened_scratchpad"]
202+self.preferences["scratchpad.last_opened_scratchpad"] \
203+= self.filehandler.get_scratchpad_autosave()
204+self.scratchpad_filename \
205+= self.preferences["scratchpad.last_opened_scratchpad"]
183206if os.path.isfile(self.scratchpad_filename):
184207try:
185208self.filehandler.open_scratchpad(self.scratchpad_filename)
186209except AttributeError, e:
187210print "Scratchpad widget isn't initialised yet, so cannot centre"
188211189-190212self.apply_settings()
191213if not self.pressure_devices:
192214print 'No pressure sensitive devices found.'
193215self.drawWindow.present()
194216217+# Handle fullscreen command line option
218+if fullscreen:
219+self.drawWindow.fullscreen_cb()
220+195221gobject.idle_add(at_application_start)
196222197223def save_settings(self):
198224"""Saves the current settings to persistent storage."""
199225def save_config():
200-settingspath = join(self.confpath, 'settings.json')
226+settingspath = join(self.user_confpath, 'settings.json')
201227jsonstr = helpers.json_dumps(self.preferences)
202228f = open(settingspath, 'w')
203229f.write(jsonstr)
@@ -221,15 +247,15 @@ def load_settings(self):
221247def get_legacy_config():
222248dummyobj = {}
223249tmpdict = {}
224-settingspath = join(self.confpath, 'settings.conf')
250+settingspath = join(self.user_confpath, 'settings.conf')
225251if os.path.exists(settingspath):
226252exec open(settingspath) in dummyobj
227253tmpdict['saving.scrap_prefix'] = dummyobj['save_scrap_prefix']
228254tmpdict['input.device_mode'] = dummyobj['input_devices_mode']
229255tmpdict['input.global_pressure_mapping'] = dummyobj['global_pressure_mapping']
230256return tmpdict
231257def get_json_config():
232-settingspath = join(self.confpath, 'settings.json')
258+settingspath = join(self.user_confpath, 'settings.json')
233259jsonstr = open(settingspath).read()
234260try:
235261return helpers.json_loads(jsonstr)
@@ -526,7 +552,7 @@ def update_input_devices(self):
526552print ''
527553528554def save_gui_config(self):
529-pygtkcompat.gtk.accel_map_save(join(self.confpath, 'accelmap.conf'))
555+pygtkcompat.gtk.accel_map_save(join(self.user_confpath, 'accelmap.conf'))
530556self.save_settings()
531557532558def message_dialog(self, text, type=gtk.MESSAGE_INFO, flags=0,