1 /*
2 
3 This file is from Nitrogen, an X11 background setter.
4 Copyright (C) 2006  Dave Foster & Javeed Shaikh
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 
20 */
21 
22 #include "Config.h"
23 #include <glib/gstdio.h>
24 #include "gcs-i18n.h"
25 #include "Util.h"
26 
27 /**
28  * Constructor, initializes default settings.
29  */
Config()30 Config::Config()
31 {
32 	recurse = true;
33 
34     m_display_mode = ICON;
35 
36     m_posx = -1;
37     m_posy = -1;
38 
39     m_sizex = 450;
40     m_sizey = 500;
41 
42     m_icon_captions = false;
43 
44     m_sort_mode = Thumbview::SORT_ALPHA;
45 }
46 
47 /**
48  * Singleton retreiver.  NOT THREAD SAFE.  Call this func early to create, then it's ok.
49  *
50  * @return	A pointer to the one config instance.
51  */
get_instance()52 Config* Config::get_instance()
53 {
54 	// instance
55 	static Config* _instance;
56 
57 	if (!_instance)
58 		_instance = new Config();
59 
60 	return _instance;
61 }
62 
63 /**
64  * Creates a clone of this Config instance. Used so the preferences dialog may manipulate the
65  * configuration without disturbing the real one.
66  */
clone()67 Config* Config::clone()
68 {
69     Config* retVal = new Config();
70 
71     retVal->recurse = recurse;
72     retVal->m_display_mode = m_display_mode;
73     retVal->m_posx = m_posx;
74     retVal->m_posy = m_posy;
75     retVal->m_sizex = m_sizex;
76     retVal->m_sizey = m_sizey;
77     retVal->m_icon_captions = m_icon_captions;
78     retVal->m_vec_dirs = VecStrs(m_vec_dirs);
79     retVal->m_sort_mode = m_sort_mode;
80 
81     return retVal;
82 }
83 
84 /**
85  * Returns the full path to the config file to be used.
86  *
87  * The file will exist.
88  *
89  * @return  An empty string, or a full path to a config file.
90  */
get_file(const Glib::ustring filename) const91 std::string Config::get_file(const Glib::ustring filename) const
92 {
93     VecStrs config_paths = Glib::get_system_config_dirs();
94     config_paths.insert(config_paths.begin(), Glib::get_user_config_dir());
95 
96     std::string cfgfile;
97 
98     for (VecStrs::const_iterator i = config_paths.begin(); i != config_paths.end(); i++) {
99         cfgfile = Glib::build_filename(*i, "nitrogen", filename);
100         if (Glib::file_test(cfgfile, Glib::FILE_TEST_EXISTS)) {
101             return cfgfile;
102         }
103     }
104 
105     return "";
106 }
107 
108 /**
109  * Returns the bg information for a previously stored one
110  *
111  * @param	disp	The display string to use
112  * @param	file	Returns the file thats in there
113  * @param	mode	Returns the mode thats in there
114  * @param	bgcolor	Returns the bg color thats in there, if any, or a default Gdk::Color if none
115  * @return			If it could find the entry
116  */
get_bg(const Glib::ustring disp,Glib::ustring & file,SetBG::SetMode & mode,Gdk::Color & bgcolor)117 bool Config::get_bg(const Glib::ustring disp, Glib::ustring &file, SetBG::SetMode &mode, Gdk::Color &bgcolor) {
118 
119     Glib::ustring cfgfile = get_bg_config_file();
120     if (cfgfile.empty())
121         return false;
122 
123 	GKeyFile *kf = g_key_file_new();
124 
125 	GError *ge = NULL;
126 	if ( g_key_file_load_from_file(kf, cfgfile.c_str(), G_KEY_FILE_NONE, &ge) == FALSE ) {
127 
128 		std::cerr << _("ERROR: Unable to load config file") << " (" << cfgfile << "): " << ge->message << "\n";
129 		g_clear_error(&ge);
130 		g_key_file_free(kf);
131 		return false;
132 	}
133 
134 	// check if the group for this display exists
135 	if ( ! g_key_file_has_group(kf, disp.c_str()) ) {
136 		// there is no config entry for this display.
137 		// abort.
138 		g_key_file_free(kf);
139 		std::cerr << _("Couldn't find group for") << " " << disp << "." << std::endl;
140 		return false;
141 	}
142 
143 	// get data
144 
145 	gchar *fileptr = g_key_file_get_string(kf, disp.c_str(), "file", NULL);
146 	if ( fileptr ) {
147 		file = Glib::ustring(fileptr);
148 		free(fileptr);
149 	} else {
150 		g_key_file_free(kf);
151 		std::cerr << _("Could not get filename from config file.") << std::endl;
152 		return false;
153 	}
154 		//error = true;
155 
156 	mode = (SetBG::SetMode)g_key_file_get_integer(kf, disp.c_str(), "mode", &ge);
157 	if (ge) {
158 		g_clear_error(&ge);
159 		g_key_file_free(kf);
160 		std::cerr << _("Could not get mode from config file.") << std::endl;
161 		return false;
162 	}
163 
164 	Glib::ustring tcol("#000000");
165 	gchar *color = g_key_file_get_string(kf, disp.c_str(), "bgcolor", &ge);
166 	if (ge) {
167 		g_clear_error(&ge);
168 		// This is not a critical error.
169 		//g_key_file_free(kf);
170 		//std::cerr << "Could not get bg color from config file." << std::endl;
171 		//return false;
172 	} else {
173 		// worked properly
174 		tcol = color;
175 		free(color);
176 	}
177 
178 	// did not fail
179 	// Glib::ustring tcol(color);
180 	// free(color);
181 	bgcolor.set(tcol);
182 
183 	g_key_file_free(kf);
184 
185 
186 	// this function looks like less of an eyesore without all the
187 	// failed trix.
188 
189 	return true; // success
190 }
191 
192 /**
193  * Sets the specified bg in the config file.
194  *
195  * @param	disp	The display this one is for
196  * @param	file	The bg file to set
197  * @param	mode	The mode the bg is set in
198  * @param	bgcolor	The background color, ignored depending on mode
199  * @return			Success
200  */
set_bg(const Glib::ustring disp,const Glib::ustring file,const SetBG::SetMode mode,const Gdk::Color bgcolor)201 bool Config::set_bg(const Glib::ustring disp, const Glib::ustring file, const SetBG::SetMode mode, const Gdk::Color bgcolor) {
202 
203 	if ( ! Config::check_dir() )
204 		return false;
205 
206 	Glib::ustring realdisp = disp;
207 	Glib::ustring cfgfile = Glib::build_filename(Glib::ustring(g_get_user_config_dir()), Glib::ustring("nitrogen"));
208 	cfgfile = Glib::build_filename(cfgfile, Glib::ustring("bg-saved.cfg"));
209 
210 	GKeyFile *kf = g_key_file_new();
211 
212 	GError *ge = NULL;
213 	if ( g_key_file_load_from_file(kf, cfgfile.c_str(), G_KEY_FILE_NONE, &ge) == FALSE ) {
214 
215 		// only give an error if it doesn't exist
216 		if ( ! ( ge->code == G_FILE_ERROR_NOENT || ge->code == G_KEY_FILE_ERROR_NOT_FOUND ) ) {
217 			std::cerr << _("ERROR: Unable to load config file") << " (" << cfgfile << "): " << ge->message << "\n";
218 			#ifdef DEBUG
219 			std::cerr << _("The error code returned was") << " " << ge->code << "\n";
220 			std::cerr << _("We expected") << " " << G_FILE_ERROR_NOENT << " " << _("or") << " " << G_KEY_FILE_ERROR_NOT_FOUND << "\n";
221 			#endif
222 			g_key_file_free(kf);
223 			g_clear_error(&ge);
224 			return false;
225 		}
226 		g_clear_error(&ge);
227 	}
228 
229 	// must do complex removals if xinerama occurs
230 	if (realdisp.find("xin_", 0, 4) != Glib::ustring::npos) {
231 
232 		if (realdisp == "xin_-1") {
233 			// get all keys, remove all keys that exist with xin_ prefixes
234 			gchar **groups;
235 			gsize num;
236 			groups = g_key_file_get_groups(kf, &num);
237 			for (int i=0; i<num; i++)
238 				if (Glib::ustring(groups[i]).find("xin_", 0, 4) != Glib::ustring::npos)
239 					g_key_file_remove_group(kf, groups[i], NULL);
240 
241 		} else {
242 
243 			// a normal xinerama screen, therefore
244 			// remove the big realdisp if it occurs
245 			if (g_key_file_has_group(kf, "xin_-1"))
246 				g_key_file_remove_group(kf, "xin_-1", NULL);
247 		}
248 	}
249 
250 
251 	// set data
252 	g_key_file_set_string(kf, realdisp.c_str(), "file", file.c_str());
253 	g_key_file_set_integer(kf, realdisp.c_str(), "mode", (gint)mode);
254 	g_key_file_set_string(kf, realdisp.c_str(), "bgcolor", Util::color_to_string(bgcolor).c_str());
255 
256 	// output it
257 	Glib::ustring outp = g_key_file_to_data(kf, NULL, NULL);
258 	std::ofstream outf(cfgfile.c_str());
259 	outf << outp;
260 	outf.close();
261 
262 	g_key_file_free(kf);
263 
264 	return true;
265 }
266 
267 /**
268  * Grabs the list of set groups in the bg config file.
269  *
270  * @param	groups	A vector of strings, each corresponding to a group name
271  * @return			If it was able to open and do everything ok
272  */
get_bg_groups(std::vector<Glib::ustring> & groups)273 bool Config::get_bg_groups(std::vector<Glib::ustring> &groups) {
274 
275 	Glib::ustring cfgfile = get_bg_config_file();
276     if (cfgfile.empty())
277         return false;
278 
279 	GKeyFile *kf = g_key_file_new();
280 
281 	if ( g_key_file_load_from_file(kf, cfgfile.c_str(), G_KEY_FILE_NONE, NULL) == FALSE ) {
282 
283 		// do not output anything here, we just want to know
284 		g_key_file_free(kf);
285 		return false;
286 	}
287 
288 	std::vector<Glib::ustring> res;
289 	gsize num;
290 	gchar** filegroups = g_key_file_get_groups(kf, &num);
291 
292 	// drop into vector
293 	for (unsigned int i=0; i<num; i++) {
294 		res.push_back(Glib::ustring(filegroups[i]));
295 	}
296 
297 	g_strfreev(filegroups);
298 	groups = res;
299 
300 	g_key_file_free(kf);
301 	return true;
302 }
303 
304 /**
305  * Checks to make sure the config dir exists.
306  *
307  * @return	If it exists, or it was able to make it
308  */
check_dir()309 bool Config::check_dir() {
310 
311 	Glib::ustring dirname = Glib::build_filename(Glib::ustring(g_get_user_config_dir()), "nitrogen");
312 	if ( !Glib::file_test(dirname, Glib::FILE_TEST_EXISTS ) )
313 		if ( g_mkdir_with_parents(dirname.c_str(), 0755) == -1 )
314 			// TODO: throw
315 			return false;
316 
317 	return true;
318 }
319 
320 /**
321  * Gets the last saved position.
322  */
get_pos(gint & x,gint & y)323 void Config::get_pos(gint &x, gint &y)
324 {
325     x = m_posx;
326     y = m_posy;
327 }
328 
329 /**
330  * Sets the position, to be saved with save_cfg.
331  */
set_pos(gint x,gint y)332 void Config::set_pos(gint x, gint y)
333 {
334     m_posx = x;
335     m_posy = y;
336 }
337 
338 /**
339  * Gets the last saved window size.
340  */
get_size(guint & w,guint & h)341 void Config::get_size(guint &w, guint &h)
342 {
343     w = m_sizex;
344     h = m_sizey;
345 }
346 
347 /**
348  * Sets the window size, to be saved with save_cfg.
349  */
set_size(guint w,guint h)350 void Config::set_size(guint w, guint h)
351 {
352     m_sizex = w;
353     m_sizey = h;
354 }
355 
356 /**
357  * Saves common configuration params to a configuration file.
358  *
359  * Stored in ~/.config/nitrogen/nitrogen.cfg
360  */
save_cfg()361 bool Config::save_cfg()
362 {
363 	if ( ! Config::check_dir() )
364 		return false;
365 
366 	Glib::ustring cfgfile = Glib::build_filename(Glib::ustring(g_get_user_config_dir()), Glib::ustring("nitrogen"));
367 	cfgfile = Glib::build_filename(cfgfile, Glib::ustring("nitrogen.cfg"));
368 
369     Glib::KeyFile kf;
370 
371     kf.set_integer("geometry", "posx", m_posx);
372     kf.set_integer("geometry", "posy", m_posy);
373     kf.set_integer("geometry", "sizex", m_sizex);
374     kf.set_integer("geometry", "sizey", m_sizey);
375 
376     if (m_display_mode == ICON)
377         kf.set_string("nitrogen", "view",  Glib::ustring("icon"));
378     else if (m_display_mode == LIST)
379         kf.set_string("nitrogen", "view",  Glib::ustring("list"));
380 
381     kf.set_boolean("nitrogen", "recurse", recurse);
382 
383     if (m_sort_mode == Thumbview::SORT_ALPHA)
384         kf.set_string("nitrogen", "sort", Glib::ustring("alpha"));
385     else if (m_sort_mode == Thumbview::SORT_RALPHA)
386         kf.set_string("nitrogen", "sort", Glib::ustring("ralpha"));
387     else if (m_sort_mode == Thumbview::SORT_TIME)
388         kf.set_string("nitrogen", "sort", Glib::ustring("time"));
389     else
390         kf.set_string("nitrogen", "sort", Glib::ustring("rtime"));
391 
392     kf.set_boolean("nitrogen", "icon_caps", m_icon_captions);
393 
394     kf.set_string_list("nitrogen", "dirs", m_vec_dirs);
395 
396     if (g_file_set_contents(cfgfile.c_str(), kf.to_data().c_str(), -1, NULL) == FALSE)
397         return false;
398 
399     return true;
400 }
401 
402 /**
403  * Loads common configuration params from the configuration file.
404  */
load_cfg()405 bool Config::load_cfg()
406 {
407 	Glib::ustring cfgfile = get_config_file();
408     if (cfgfile.empty())
409         return false;
410 
411     // TODO: use load_from_data_dirs?
412     Glib::KeyFile kf;
413     if (!kf.load_from_file(cfgfile))
414         return false;
415 
416     if (kf.has_key("geometry", "posx"))     m_posx = kf.get_integer("geometry", "posx");
417     if (kf.has_key("geometry", "posy"))     m_posy = kf.get_integer("geometry", "posy");
418     if (kf.has_key("geometry", "sizex"))    m_sizex = kf.get_integer("geometry", "sizex");
419     if (kf.has_key("geometry", "sizey"))    m_sizey = kf.get_integer("geometry", "sizey");
420 
421     if (kf.has_key("nitrogen", "view"))
422     {
423         Glib::ustring mode = kf.get_string("nitrogen", "view");
424         if (mode == Glib::ustring("icon"))
425             m_display_mode = ICON;
426         else if (mode == Glib::ustring("list"))
427             m_display_mode = LIST;
428     }
429 
430     if (kf.has_key("nitrogen", "dirs"))         m_vec_dirs = kf.get_string_list("nitrogen", "dirs");
431     if (kf.has_key("nitrogen", "icon_caps"))    m_icon_captions = kf.get_boolean("nitrogen", "icon_caps");
432     if (kf.has_key("nitrogen", "recurse"))      recurse = kf.get_boolean("nitrogen", "recurse");
433 
434     if (kf.has_key("nitrogen", "sort"))
435     {
436         Glib::ustring sort_mode = kf.get_string("nitrogen", "sort");
437         if (sort_mode == Glib::ustring("alpha"))
438             m_sort_mode = Thumbview::SORT_ALPHA;
439         else if (sort_mode == Glib::ustring("ralpha"))
440             m_sort_mode = Thumbview::SORT_RALPHA;
441         else if (sort_mode == Glib::ustring("time"))
442             m_sort_mode = Thumbview::SORT_TIME;
443         else if (sort_mode == Glib::ustring("rtime"))
444             m_sort_mode = Thumbview::SORT_RTIME;
445     }
446 
447     return true;
448 }
449 
get_dirs()450 VecStrs Config::get_dirs()
451 {
452     return m_vec_dirs;
453 }
454 
set_dirs(const VecStrs & new_dirs)455 void Config::set_dirs(const VecStrs& new_dirs)
456 {
457     m_vec_dirs = new_dirs;
458 }
459 
460 /**
461  * Adds a directory to the list of directories.
462  *
463  * Does not add if the passed dir already is in the list of dirs.
464  * Returns a boolean to indicate if this call resulted in an add.
465  * If it returns false, it didn't add a new one becuase it already
466  * existed.
467  */
add_dir(const std::string & dir)468 bool Config::add_dir(const std::string& dir)
469 {
470     VecStrs::iterator i = std::find(m_vec_dirs.begin(), m_vec_dirs.end(), dir);
471     if (i == m_vec_dirs.end())
472     {
473         m_vec_dirs.push_back(std::string(dir));
474         return true;
475     }
476 
477     return false;
478 }
479 
480 /**
481  * Removes the given directory from the list of directories.
482  *
483  * Returns true if it was able to find and remove it. Otherwise, returns
484  * false.
485  */
rm_dir(const std::string & dir)486 bool Config::rm_dir(const std::string& dir)
487 {
488     VecStrs::iterator i = std::find(m_vec_dirs.begin(), m_vec_dirs.end(), dir);
489     if (i != m_vec_dirs.end())
490     {
491         m_vec_dirs.erase(i);
492         return true;
493     }
494 
495     return false;
496 }
497 
498 
499