summaryrefslogtreecommitdiff
path: root/lib/gui/presets.py
blob: b3312f0e0101a3a38a539348803d55dc6c75e3d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
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.ALIGN_CENTER_VERTICAL | 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 = 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()