new skin token: %cx - 24 hour time format enabled in the setting.. e.g %?cx<24 hour...
[kugel-rb.git] / apps / plugins / theme_remove.c
bloba2e7bf21a5b4a5c27dd986955b6d11c9fd5ec10f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "plugin.h"
21 #include "lib/configfile.h"
23 PLUGIN_HEADER
25 /* taken from apps/gui/wps_parser.c */
26 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
27 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
29 #define CONFIG_FILENAME "theme_remove.cfg"
30 #define LOG_FILENAME "/theme_remove_log.txt"
31 #define RB_FONTS_CONFIG VIEWERS_DIR "/rockbox-fonts.config"
33 enum remove_option {
34 ALWAYS_REMOVE,
35 NEVER_REMOVE,
36 REMOVE_IF_NOT_USED,
37 ASK_FOR_REMOVAL,
38 NUM_REMOVE_OPTION
41 struct remove_setting {
42 const char *name;
43 const char *prefix, *suffix;
44 char value[MAX_PATH];
45 int option;
46 int (*func)(struct remove_setting *);
47 bool used;
50 static int remove_wps(struct remove_setting *);
51 #ifdef HAVE_LCD_BITMAP
52 static int remove_icons(struct remove_setting *setting);
53 #endif
55 enum remove_settings {
56 #ifdef HAVE_LCD_BITMAP
57 REMOVE_FONT,
58 #endif
59 REMOVE_WPS,
60 #ifdef HAVE_REMOTE_LCD
61 REMOVE_RWPS,
62 #endif
63 #if LCD_DEPTH > 1
64 REMOVE_BACKDROP,
65 #endif
66 #ifdef HAVE_LCD_BITMAP
67 REMOVE_ICON,
68 REMOVE_VICON,
69 #endif
70 #ifdef HAVE_REMOTE_LCD
71 REMOVE_RICON,
72 REMOVE_RVICON,
73 #endif
74 #ifdef HAVE_LCD_COLOR
75 REMOVE_COLOURS,
76 #endif
77 NUM_REMOVE_ITEMS
80 static bool create_log = true;
81 static struct remove_setting remove_list[NUM_REMOVE_ITEMS] = {
82 #ifdef HAVE_LCD_BITMAP
83 [REMOVE_FONT] = { "font", FONT_DIR "/", ".fnt", "",
84 ASK_FOR_REMOVAL, NULL, false },
85 #endif
86 [REMOVE_WPS] = { "wps", WPS_DIR "/", ".wps", "",
87 REMOVE_IF_NOT_USED, remove_wps, false },
88 #ifdef HAVE_REMOTE_LCD
89 [REMOVE_RWPS] = { "rwps", WPS_DIR "/", ".rwps", "",
90 REMOVE_IF_NOT_USED, remove_wps, false },
91 #endif
92 #if LCD_DEPTH > 1
93 [REMOVE_BACKDROP] = { "backdrop", BACKDROP_DIR "/", ".bmp", "",
94 REMOVE_IF_NOT_USED, NULL, false },
95 #endif
96 #ifdef HAVE_LCD_BITMAP
97 [REMOVE_ICON] = { "iconset", ICON_DIR "/", ".bmp", "",
98 ASK_FOR_REMOVAL, NULL, false },
99 [REMOVE_VICON] = { "viewers iconset", ICON_DIR "/", ".bmp", "",
100 ASK_FOR_REMOVAL, remove_icons, false },
101 #endif
102 #ifdef HAVE_REMOTE_LCD
103 [REMOVE_RICON] = { "remote iconset", ICON_DIR "/", ".bmp", "",
104 ASK_FOR_REMOVAL, NULL, false },
105 [REMOVE_RVICON] = { "remote viewers iconset", ICON_DIR "/", ".bmp", "",
106 ASK_FOR_REMOVAL, NULL, false },
107 #endif
108 #ifdef HAVE_LCD_COLOR
109 [REMOVE_COLOURS] = { "filetype colours", THEME_DIR "/", ".colours", "",
110 ASK_FOR_REMOVAL, NULL, false },
111 #endif
113 static char *option_names[NUM_REMOVE_OPTION] = {
114 "always", "never", "not used", "ask",
116 static struct configdata config[] = {
117 #ifdef HAVE_LCD_BITMAP
118 { TYPE_INT, 0, NUM_REMOVE_OPTION,
119 { .int_p = &remove_list[REMOVE_FONT].option },
120 "remove font", option_names },
121 #endif
122 { TYPE_INT, 0, NUM_REMOVE_OPTION,
123 { .int_p = &remove_list[REMOVE_WPS].option },
124 "remove wps", option_names },
125 #ifdef HAVE_REMOTE_LCD
126 { TYPE_INT, 0, NUM_REMOVE_OPTION,
127 { .int_p = &remove_list[REMOVE_RWPS].option },
128 "remove rwps", option_names },
129 #endif
130 #if LCD_DEPTH > 1
131 { TYPE_INT, 0, NUM_REMOVE_OPTION,
132 { .int_p = &remove_list[REMOVE_BACKDROP].option },
133 "remove backdrop", option_names },
134 #endif
135 #ifdef HAVE_LCD_BITMAP
136 { TYPE_INT, 0, NUM_REMOVE_OPTION,
137 { .int_p = &remove_list[REMOVE_ICON].option },
138 "remove iconset", option_names },
139 { TYPE_INT, 0, NUM_REMOVE_OPTION,
140 { .int_p = &remove_list[REMOVE_VICON].option },
141 "remove viconset", option_names },
142 #endif
143 #ifdef HAVE_REMOTE_LCD
144 { TYPE_INT, 0, NUM_REMOVE_OPTION,
145 { .int_p = &remove_list[REMOVE_RICON].option },
146 "remove riconset", option_names },
147 { TYPE_INT, 0, NUM_REMOVE_OPTION,
148 { .int_p = &remove_list[REMOVE_RVICON].option },
149 "remove rviconset", option_names },
150 #endif
151 #ifdef HAVE_LCD_COLOR
152 { TYPE_INT, 0, NUM_REMOVE_OPTION,
153 { .int_p = &remove_list[REMOVE_COLOURS].option },
154 "remove colours", option_names },
155 #endif
156 {TYPE_BOOL, 0, 1, { .bool_p = &create_log },
157 "create log", NULL},
159 static const int nb_config = sizeof(config)/sizeof(*config);
160 static char themefile[MAX_PATH];
161 static int log_fd = -1;
163 static int show_mess(const char *text, const char *file)
165 static char buf[MAX_PATH*2];
167 if (file)
168 rb->snprintf(buf, sizeof(buf), "%s: %s", text, file);
169 else
170 rb->snprintf(buf, sizeof(buf), "%s", text);
172 DEBUGF("%s\n", buf);
173 if (log_fd >= 0)
174 rb->fdprintf(log_fd, "%s\n", buf);
176 rb->splash(0, buf);
177 rb->sleep(HZ/4);
179 return 0;
182 /* set full path of file. */
183 static void set_file_name(char *buf, const char*file,
184 struct remove_setting *setting)
186 int len1, len2;
187 if (rb->strncasecmp(file, setting->prefix, rb->strlen(setting->prefix)))
188 rb->snprintf(buf, MAX_PATH, "%s%s", setting->prefix, file);
189 else
190 rb->strlcpy(buf, file, MAX_PATH);
191 len1 = rb->strlen(buf);
192 len2 = rb->strlen(setting->suffix);
193 if (rb->strcasecmp(buf+len1-len2, setting->suffix))
194 rb->strlcpy(&buf[len1], setting->suffix, MAX_PATH-len1);
197 /* taken from apps/onplay.c */
198 /* helper function to remove a non-empty directory */
199 static int remove_dir(char* dirname, int len)
201 int result = 0;
202 DIR* dir;
203 int dirlen = rb->strlen(dirname);
205 dir = rb->opendir(dirname);
206 if (!dir)
207 return -1; /* open error */
209 while (true)
211 struct dirent* entry;
212 /* walk through the directory content */
213 entry = rb->readdir(dir);
214 if (!entry)
215 break;
217 dirname[dirlen] ='\0';
219 /* append name to current directory */
220 rb->snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name);
221 if (entry->attribute & ATTR_DIRECTORY)
223 /* remove a subdirectory */
224 if (!rb->strcmp((char *)entry->d_name, ".") ||
225 !rb->strcmp((char *)entry->d_name, ".."))
226 continue; /* skip these */
228 result = remove_dir(dirname, len); /* recursion */
229 if (result)
230 break;
232 else
234 /* remove a file */
235 result = rb->remove(dirname);
237 if (ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK))
239 show_mess("Canceled", NULL);
240 result = -1;
241 break;
244 rb->closedir(dir);
246 if (!result)
247 { /* remove the now empty directory */
248 dirname[dirlen] = '\0'; /* terminate to original length */
250 result = rb->rmdir(dirname);
251 show_mess("Removed", dirname);
254 return result;
257 static int remove_wps(struct remove_setting *setting)
259 char bmpdir[MAX_PATH];
260 char *p;
261 rb->strcpy(bmpdir, setting->value);
262 p = rb->strrchr(bmpdir, '.');
263 if (p) *p = 0;
264 if (!rb->dir_exists(bmpdir))
265 return 0;
266 return remove_dir(bmpdir, MAX_PATH);
269 #ifdef HAVE_LCD_BITMAP
270 static int remove_icons(struct remove_setting *setting)
272 char path[MAX_PATH];
273 char *p;
274 rb->strcpy(path, setting->value);
275 p = rb->strrchr(path, '.');
276 rb->strlcpy(p, ".icons", path+MAX_PATH-p);
278 if (!rb->file_exists(path))
280 return 0;
282 if (rb->remove(path))
284 show_mess("Failed", path);
285 return 1;
287 show_mess("Removed", path);
288 return 0;
290 #endif
292 #ifdef HAVE_LCD_BITMAP
293 static char font_file[MAX_PATH];
294 #endif
295 static bool is_deny_file(const char *file)
297 const char *deny_files[] = {
298 WPS_DEFAULTCFG,
299 RWPS_DEFAULTCFG,
300 #ifdef HAVE_LCD_BITMAP
301 font_file,
302 #endif
303 NULL
305 const char **p = deny_files;
306 while ( *p )
308 if (!rb->strcmp(file, *p))
309 return true;
310 p++;
312 return false;
315 static void check_whether_used_in_setting(void)
317 const char *setting_files[] = {
318 #ifdef HAVE_LCD_BITMAP
319 rb->global_settings->font_file,
320 #endif
321 rb->global_settings->wps_file,
322 #ifdef HAVE_REMOTE_LCD
323 rb->global_settings->rwps_file,
324 #endif
325 #if LCD_DEPTH > 1
326 rb->global_settings->backdrop_file,
327 #endif
328 #ifdef HAVE_LCD_BITMAP
329 rb->global_settings->icon_file,
330 rb->global_settings->viewers_icon_file,
331 #endif
332 #ifdef HAVE_REMOTE_LCD
333 rb->global_settings->remote_icon_file,
334 rb->global_settings->remote_viewers_icon_file,
335 #endif
336 #ifdef HAVE_LCD_COLOR
337 rb->global_settings->colors_file,
338 #endif
340 char tempfile[MAX_PATH];
341 int i;
342 for (i=0; i<NUM_REMOVE_ITEMS; i++)
344 struct remove_setting *setting = &remove_list[i];
345 if (setting->value[0])
347 set_file_name(tempfile, setting_files[i], setting);
348 if (!rb->strcasecmp(tempfile, setting->value))
349 setting->used = true;
353 static void check_whether_used_in_file(const char *cfgfile)
355 char line[MAX_PATH];
356 char settingfile[MAX_PATH];
357 char *p;
358 int fd;
359 char *name, *value;
360 int i;
362 if (!rb->strcasecmp(themefile, cfgfile))
363 return;
364 fd = rb->open(cfgfile, O_RDONLY);
365 if (fd < 0)
366 return;
367 while (rb->read_line(fd, line, sizeof(line)) > 0)
369 if (!rb->settings_parseline(line, &name, &value))
370 continue;
371 /* remove trailing spaces. */
372 p = value+rb->strlen(value)-1;
373 while (*p == ' ') *p-- = 0;
374 if (*value == 0 || !rb->strcmp(value, "-"))
375 continue;
376 for (i=0; i<NUM_REMOVE_ITEMS; i++)
378 struct remove_setting *setting = &remove_list[i];
379 if (!rb->strcmp(name, setting->name))
381 if (setting->value[0])
383 set_file_name(settingfile, value, setting);
384 if (!rb->strcasecmp(settingfile, setting->value))
385 setting->used = true;
387 break;
391 rb->close(fd);
393 static void check_whether_used(void)
395 char cfgfile[MAX_PATH];
396 DIR *dir;
398 check_whether_used_in_setting();
399 #ifdef HAVE_LCD_BITMAP
400 /* mark font files come from rockbox-font.zip as used and don't remove
401 * them automatically as themes may depend on those fonts. */
402 if (remove_list[REMOVE_FONT].option == REMOVE_IF_NOT_USED)
403 check_whether_used_in_file(RB_FONTS_CONFIG);
404 #endif
406 dir = rb->opendir(THEME_DIR);
407 if (!dir)
408 return; /* open error */
410 while (true)
412 struct dirent* entry;
413 char *p;
414 int i;
415 /* walk through the directory content */
416 entry = rb->readdir(dir);
417 if (!entry)
418 break;
419 p = rb->strrchr(entry->d_name, '.');
420 if (!p || rb->strcmp(p, ".cfg"))
421 continue;
423 rb->snprintf(cfgfile, MAX_PATH, "%s/%s", THEME_DIR, entry->d_name);
424 check_whether_used_in_file(cfgfile);
425 /* break the loop if all files need to be checked in the theme
426 * turned out to be used. */
427 for (i = 0; i < NUM_REMOVE_ITEMS; i++)
429 struct remove_setting *setting = &remove_list[i];
430 if (setting->option == REMOVE_IF_NOT_USED)
432 if (setting->value[0] && !setting->used)
433 break;
436 if (i == NUM_REMOVE_ITEMS)
437 break;
439 rb->closedir(dir);
442 static int remove_file(struct remove_setting *setting)
444 if (!rb->file_exists(setting->value))
446 show_mess("Doesn't exist", setting->value);
447 return 0;
449 if (is_deny_file(setting->value))
451 show_mess("Denied", setting->value);
452 return 0;
454 switch (setting->option)
456 case ALWAYS_REMOVE:
457 break;
458 case NEVER_REMOVE:
459 show_mess("Skipped", setting->value);
460 return 0;
461 break;
462 case REMOVE_IF_NOT_USED:
463 if (setting->used)
465 show_mess("Used", setting->value);
466 return 0;
468 break;
469 case ASK_FOR_REMOVAL:
470 default:
472 const char *message_lines[] = { "Delete?", setting->value };
473 const struct text_message text_message = { message_lines, 2 };
474 if (rb->gui_syncyesno_run(&text_message, NULL, NULL) != YESNO_YES)
476 show_mess("Skipped", setting->value);
477 return 0;
480 break;
482 if (rb->remove(setting->value))
484 show_mess("Failed", setting->value);
485 return -1;
487 if (setting->func && setting->func(setting))
488 return -1;
489 show_mess("Removed", setting->value);
490 return 1;
492 static int remove_theme(void)
494 static char line[MAX_PATH];
495 int fd;
496 int i, num_removed = 0;
497 char *name, *value;
498 bool needs_to_check_whether_used = false;
500 /* initialize for safe */
501 for (i=0; i<NUM_REMOVE_ITEMS; i++)
502 remove_list[i].value[0] = 0;
504 /* load settings */
505 fd = rb->open(themefile, O_RDONLY);
506 if (fd < 0) return fd;
507 while (rb->read_line(fd, line, sizeof(line)) > 0)
509 if (!rb->settings_parseline(line, &name, &value))
510 continue;
511 /* remove trailing spaces. */
512 char *p = value+rb->strlen(value)-1;
513 while (*p == ' ') *p-- = 0;
514 if (*value == 0 || !rb->strcmp(value, "-"))
515 continue;
516 for (i=0; i<NUM_REMOVE_ITEMS; i++)
518 struct remove_setting *setting = &remove_list[i];
519 if (!rb->strcmp(name, setting->name))
521 set_file_name(setting->value, value, setting);
522 if(setting->option == REMOVE_IF_NOT_USED)
523 needs_to_check_whether_used = true;
524 break;
528 rb->close(fd);
530 if(needs_to_check_whether_used)
531 check_whether_used();
533 /* now remove file assosiated to the theme. */
534 for (i=0; i<NUM_REMOVE_ITEMS; i++)
536 if (remove_list[i].value[0])
538 int ret = remove_file(&remove_list[i]);
539 if (ret < 0)
540 return ret;
541 num_removed += ret;
545 /* remove the setting file iff it is in theme directory to protect
546 * aginst accidental removal of non theme cfg file. if the file is
547 * not in the theme directory, the file may not be a theme cfg file. */
548 if (rb->strncasecmp(themefile, THEME_DIR "/", sizeof(THEME_DIR "/")-1))
550 show_mess("Skipped", themefile);
552 else if (rb->remove(themefile))
554 show_mess("Failed", themefile);
555 return -1;
557 else
559 show_mess("Removed", themefile);
560 rb->reload_directory();
561 num_removed++;
563 return num_removed;
566 static bool option_changed = false;
567 static bool option_menu(void)
569 MENUITEM_STRINGLIST(option_menu, "Remove Options", NULL,
570 /* same order as remove_list */
571 #ifdef HAVE_LCD_BITMAP
572 "Font",
573 #endif
574 "WPS",
575 #ifdef HAVE_REMOTE_LCD
576 "Remote WPS",
577 #endif
578 #if LCD_DEPTH > 1
579 "Backdrop",
580 #endif
581 #ifdef HAVE_LCD_BITMAP
582 "Iconset", "Viewers Iconset",
583 #endif
584 #ifdef HAVE_REMOTE_LCD
585 "Remote Iconset", "Remote Viewers Iconset",
586 #endif
587 #ifdef HAVE_LCD_COLOR
588 "Filetype Colours",
589 #endif
590 "Create Log File");
591 struct opt_items remove_names[] = {
592 {"Always Remove", -1}, {"Never Remove", -1},
593 {"Remove if not Used", -1}, {"Ask for Removal", -1},
595 int selected = 0, result;
597 while (1)
599 result = rb->do_menu(&option_menu, &selected, NULL, false);
600 if (result >= 0 && result < NUM_REMOVE_ITEMS)
602 struct remove_setting *setting = &remove_list[result];
603 int prev_option = setting->option;
604 if (rb->set_option(option_menu_[result], &setting->option, INT,
605 remove_names, NUM_REMOVE_OPTION, NULL))
606 return true;
607 if (prev_option != setting->option)
608 option_changed = true;
610 else if (result == NUM_REMOVE_ITEMS)
612 bool prev_value = create_log;
613 if(rb->set_bool("Create Log File", &create_log))
614 return true;
615 if (prev_value != create_log)
616 option_changed = true;
618 else if (result == MENU_ATTACHED_USB)
619 return true;
620 else
621 return false;
624 return false;
627 enum plugin_status plugin_start(const void* parameter)
629 static char title[64];
630 char *p;
631 MENUITEM_STRINGLIST(menu, title, NULL,
632 "Remove Theme", "Remove Options",
633 "Quit");
634 int selected = 0, ret;
635 bool exit = false;
637 if (!parameter)
638 return PLUGIN_ERROR;
640 rb->snprintf(title, sizeof(title), "Remove %s",
641 rb->strrchr(parameter, '/')+1);
642 if((p = rb->strrchr(title, '.')))
643 *p = 0;
645 #ifdef HAVE_LCD_BITMAP
646 rb->snprintf(font_file, MAX_PATH, FONT_DIR "/%s.fnt",
647 rb->global_settings->font_file);
648 #endif
649 rb->strlcpy(themefile, parameter, MAX_PATH);
650 if (!rb->file_exists(themefile))
652 rb->splash(HZ, "File open error!");
653 return PLUGIN_ERROR;
655 configfile_load(CONFIG_FILENAME, config, nb_config, 0);
656 while (!exit)
658 switch (rb->do_menu(&menu, &selected, NULL, false))
660 case 0:
661 if(create_log)
663 log_fd = rb->open(LOG_FILENAME, O_WRONLY|O_CREAT|O_APPEND);
664 if(log_fd >= 0)
665 rb->fdprintf(log_fd, "---- %s ----\n", title);
666 else
667 show_mess("Couldn't open log file.", NULL);
669 ret = remove_theme();
670 p = (ret >= 0? "Successfully removed!": "Remove failure");
671 show_mess(p, NULL);
672 if(log_fd >= 0)
674 rb->fdprintf(log_fd, "----------------\n");
675 rb->close(log_fd);
676 log_fd = -1;
678 rb->lcd_clear_display();
679 rb->lcd_update();
680 rb->splashf(0, "%s %s", p, "Press any key to exit.");
681 rb->button_clear_queue();
682 rb->button_get(true);
683 exit = true;
684 break;
685 case 1:
686 if (option_menu())
687 return PLUGIN_USB_CONNECTED;
688 break;
689 case 2:
690 exit = true;
691 break;
692 case MENU_ATTACHED_USB:
693 return PLUGIN_USB_CONNECTED;
694 break;
695 default:
696 break;
699 if(option_changed)
700 configfile_save(CONFIG_FILENAME, config, nb_config, 0);
701 return PLUGIN_OK;