1 // Theme.cc for FbTk - Fluxbox ToolKit
2 // Copyright (c) 2002 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21
22 #include "Theme.hh"
23
24 #include "XrmDatabaseHelper.hh"
25 #include "App.hh"
26 #include "StringUtil.hh"
27 #include "FileUtil.hh"
28 #include "I18n.hh"
29 #include "Image.hh"
30 #include "STLUtil.hh"
31
32 #ifdef HAVE_CSTDIO
33 #include <cstdio>
34 #else
35 #include <stdio.h>
36 #endif
37 #include <memory>
38 #include <iostream>
39 #include <algorithm>
40
41 using std::cerr;
42 using std::endl;
43 using std::string;
44
45 namespace FbTk {
46
47 struct LoadThemeHelper {
LoadThemeHelperFbTk::LoadThemeHelper48 LoadThemeHelper():m_tm(ThemeManager::instance()) {}
operator ()FbTk::LoadThemeHelper49 void operator ()(Theme *tm) {
50 m_tm.loadTheme(*tm);
51 }
operator ()FbTk::LoadThemeHelper52 void operator ()(ThemeManager::ThemeList &tmlist) {
53
54 STLUtil::forAll(tmlist, *this);
55 // send reconfiguration signal to theme and listeners
56 ThemeManager::ThemeList::iterator it = tmlist.begin();
57 ThemeManager::ThemeList::iterator it_end = tmlist.end();
58 for (; it != it_end; ++it) {
59 (*it)->reconfigSig().emit();
60 }
61 }
62
63 ThemeManager &m_tm;
64 };
65
Theme(int screen_num)66 Theme::Theme(int screen_num):m_screen_num(screen_num) {
67 ThemeManager::instance().registerTheme(*this);
68 }
69
~Theme()70 Theme::~Theme() {
71 ThemeManager::instance().unregisterTheme(*this);
72 }
73
instance()74 ThemeManager &ThemeManager::instance() {
75 static ThemeManager tm;
76 return tm;
77 }
78
ThemeManager()79 ThemeManager::ThemeManager():
80 // max_screens: we initialize this later so we can set m_verbose
81 // without having a display connection
82 m_max_screens(-1),
83 m_verbose(false),
84 m_themelocation("") {
85
86 }
87
registerTheme(Theme & tm)88 bool ThemeManager::registerTheme(Theme &tm) {
89 if (m_max_screens < 0) {
90 m_max_screens = ScreenCount(FbTk::App::instance()->display());
91 m_themes.resize(m_max_screens);
92 }
93
94 // valid screen num?
95 if (m_max_screens < tm.screenNum() || tm.screenNum() < 0)
96 return false;
97
98 ThemeList::const_iterator it = m_themes[tm.screenNum()].begin(),
99 it_end = m_themes[tm.screenNum()].end();
100 if (std::find(it, it_end, &tm) == it_end) {
101 m_themes[tm.screenNum()].push_back(&tm);
102 return true;
103 }
104 return false;
105 }
106
unregisterTheme(Theme & tm)107 bool ThemeManager::unregisterTheme(Theme &tm) {
108 if (m_max_screens < tm.screenNum() || tm.screenNum() < 0)
109 return false;
110
111 m_themes[tm.screenNum()].remove(&tm);
112
113 return true;
114 }
115
load(const string & filename,const string & overlay_filename,int screen_num)116 bool ThemeManager::load(const string &filename,
117 const string &overlay_filename, int screen_num) {
118
119 string location = FbTk::StringUtil::expandFilename(filename);
120 StringUtil::removeTrailingWhitespace(location);
121 StringUtil::removeFirstWhitespace(location);
122 string prefix = "";
123
124 if (FileUtil::isDirectory(location.c_str())) {
125 prefix = location;
126
127 location.append("/theme.cfg");
128 if (!FileUtil::isRegularFile(location.c_str())) {
129 location = prefix;
130 location.append("/style.cfg");
131 if (!FileUtil::isRegularFile(location.c_str())) {
132 cerr<<"Error loading theme file "<<location<<": not a regular file"<<endl;
133 return false;
134 }
135 }
136 } else {
137 // dirname
138 prefix = location.substr(0, location.find_last_of('/'));
139 }
140
141 if (!m_database.load(location.c_str()))
142 return false;
143
144
145 if (!overlay_filename.empty()) {
146 string overlay_location = FbTk::StringUtil::expandFilename(overlay_filename);
147 if (FileUtil::isRegularFile(overlay_location.c_str())) {
148 XrmDatabaseHelper overlay_db;
149 if (overlay_db.load(overlay_location.c_str())) {
150 // after a merge the src_db is destroyed
151 // so, make sure XrmDatabaseHelper::m_database == 0
152 XrmMergeDatabases(*overlay_db, &(*m_database));
153 *overlay_db = 0;
154 }
155 }
156 }
157
158 // relies on the fact that load_rc clears search paths each time
159 if (m_themelocation != "") {
160 Image::removeSearchPath(m_themelocation);
161 m_themelocation.append("/pixmaps");
162 Image::removeSearchPath(m_themelocation);
163 }
164
165 m_themelocation = prefix;
166
167 location = prefix;
168 Image::addSearchPath(location);
169 location.append("/pixmaps");
170 Image::addSearchPath(location);
171
172 LoadThemeHelper load_theme_helper;
173
174 // get list and go throu all the resources and load them
175 // and then reconfigure them
176 if (screen_num < 0 || screen_num > m_max_screens) {
177 STLUtil::forAll(m_themes, load_theme_helper);
178 } else {
179 load_theme_helper(m_themes[screen_num]);
180 }
181
182 return true;
183 }
184
loadTheme(Theme & tm)185 void ThemeManager::loadTheme(Theme &tm) {
186 Theme::ItemList::iterator i = tm.itemList().begin();
187 Theme::ItemList::iterator i_end = tm.itemList().end();
188 for (; i != i_end; ++i) {
189 ThemeItem_base *resource = *i;
190 if (!loadItem(*resource)) {
191 // try fallback resource in theme
192 if (!tm.fallback(*resource)) {
193 if (verbose()) {
194 _FB_USES_NLS;
195 cerr<<_FBTK_CONSOLETEXT(Error, ThemeItem, "Failed to read theme item", "When reading a style, couldn't read a specific item (following)")<<": "<<resource->name()<<endl;
196 }
197 resource->setDefaultValue();
198 }
199 }
200 }
201 // send reconfiguration signal to theme and listeners
202 }
203
loadItem(ThemeItem_base & resource)204 bool ThemeManager::loadItem(ThemeItem_base &resource) {
205 return loadItem(resource, resource.name(), resource.altName());
206 }
207
208 /// handles resource item loading with specific name/altname
loadItem(ThemeItem_base & resource,const string & name,const string & alt_name)209 bool ThemeManager::loadItem(ThemeItem_base &resource, const string &name, const string &alt_name) {
210 XrmValue value;
211 char *value_type;
212 if (XrmGetResource(*m_database, name.c_str(),
213 alt_name.c_str(), &value_type, &value)) {
214 resource.setFromString(value.addr);
215 resource.load(&name, &alt_name); // load additional stuff by the ThemeItem
216 } else
217 return false;
218
219 return true;
220 }
221
resourceValue(const string & name,const string & altname)222 string ThemeManager::resourceValue(const string &name, const string &altname) {
223 XrmValue value;
224 char *value_type;
225 if (*m_database != 0 && XrmGetResource(*m_database, name.c_str(),
226 altname.c_str(), &value_type, &value) && value.addr != 0)
227 return string(value.addr);
228
229 return "";
230 }
231
232 /*
233 void ThemeManager::listItems() {
234 ThemeList::iterator it = m_themelist.begin();
235 ThemeList::iterator it_end = m_themelist.end();
236 for (; it != it_end; ++it) {
237 list<ThemeItem_base *>::iterator item = (*it)->itemList().begin();
238 list<ThemeItem_base *>::iterator item_end = (*it)->itemList().end();
239 for (; item != item_end; ++item) {
240
241 if (typeid(**item) == typeid(ThemeItem<Texture>)) {
242 cerr<<(*item)->name()<<": <texture type>"<<endl;
243 cerr<<(*item)->name()<<".pixmap: <filename>"<<endl;
244 cerr<<(*item)->name()<<".color: <color>"<<endl;
245 cerr<<(*item)->name()<<".colorTo: <color>"<<endl;
246 } else if (typeid(**item) == typeid(ThemeItem<Color>)) {
247 cerr<<(*item)->name()<<": <color>"<<endl;
248 } else if (typeid(**item) == typeid(ThemeItem<int>)) {
249 cerr<<(*item)->name()<<": <integer>"<<endl;
250 } else if (typeid(**item) == typeid(ThemeItem<bool>)) {
251 cerr<<(*item)->name()<<": <boolean>"<<endl;
252 } else if (typeid(**item) == typeid(ThemeItem<PixmapWithMask>)) {
253 cerr<<(*item)->name()<<": <filename>"<<endl;
254 } else if (typeid(**item) == typeid(ThemeItem<string>)) {
255 cerr<<(*item)->name()<<": <string>"<<endl;
256 } else if (typeid(**item) == typeid(ThemeItem<Font>)) {
257 cerr<<(*item)->name()<<": <font>"<<endl;
258 } else {
259 cerr<<(*item)->name()<<":"<<endl;
260 }
261 }
262 }
263
264 }
265 */
266 } // end namespace FbTk
267