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