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