1# Copyright (c) 2018 Ultimaker B.V.
2# Cura is released under the terms of the LGPLv3 or higher.
3
4from typing import Optional, List
5
6from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
7
8from UM.Logger import Logger
9from UM.Preferences import Preferences
10from UM.Resources import Resources
11
12from UM.i18n import i18nCatalog
13from cura.Settings.SettingVisibilityPreset import SettingVisibilityPreset
14
15catalog = i18nCatalog("cura")
16
17
18class SettingVisibilityPresetsModel(QObject):
19    onItemsChanged = pyqtSignal()
20    activePresetChanged = pyqtSignal()
21
22    def __init__(self, preferences: Preferences, parent = None) -> None:
23        super().__init__(parent)
24
25        self._items = []  # type: List[SettingVisibilityPreset]
26        self._custom_preset = SettingVisibilityPreset(preset_id = "custom", name = "Custom selection", weight = -100)
27
28        self._populate()
29
30        basic_item = self.getVisibilityPresetById("basic")
31        if basic_item is not None:
32            basic_visibile_settings = ";".join(basic_item.settings)
33        else:
34            Logger.log("w", "Unable to find the basic visiblity preset.")
35            basic_visibile_settings = ""
36
37        self._preferences = preferences
38
39        # Preference to store which preset is currently selected
40        self._preferences.addPreference("cura/active_setting_visibility_preset", "basic")
41
42        # Preference that stores the "custom" set so it can always be restored (even after a restart)
43        self._preferences.addPreference("cura/custom_visible_settings", basic_visibile_settings)
44        self._preferences.preferenceChanged.connect(self._onPreferencesChanged)
45
46        self._active_preset_item = self.getVisibilityPresetById(self._preferences.getValue("cura/active_setting_visibility_preset"))
47
48        # Initialize visible settings if it is not done yet
49        visible_settings = self._preferences.getValue("general/visible_settings")
50
51        if not visible_settings:
52            new_visible_settings = self._active_preset_item.settings if self._active_preset_item is not None else []
53            self._preferences.setValue("general/visible_settings", ";".join(new_visible_settings))
54        else:
55            self._onPreferencesChanged("general/visible_settings")
56
57        self.activePresetChanged.emit()
58
59    def getVisibilityPresetById(self, item_id: str) -> Optional[SettingVisibilityPreset]:
60        result = None
61        for item in self._items:
62            if item.presetId == item_id:
63                result = item
64                break
65        return result
66
67    def _populate(self) -> None:
68        from cura.CuraApplication import CuraApplication
69        items = []  # type: List[SettingVisibilityPreset]
70        items.append(self._custom_preset)
71        for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.SettingVisibilityPreset):
72            setting_visibility_preset = SettingVisibilityPreset()
73            try:
74                setting_visibility_preset.loadFromFile(file_path)
75            except Exception:
76                Logger.logException("e", "Failed to load setting preset %s", file_path)
77
78            items.append(setting_visibility_preset)
79
80        # Add the "all" visibility:
81        all_setting_visibility_preset = SettingVisibilityPreset(preset_id = "all", name = "All", weight = 9001)
82        all_setting_visibility_preset.setSettings(list(CuraApplication.getInstance().getMachineManager().getAllSettingKeys()))
83        items.append(all_setting_visibility_preset)
84        # Sort them on weight (and if that fails, use ID)
85        items.sort(key = lambda k: (int(k.weight), k.presetId))
86
87        self.setItems(items)
88
89    @pyqtProperty("QVariantList", notify = onItemsChanged)
90    def items(self) -> List[SettingVisibilityPreset]:
91        return self._items
92
93    def setItems(self, items: List[SettingVisibilityPreset]) -> None:
94        if self._items != items:
95            self._items = items
96            self.onItemsChanged.emit()
97
98    @pyqtSlot(str)
99    def setActivePreset(self, preset_id: str) -> None:
100        if self._active_preset_item is not None and preset_id == self._active_preset_item.presetId:
101            Logger.log("d", "Same setting visibility preset [%s] selected, do nothing.", preset_id)
102            return
103
104        preset_item = self.getVisibilityPresetById(preset_id)
105        if preset_item is None:
106            Logger.log("w", "Tried to set active preset to unknown id [%s]", preset_id)
107            return
108
109        need_to_save_to_custom = self._active_preset_item is None or (self._active_preset_item.presetId == "custom" and preset_id != "custom")
110        if need_to_save_to_custom:
111            # Save the current visibility settings to custom
112            current_visibility_string = self._preferences.getValue("general/visible_settings")
113            if current_visibility_string:
114                self._preferences.setValue("cura/custom_visible_settings", current_visibility_string)
115
116        new_visibility_string = ";".join(preset_item.settings)
117        if preset_id == "custom":
118            # Get settings from the stored custom data
119            new_visibility_string = self._preferences.getValue("cura/custom_visible_settings")
120            if new_visibility_string is None:
121                new_visibility_string = self._preferences.getValue("general/visible_settings")
122        self._preferences.setValue("general/visible_settings", new_visibility_string)
123
124        self._preferences.setValue("cura/active_setting_visibility_preset", preset_id)
125        self._active_preset_item = preset_item
126        self.activePresetChanged.emit()
127
128    @pyqtProperty(str, notify = activePresetChanged)
129    def activePreset(self) -> str:
130        if self._active_preset_item is not None:
131            return self._active_preset_item.presetId
132        return ""
133
134    def _onPreferencesChanged(self, name: str) -> None:
135        if name != "general/visible_settings":
136            return
137
138        # Find the preset that matches with the current visible settings setup
139        visibility_string = self._preferences.getValue("general/visible_settings")
140        if not visibility_string:
141            return
142
143        visibility_set = set(visibility_string.split(";"))
144        matching_preset_item = None
145        for item in self._items:
146            if item.presetId == "custom":
147                continue
148            if set(item.settings) == visibility_set:
149                matching_preset_item = item
150                break
151
152        item_to_set = self._active_preset_item
153        if matching_preset_item is None:
154            # The new visibility setup is "custom" should be custom
155            if self._active_preset_item is None or self._active_preset_item.presetId == "custom":
156                # We are already in custom, just save the settings
157                self._preferences.setValue("cura/custom_visible_settings", visibility_string)
158            else:
159                # We need to move to custom preset.
160                item_to_set = self.getVisibilityPresetById("custom")
161        else:
162            item_to_set = matching_preset_item
163
164        # If we didn't find a matching preset, fallback to custom.
165        if item_to_set is None:
166            item_to_set = self._custom_preset
167
168        if self._active_preset_item is None or self._active_preset_item.presetId != item_to_set.presetId:
169            self._active_preset_item = item_to_set
170            if self._active_preset_item is not None:
171                self._preferences.setValue("cura/active_setting_visibility_preset", self._active_preset_item.presetId)
172            self.activePresetChanged.emit()
173