1 // Aseprite
2 // Copyright (C) 2001-2018  David Capello
3 //
4 // This program is distributed under the terms of
5 // the End-User License Agreement for Aseprite.
6 
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10 
11 #include "app/doc.h"
12 #include "app/ini_file.h"
13 #include "app/pref/preferences.h"
14 #include "app/resource_finder.h"
15 #include "app/tools/ink.h"
16 #include "app/tools/tool.h"
17 #include "doc/sprite.h"
18 #include "she/system.h"
19 
20 namespace app {
21 
22 static Preferences* singleton = nullptr;
23 
24 // static
instance()25 Preferences& Preferences::instance()
26 {
27   ASSERT(singleton);
28   return *singleton;
29 }
30 
Preferences()31 Preferences::Preferences()
32   : app::gen::GlobalPref("")
33 {
34   ASSERT(!singleton);
35   singleton = this;
36 
37   load();
38 
39   // Hide the menu bar depending on:
40   // 1. the native menu bar is available
41   // 2. this is the first run of the program
42   if (she::instance()->menus() &&
43       updater.uuid().empty()) {
44     general.showMenuBar(false);
45   }
46 }
47 
~Preferences()48 Preferences::~Preferences()
49 {
50   save();
51 
52   for (auto& pair : m_tools)
53     delete pair.second;
54 
55   for (auto& pair : m_docs)
56     delete pair.second;
57 
58   ASSERT(singleton == this);
59   singleton = nullptr;
60 }
61 
load()62 void Preferences::load()
63 {
64   app::gen::GlobalPref::load();
65 }
66 
save()67 void Preferences::save()
68 {
69   app::gen::GlobalPref::save();
70 
71   for (auto& pair : m_tools)
72     pair.second->save();
73 
74   for (auto& pair : m_docs)
75     serializeDocPref(pair.first, pair.second, true);
76 
77   flush_config_file();
78 }
79 
isSet(OptionBase & opt) const80 bool Preferences::isSet(OptionBase& opt) const
81 {
82   return (get_config_string(opt.section(), opt.id(), nullptr) != nullptr);
83 }
84 
tool(tools::Tool * tool)85 ToolPreferences& Preferences::tool(tools::Tool* tool)
86 {
87   ASSERT(tool != NULL);
88 
89   auto it = m_tools.find(tool->getId());
90   if (it != m_tools.end()) {
91     return *it->second;
92   }
93   else {
94     std::string section = std::string("tool.") + tool->getId();
95     ToolPreferences* toolPref = new ToolPreferences(section);
96 
97     // Default size for eraser, blur, etc.
98     if (tool->getInk(0)->isEraser() ||
99         tool->getInk(0)->isEffect()) {
100       toolPref->brush.size.setDefaultValue(8);
101     }
102 
103     m_tools[tool->getId()] = toolPref;
104     toolPref->load();
105     return *toolPref;
106   }
107 }
108 
document(const Doc * doc)109 DocumentPreferences& Preferences::document(const Doc* doc)
110 {
111   auto it = m_docs.find(doc);
112   if (it != m_docs.end()) {
113     return *it->second;
114   }
115   else {
116     DocumentPreferences* docPref;
117     if (doc) {
118       docPref = new DocumentPreferences("");
119 
120       // The default preferences for this document are the current
121       // defaults for (document=nullptr).
122       DocumentPreferences& defPref = this->document(nullptr);
123       *docPref = defPref;
124 
125       // Default values for symmetry
126       docPref->symmetry.xAxis.setDefaultValue(doc->sprite()->width()/2);
127       docPref->symmetry.yAxis.setDefaultValue(doc->sprite()->height()/2);
128     }
129     else
130       docPref = new DocumentPreferences("");
131 
132     m_docs[doc] = docPref;
133 
134     // Load document preferences
135     serializeDocPref(doc, docPref, false);
136 
137     return *docPref;
138   }
139 }
140 
removeDocument(Doc * doc)141 void Preferences::removeDocument(Doc* doc)
142 {
143   ASSERT(doc);
144 
145   auto it = m_docs.find(doc);
146   if (it != m_docs.end()) {
147     serializeDocPref(it->first, it->second, true);
148     delete it->second;
149     m_docs.erase(it);
150   }
151 }
152 
onRemoveDocument(Doc * doc)153 void Preferences::onRemoveDocument(Doc* doc)
154 {
155   removeDocument(doc);
156 }
157 
docConfigFileName(const Doc * doc)158 std::string Preferences::docConfigFileName(const Doc* doc)
159 {
160   if (!doc)
161     return "";
162 
163   ResourceFinder rf;
164   std::string fn = doc->filename();
165   for (size_t i=0; i<fn.size(); ++i) {
166     if (fn[i] == ' ' || fn[i] == '/' || fn[i] == '\\' || fn[i] == ':' || fn[i] == '.') {
167       fn[i] = '-';
168     }
169   }
170   rf.includeUserDir(("files/" + fn + ".ini").c_str());
171   return rf.getFirstOrCreateDefault();
172 }
173 
serializeDocPref(const Doc * doc,app::DocumentPreferences * docPref,bool save)174 void Preferences::serializeDocPref(const Doc* doc, app::DocumentPreferences* docPref, bool save)
175 {
176   bool flush_config = false;
177 
178   if (doc) {
179     // We do nothing if the document isn't associated to a file and we
180     // want to save its specific preferences.
181     if (save && !doc->isAssociatedToFile())
182       return;
183 
184     // We always push a new configuration file in the stack to avoid
185     // modifying the default preferences when a document in "doc" is
186     // specified.
187     push_config_state();
188     if (doc->isAssociatedToFile()) {
189       set_config_file(docConfigFileName(doc).c_str());
190       flush_config = true;
191     }
192   }
193 
194   if (save) {
195     docPref->save();
196   }
197   else {
198     // Load default preferences, or preferences from .ini file.
199     docPref->load();
200   }
201 
202   if (doc) {
203     if (flush_config)
204       flush_config_file();
205 
206     pop_config_state();
207   }
208 }
209 
210 } // namespace app
211