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 // $Id: Theme.cc 4199 2006-02-16 06:53:05Z mathias $
23
24 #include "Theme.hh"
25
26 #include "XrmDatabaseHelper.hh"
27 #include "App.hh"
28 #include "StringUtil.hh"
29 #include "FileUtil.hh"
30 #include "Image.hh"
31
32 #ifdef HAVE_CSTDIO
33 #include <cstdio>
34 #else
35 #include <stdio.h>
36 #endif
37 #include <algorithm>
38 #include <memory>
39 #include <iostream>
40
41 using namespace std;
42
43 namespace FbTk {
44
45 struct LoadThemeHelper {
LoadThemeHelperFbTk::LoadThemeHelper46 LoadThemeHelper():m_tm(ThemeManager::instance()) {}
operator ()FbTk::LoadThemeHelper47 void operator ()(Theme *tm) {
48 m_tm.loadTheme(*tm);
49 }
operator ()FbTk::LoadThemeHelper50 void operator ()(ThemeManager::ThemeList &tmlist) {
51
52 for_each(tmlist.begin(), tmlist.end(),
53 *this);
54 // send reconfiguration signal to theme and listeners
55 ThemeManager::ThemeList::iterator it = tmlist.begin();
56 ThemeManager::ThemeList::iterator it_end = tmlist.end();
57 for (; it != it_end; ++it) {
58 (*it)->reconfigTheme();
59 (*it)->reconfigSig().notify();
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 // TODO: use find and return false if it's already there
98 // instead of unique
99
100 m_themes[tm.screenNum()].push_back(&tm);
101 m_themes[tm.screenNum()].unique();
102 return true;
103 }
104
unregisterTheme(Theme & tm)105 bool ThemeManager::unregisterTheme(Theme &tm) {
106 if (m_max_screens < tm.screenNum() || tm.screenNum() < 0)
107 return false;
108
109 m_themes[tm.screenNum()].remove(&tm);
110
111 return true;
112 }
113
load(const std::string & filename,const std::string & overlay_filename,int screen_num)114 bool ThemeManager::load(const std::string &filename,
115 const std::string &overlay_filename, int screen_num) {
116 std::string location = FbTk::StringUtil::expandFilename(filename);
117 std::string prefix = "";
118
119 if (FileUtil::isDirectory(filename.c_str())) {
120 prefix = location;
121
122 location.append("/theme.cfg");
123 if (!FileUtil::isRegularFile(location.c_str())) {
124 location = prefix;
125 location.append("/style.cfg");
126 if (!FileUtil::isRegularFile(location.c_str())) {
127 cerr<<"Error loading theme file "<<location<<": not a regular file"<<endl;
128 return false;
129 }
130 }
131 } else {
132 // dirname
133 prefix = location.substr(0, location.find_last_of('/'));
134 }
135
136 if (!m_database.load(location.c_str()))
137 return false;
138
139
140 if (!overlay_filename.empty()) {
141 std::string overlay_location = FbTk::StringUtil::expandFilename(overlay_filename);
142 if (FileUtil::isRegularFile(overlay_location.c_str())) {
143 XrmDatabaseHelper overlay_db;
144 if (overlay_db.load(overlay_location.c_str())) {
145 // after a merge the src_db is destroyed
146 // so, make sure XrmDatabaseHelper::m_database == 0
147 XrmMergeDatabases(*overlay_db, &(*m_database));
148 *overlay_db = 0;
149 }
150 }
151 }
152
153 // relies on the fact that load_rc clears search paths each time
154 if (m_themelocation != "") {
155 Image::removeSearchPath(m_themelocation);
156 m_themelocation.append("/pixmaps");
157 Image::removeSearchPath(m_themelocation);
158 }
159
160 m_themelocation = prefix;
161
162 location = prefix;
163 Image::addSearchPath(location);
164 location.append("/pixmaps");
165 Image::addSearchPath(location);
166
167 LoadThemeHelper load_theme_helper;
168
169 // get list and go throu all the resources and load them
170 // and then reconfigure them
171 if (screen_num < 0 || screen_num > m_max_screens) {
172 for_each(m_themes.begin(),
173 m_themes.end(),
174 load_theme_helper);
175 } else {
176 load_theme_helper(m_themes[screen_num]);
177 }
178
179 return true;
180 }
181
loadTheme(Theme & tm)182 void ThemeManager::loadTheme(Theme &tm) {
183 Theme::ItemList::iterator i = tm.itemList().begin();
184 Theme::ItemList::iterator i_end = tm.itemList().end();
185 for (; i != i_end; ++i) {
186 ThemeItem_base *resource = *i;
187 if (!loadItem(*resource)) {
188 // try fallback resource in theme
189 if (!tm.fallback(*resource)) {
190 if (verbose()) {
191 cerr<<"Failed to read theme item: "<<resource->name()<<endl;
192 }
193 resource->setDefaultValue();
194 }
195 }
196 }
197 // send reconfiguration signal to theme and listeners
198 }
199
loadItem(ThemeItem_base & resource)200 bool ThemeManager::loadItem(ThemeItem_base &resource) {
201 return loadItem(resource, resource.name(), resource.altName());
202 }
203
204 /// handles resource item loading with specific name/altname
loadItem(ThemeItem_base & resource,const std::string & name,const std::string & alt_name)205 bool ThemeManager::loadItem(ThemeItem_base &resource, const std::string &name, const std::string &alt_name) {
206 XrmValue value;
207 char *value_type;
208 if (XrmGetResource(*m_database, name.c_str(),
209 alt_name.c_str(), &value_type, &value)) {
210 resource.setFromString(value.addr);
211 resource.load(&name, &alt_name); // load additional stuff by the ThemeItem
212 } else
213 return false;
214
215 return true;
216 }
217
resourceValue(const std::string & name,const std::string & altname)218 std::string ThemeManager::resourceValue(const std::string &name, const std::string &altname) {
219 XrmValue value;
220 char *value_type;
221 if (*m_database != 0 && XrmGetResource(*m_database, name.c_str(),
222 altname.c_str(), &value_type, &value) && value.addr != 0)
223 return string(value.addr);
224
225 return "";
226 }
227
228 /*
229 void ThemeManager::listItems() {
230 ThemeList::iterator it = m_themelist.begin();
231 ThemeList::iterator it_end = m_themelist.end();
232 for (; it != it_end; ++it) {
233 std::list<ThemeItem_base *>::iterator item = (*it)->itemList().begin();
234 std::list<ThemeItem_base *>::iterator item_end = (*it)->itemList().end();
235 for (; item != item_end; ++item) {
236
237 if (typeid(**item) == typeid(ThemeItem<Texture>)) {
238 cerr<<(*item)->name()<<": <texture type>"<<endl;
239 cerr<<(*item)->name()<<".pixmap: <filename>"<<endl;
240 cerr<<(*item)->name()<<".color: <color>"<<endl;
241 cerr<<(*item)->name()<<".colorTo: <color>"<<endl;
242 } else if (typeid(**item) == typeid(ThemeItem<Color>)) {
243 cerr<<(*item)->name()<<": <color>"<<endl;
244 } else if (typeid(**item) == typeid(ThemeItem<int>)) {
245 cerr<<(*item)->name()<<": <integer>"<<endl;
246 } else if (typeid(**item) == typeid(ThemeItem<bool>)) {
247 cerr<<(*item)->name()<<": <boolean>"<<endl;
248 } else if (typeid(**item) == typeid(ThemeItem<PixmapWithMask>)) {
249 cerr<<(*item)->name()<<": <filename>"<<endl;
250 } else if (typeid(**item) == typeid(ThemeItem<std::string>)) {
251 cerr<<(*item)->name()<<": <string>"<<endl;
252 } else if (typeid(**item) == typeid(ThemeItem<Font>)) {
253 cerr<<(*item)->name()<<": <font>"<<endl;
254 } else {
255 cerr<<(*item)->name()<<":"<<endl;
256 }
257 }
258 }
259
260 }
261 */
262 }; // end namespace FbTk
263