# Authors: see git history # # Copyright (c) 2010 Authors # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. import json import os import re import wx from ..i18n import _ from ..utils import cache from .dialogs import info_dialog class PresetsPanel(wx.Panel): """A wx.Panel for loading, saving, and applying presets. A preset is a named collection of settings. From the perspective of this class, a preset is an opaque JSON-serializable object. The PresetsPanel will handle interaction with the user and inform the instantiator of events such as a preset being loaded. Presets starting and ending with "__" will not be shown to the user. This allows for the instantiator to manage hidden presets such as "__LAST__". """ HIDDEN_PRESET_RE = re.compile('^__.*__$') def __init__(self, parent, *args, **kwargs): """Construct a PresetsPanel. The parent is the parent window for this wx.Panel. The parent is expected to implement the following methods: def get_preset_data(self) returns a JSON object representing the current state as a preset def apply_preset_data(self, preset_data): apply the preset data to the GUI, updating GUI elements as necessary def get_preset_suite_name(self): Return a string used in the presets filename, e.g. "lettering" -> "lettering_presets.json". If not defined, "presets.json" will be used. """ kwargs.setdefault('style', wx.BORDER_NONE) wx.Panel.__init__(self, parent, wx.ID_ANY, *args, **kwargs) self.parent = parent self.presets_box = wx.StaticBox(self, wx.ID_ANY, label=_("Presets")) self.preset_chooser = wx.ComboBox(self, wx.ID_ANY) self.update_preset_list() self.preset_chooser.SetSelection(-1) self.load_preset_button = wx.Button(self, wx.ID_ANY, _("Load")) self.load_preset_button.Bind(wx.EVT_BUTTON, self.load_selected_preset) self.add_preset_button = wx.Button(self, wx.ID_ANY, _("Add")) self.add_preset_button.Bind(wx.EVT_BUTTON, self.add_preset) self.overwrite_preset_button = wx.Button(self, wx.ID_ANY, _("Overwrite")) self.overwrite_preset_button.Bind(wx.EVT_BUTTON, self.overwrite_preset) self.delete_preset_button = wx.Button(self, wx.ID_ANY, _("Delete")) self.delete_preset_button.Bind(wx.EVT_BUTTON, self.delete_preset) presets_sizer = wx.StaticBoxSizer(self.presets_box, wx.HORIZONTAL) self.preset_chooser.SetMinSize((200, -1)) presets_sizer.Add(self.preset_chooser, 1, wx.LEFT | wx.BOTTOM | wx.EXPAND, 10) presets_sizer.Add(self.load_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 10) presets_sizer.Add(self.add_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 10) presets_sizer.Add(self.overwrite_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 10) presets_sizer.Add(self.delete_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 10) self.SetSizerAndFit(presets_sizer) self.Layout() @property @cache def suite_name(self): try: return self.parent.get_preset_suite_name() + "_presets" except AttributeError: return "presets" @cache def presets_path(self): try: import appdirs config_path = appdirs.user_config_dir('inkstitch') except ImportError: config_path = os.path.expanduser('~/.inkstitch') if not os.path.exists(config_path): os.makedirs(config_path) return os.path.join(config_path, '%s.json' % self.suite_name) def _load_presets(self): try: with open(self.presets_path(), 'r') as presets: presets = json.load(presets) return presets except (IOError, ValueError): return {} def _save_presets(self, presets): with open(self.presets_path(), 'w') as presets_file: json.dump(presets, presets_file) def update_preset_list(self): preset_names = list(self._load_presets().keys()) preset_names = [preset for preset in preset_names if not self.is_hidden(preset)] self.preset_chooser.SetItems(sorted(preset_names)) def is_hidden(self, preset_name): return self.HIDDEN_PRESET_RE.match(preset_name) def get_preset_name(self): preset_name = self.preset_chooser.GetValue().strip() if preset_name: return preset_name else: info_dialog(self, _("Please enter or select a preset name first."), caption=_('Preset')) return def check_and_load_preset(self, preset_name): preset = self._load_presets().get(preset_name) if not preset: info_dialog(self, _('Preset "%s" not found.') % preset_name, caption=_('Preset')) return preset def store_preset(self, preset_name, data): presets = self._load_presets() presets[preset_name] = data self._save_presets(presets) self.update_preset_list() def add_preset(self, event, overwrite=False): preset_name = self.get_preset_name() if not preset_name: return if not overwrite and preset_name in self._load_presets(): info_dialog(self, _('Preset "%s" already exists. Please use another name or press "Overwrite"') % preset_name, caption=_('Preset')) self.store_preset(preset_name, self.parent.get_preset_data()) event.Skip() def overwrite_preset(self, event): self.add_preset(event, overwrite=True) def load_preset(self, preset_name): preset = self.check_and_load_preset(preset_name) if not preset: return self.parent.apply_preset_data(preset) def load_selected_preset(self, event): preset_name = self.get_preset_name() if not preset_name: return self.load_preset(preset_name) event.Skip() def delete_preset(self, event): preset_name = self.get_preset_name() if not preset_name: return preset = self.check_and_load_preset(preset_name) if not preset: return presets = self._load_presets() presets.pop(preset_name, None) self._save_presets(presets) self.update_preset_list() self.preset_chooser.SetValue("") event.Skip()