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