prefs_themes.c 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052
  1. /*
  2. * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
  3. * Copyright (C) 2003-2006 Hiroyuki Yamamoto & the Claws Mail team
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. # include <config.h>
  21. #endif
  22. #include "defs.h"
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <sys/stat.h>
  27. #include <glib.h>
  28. #include <glib/gi18n.h>
  29. #include <gtk/gtk.h>
  30. #include "utils.h"
  31. #include "codeconv.h"
  32. #include "prefs_common.h"
  33. #include "prefs_gtk.h"
  34. #include "gtk/gtkutils.h"
  35. #include "gtk/prefswindow.h"
  36. #include "gtk/filesel.h"
  37. #include "stock_pixmap.h"
  38. #include "mainwindow.h"
  39. #include "compose.h"
  40. #include "alertpanel.h"
  41. #define IS_CURRENT_THEME(path) (strcmp(prefs_common.pixmap_theme_path, path) == 0)
  42. #define IS_INTERNAL_THEME(path) (strcmp(DEFAULT_PIXMAP_THEME, path) == 0)
  43. #define IS_SYSTEM_THEME(path) (prefs_themes_is_system_theme(path))
  44. #define PREVIEW_ICONS 7
  45. typedef struct _ThemesPage
  46. {
  47. PrefsPage page;
  48. GtkWidget *window; /* do not modify */
  49. GtkWidget *op_menu;
  50. GtkWidget *btn_install;
  51. GtkWidget *btn_more;
  52. GtkWidget *global;
  53. GtkWidget *name;
  54. GtkWidget *author;
  55. GtkWidget *url;
  56. GtkWidget *status;
  57. GtkWidget *icons[PREVIEW_ICONS];
  58. GtkWidget *btn_use;
  59. GtkWidget *btn_remove;
  60. GdkPixmap *pixmaps[PREVIEW_ICONS];
  61. GdkBitmap *masks[PREVIEW_ICONS];
  62. /* gchar *theme_path; */
  63. } ThemesPage;
  64. typedef struct _ThemeInfo
  65. {
  66. gchar *name;
  67. gchar *author;
  68. gchar *url;
  69. gchar *status;
  70. } ThemeInfo;
  71. typedef struct _ThemeName
  72. {
  73. gchar *name;
  74. GList *item;
  75. } ThemeName;
  76. typedef struct _ThemesData
  77. {
  78. GList *themes;
  79. GList *names;
  80. gchar *displayed;
  81. ThemesPage *page;
  82. } ThemesData;
  83. typedef void (*FileFunc) (const gchar *filename, gpointer data);
  84. typedef struct _DirInfo {
  85. gint bytes;
  86. gint files;
  87. gint pixms;
  88. } DirInfo;
  89. typedef struct _CopyInfo {
  90. gchar *dest;
  91. gchar *status;
  92. } CopyInfo;
  93. static ThemesData *prefs_themes_data;
  94. StockPixmap prefs_themes_icons[PREVIEW_ICONS] = {
  95. STOCK_PIXMAP_DIR_CLOSE,
  96. STOCK_PIXMAP_MAIL_SEND,
  97. STOCK_PIXMAP_MAIL_RECEIVE,
  98. STOCK_PIXMAP_MAIL_ATTACH,
  99. STOCK_PIXMAP_BOOK,
  100. STOCK_PIXMAP_MIME_TEXT_PLAIN,
  101. STOCK_PIXMAP_REPLIED
  102. };
  103. static void prefs_themes_btn_use_clicked_cb (GtkWidget *widget, gpointer data);
  104. static void prefs_themes_btn_remove_clicked_cb (GtkWidget *widget, gpointer data);
  105. static void prefs_themes_btn_install_clicked_cb (GtkWidget *widget, gpointer data);
  106. static void prefs_themes_menu_item_activated_cb (GtkWidget *widget, gpointer data);
  107. static void prefs_themes_update_buttons (const ThemesData *tdata);
  108. static void prefs_themes_display_global_stats (const ThemesData *tdata);
  109. static void prefs_themes_get_theme_info (ThemesData *tdata);
  110. static void prefs_themes_display_theme_info (ThemesData *tdata, const ThemeInfo *info);
  111. static void prefs_themes_get_themes_and_names (ThemesData *tdata);
  112. static int prefs_themes_cmp_name(gconstpointer a, gconstpointer b);
  113. static void prefs_themes_free_names (ThemesData *tdata);
  114. static void prefs_themes_set_themes_menu (GtkOptionMenu *opmenu, const ThemesData *tdata);
  115. static gchar *prefs_themes_get_theme_stats (const gchar *dirname);
  116. static gboolean prefs_themes_is_system_theme (const gchar *dirname);
  117. static void prefs_themes_create_widget (PrefsPage *page, GtkWindow *window, gpointer data);
  118. static void prefs_themes_destroy_widget (PrefsPage *page);
  119. static void prefs_themes_save (PrefsPage *page);
  120. static void prefs_themes_foreach_file (const gchar *dirname, const FileFunc func, gpointer data);
  121. static void prefs_themes_file_stats (const gchar *filename, gpointer data);
  122. static void prefs_themes_file_remove (const gchar *filename, gpointer data);
  123. static void prefs_themes_file_install (const gchar *filename, gpointer data);
  124. static void prefs_themes_file_stats(const gchar *filename, gpointer data)
  125. {
  126. struct stat s;
  127. DirInfo *di = (DirInfo *)data;
  128. gint len;
  129. if (0 == stat(filename, &s) && 0 != S_ISREG(s.st_mode)) {
  130. di->bytes += s.st_size;
  131. di->files++;
  132. len = strlen(filename);
  133. if (len > 4) {
  134. if (filename[len - 1] == 'm' && filename[len - 2] == 'p' &&
  135. filename[len - 3] == 'x' && filename[len - 4] == '.')
  136. di->pixms++;
  137. }
  138. }
  139. }
  140. static void prefs_themes_file_remove(const gchar *filename, gpointer data)
  141. {
  142. gchar **status = (gchar **)data;
  143. gchar *base;
  144. if ((*status) != NULL)
  145. return;
  146. base = g_path_get_basename(filename);
  147. if (TRUE == is_dir_exist(filename)) {
  148. if (strcmp(base, ".") != 0 && strcmp(base, "..") != 0)
  149. g_warning("prefs_themes_file_remove(): subdir in theme dir skipped: '%s'.\n",
  150. base);
  151. }
  152. else if (0 != g_unlink(filename)) {
  153. (*status) = g_strdup(filename);
  154. }
  155. g_free(base);
  156. }
  157. static void prefs_themes_file_install(const gchar *filename, gpointer data)
  158. {
  159. CopyInfo *ci = (CopyInfo *)data;
  160. gchar *base;
  161. if (ci->status != NULL)
  162. return;
  163. base = g_path_get_basename(filename);
  164. if (TRUE == is_dir_exist(filename)) {
  165. if (strcmp(base, ".") != 0 && strcmp(base, "..") !=0 )
  166. g_warning("prefs_themes_file_install(): subdir in theme dir skipped: '%s'.\n",
  167. base);
  168. }
  169. else {
  170. gchar *fulldest;
  171. fulldest = g_strconcat(ci->dest, G_DIR_SEPARATOR_S, base, NULL);
  172. if (0 != copy_file(filename, fulldest, FALSE)) {
  173. ci->status = g_strdup(filename);
  174. }
  175. g_free(fulldest);
  176. }
  177. g_free(base);
  178. }
  179. static void prefs_themes_foreach_file(const gchar *dirname, const FileFunc func, gpointer data)
  180. {
  181. struct dirent *d;
  182. DIR *dp;
  183. g_return_if_fail(dirname != NULL);
  184. g_return_if_fail(func != NULL);
  185. if ((dp = opendir(dirname)) == NULL) {
  186. debug_print("directory %s not found", dirname);
  187. return;
  188. }
  189. while ((d = readdir(dp)) != NULL) {
  190. gchar *entry;
  191. gchar *fullentry;
  192. entry = d->d_name;
  193. fullentry = g_strconcat(dirname, G_DIR_SEPARATOR_S, entry, NULL);
  194. (*func)(fullentry, data);
  195. g_free(fullentry);
  196. }
  197. }
  198. static gboolean prefs_themes_is_system_theme(const gchar *dirname)
  199. {
  200. gint len;
  201. g_return_val_if_fail(dirname != NULL, FALSE);
  202. len = strlen(PACKAGE_DATA_DIR);
  203. if (strlen(dirname) > len && 0 == strncmp(dirname, PACKAGE_DATA_DIR, len))
  204. return TRUE;
  205. return FALSE;
  206. }
  207. static void prefs_themes_set_themes_menu(GtkOptionMenu *opmenu, const ThemesData *tdata)
  208. {
  209. GList *themes = tdata->names;
  210. GtkWidget *menu;
  211. GtkWidget *item;
  212. gint i = 0, active = 0;
  213. GList *sorted_list = NULL;
  214. g_return_if_fail(opmenu != NULL);
  215. gtk_option_menu_remove_menu(opmenu);
  216. /* sort theme data list by data name */
  217. menu = gtk_menu_new ();
  218. while (themes != NULL) {
  219. ThemeName *tname = (ThemeName *)(themes->data);
  220. sorted_list = g_list_insert_sorted(sorted_list, (gpointer)(tname),
  221. (GCompareFunc)prefs_themes_cmp_name);
  222. themes = g_list_next(themes);
  223. }
  224. /* feed gtk_menu w/ sorted themes names */
  225. themes = sorted_list;
  226. while (themes != NULL) {
  227. ThemeName *tname = (ThemeName *)(themes->data);
  228. gchar *tpath = (gchar *)(tname->item->data);
  229. item = gtk_menu_item_new_with_label(tname->name);
  230. gtk_widget_show(item);
  231. g_signal_connect(G_OBJECT(item), "activate",
  232. G_CALLBACK(prefs_themes_menu_item_activated_cb),
  233. tname->item->data);
  234. gtk_menu_append(GTK_MENU(menu), item);
  235. if (tdata->displayed != NULL && tdata->displayed == tpath)
  236. active = i;
  237. ++i;
  238. themes = g_list_next(themes);
  239. }
  240. g_list_free(sorted_list);
  241. gtk_menu_set_active(GTK_MENU(menu), active);
  242. gtk_option_menu_set_menu (opmenu, menu);
  243. }
  244. static int prefs_themes_cmp_name(gconstpointer a_p, gconstpointer b_p)
  245. {
  246. /* compare two ThemeData structures by their name attribute */
  247. return strcmp2((gchar *)(((ThemeName*)a_p)->name),
  248. (gchar *)(((ThemeName*)b_p)->name));
  249. }
  250. static void prefs_themes_get_themes_and_names(ThemesData *tdata)
  251. {
  252. GList *tpaths;
  253. g_return_if_fail(tdata != NULL);
  254. if (tdata->themes != NULL)
  255. stock_pixmap_themes_list_free(tdata->themes);
  256. if (tdata->names != NULL)
  257. prefs_themes_free_names(tdata);
  258. tdata->themes = stock_pixmap_themes_list_new();
  259. tpaths = tdata->themes;
  260. while (tpaths != NULL) {
  261. ThemeName *name = g_new0(ThemeName, 1);
  262. gchar *sname = g_path_get_basename((const gchar *)(tpaths->data));
  263. if (IS_INTERNAL_THEME(sname))
  264. name->name = g_strdup(_("Default internal theme"));
  265. else
  266. name->name = g_strdup(sname);
  267. name->item = tpaths;
  268. tdata->names = g_list_append(tdata->names, name);
  269. if (!strcmp2(tpaths->data, prefs_common.pixmap_theme_path)) {
  270. tdata->displayed = (gchar *)tpaths->data;
  271. }
  272. tpaths = g_list_next(tpaths);
  273. g_free(sname);
  274. }
  275. }
  276. void prefs_themes_init(void)
  277. {
  278. ThemesData *tdata;
  279. ThemesPage *page;
  280. GList *tpaths;
  281. static gchar *path[3];
  282. path[0] = _("Display");
  283. path[1] = _("Themes");
  284. path[2] = NULL;
  285. debug_print("Creating preferences for themes...\n");
  286. tdata = g_new0(ThemesData, 1);
  287. prefs_themes_data = tdata;
  288. prefs_themes_get_themes_and_names(tdata);
  289. page = g_new0(ThemesPage, 1);
  290. page->page.path = path;
  291. page->page.create_widget = prefs_themes_create_widget;
  292. page->page.destroy_widget = prefs_themes_destroy_widget;
  293. page->page.save_page = prefs_themes_save;
  294. page->page.weight = 130.0;
  295. prefs_gtk_register_page((PrefsPage *) page);
  296. tdata->page = page;
  297. tpaths = g_list_first(tdata->themes);
  298. if (tdata->displayed == NULL)
  299. tdata->displayed = (gchar *)(tpaths->data);
  300. }
  301. static void prefs_themes_free_names(ThemesData *tdata)
  302. {
  303. GList *names;
  304. names = tdata->names;
  305. while (names != NULL) {
  306. ThemeName *tn = (ThemeName *)(names->data);
  307. tn->item = NULL;
  308. g_free(tn->name);
  309. g_free(tn);
  310. names = g_list_next(names);
  311. }
  312. g_list_free(names);
  313. tdata->names = NULL;
  314. }
  315. void prefs_themes_done(void)
  316. {
  317. ThemesData *tdata = prefs_themes_data;
  318. debug_print("Finished preferences for themes.\n");
  319. stock_pixmap_themes_list_free(tdata->themes);
  320. prefs_themes_free_names(tdata);
  321. g_free(tdata->page);
  322. g_free(tdata);
  323. }
  324. static void prefs_themes_btn_use_clicked_cb(GtkWidget *widget, gpointer data)
  325. {
  326. ThemesData *tdata = prefs_themes_data;
  327. gchar *theme_str;
  328. theme_str = tdata->displayed;
  329. g_free(prefs_common.pixmap_theme_path);
  330. prefs_common.pixmap_theme_path = g_strdup(theme_str);
  331. main_window_reflect_prefs_all_real(TRUE);
  332. compose_reflect_prefs_pixmap_theme();
  333. prefs_themes_update_buttons(tdata);
  334. }
  335. static void prefs_themes_btn_remove_clicked_cb(GtkWidget *widget, gpointer data)
  336. {
  337. ThemesData *tdata = prefs_themes_data;
  338. gchar *theme_str;
  339. gchar *alert_title = NULL;
  340. AlertValue val = 0;
  341. gchar *tmp = NULL;
  342. theme_str = tdata->displayed;
  343. tmp = g_path_get_basename(theme_str);
  344. if (IS_SYSTEM_THEME(theme_str)) {
  345. if (getuid() != 0) {
  346. alertpanel_error(_("Only root can remove system themes"));
  347. return;
  348. }
  349. alert_title = g_strdup_printf(_("Remove system theme '%s'"), tmp);
  350. }
  351. if (NULL == alert_title) {
  352. alert_title = g_strdup_printf(_("Remove theme '%s'"), tmp);
  353. }
  354. g_free(tmp);
  355. val = alertpanel(alert_title,
  356. _("Are you sure you want to remove this theme?"),
  357. GTK_STOCK_NO, GTK_STOCK_YES, NULL);
  358. g_free(alert_title);
  359. if (G_ALERTALTERNATE == val) {
  360. gchar *status = NULL;
  361. prefs_themes_foreach_file(theme_str, prefs_themes_file_remove, &status);
  362. if (0 != rmdir(theme_str)) {
  363. if (status != NULL) {
  364. alertpanel_error(_("File %s failed\nwhile removing theme."), status);
  365. g_free(status);
  366. }
  367. else
  368. alertpanel_error(_("Removing theme directory failed."));
  369. }
  370. else {
  371. alertpanel_notice(_("Theme removed succesfully"));
  372. /* update interface back to first theme */
  373. prefs_themes_get_themes_and_names(tdata);
  374. prefs_themes_set_themes_menu(GTK_OPTION_MENU(tdata->page->op_menu), tdata);
  375. prefs_themes_display_global_stats(tdata);
  376. tdata->displayed = (gchar *)((g_list_first(tdata->themes))->data);
  377. prefs_themes_get_theme_info(tdata);
  378. }
  379. }
  380. }
  381. static void prefs_themes_btn_install_clicked_cb(GtkWidget *widget, gpointer data)
  382. {
  383. gchar *filename, *source;
  384. gchar *themeinfo, *themename;
  385. gchar *alert_title = NULL;
  386. CopyInfo *cinfo;
  387. AlertValue val = 0;
  388. ThemesData *tdata = prefs_themes_data;
  389. filename = filesel_select_file_open_folder(_("Select theme folder"), NULL);
  390. if (filename == NULL)
  391. return;
  392. if (filename[strlen(filename) - 1] != G_DIR_SEPARATOR)
  393. filename = g_strconcat(filename, G_DIR_SEPARATOR_S, NULL);
  394. else
  395. filename = g_strdup(filename);
  396. cinfo = g_new0(CopyInfo, 1);
  397. source = g_path_get_dirname(filename);
  398. themename = g_path_get_basename(source);
  399. debug_print("Installing '%s' theme from %s\n", themename, filename);
  400. themeinfo = g_strconcat(source, G_DIR_SEPARATOR_S, THEMEINFO_FILE, NULL);
  401. alert_title = g_strdup_printf(_("Install theme '%s'"), themename);
  402. if (file_exist(themeinfo, FALSE) == FALSE) {
  403. val = alertpanel(alert_title,
  404. _("This folder doesn't seem to be a theme folder.\nInstall anyway?"),
  405. GTK_STOCK_NO, GTK_STOCK_YES, NULL);
  406. if (G_ALERTALTERNATE != val)
  407. goto end_inst;
  408. }
  409. if (getuid() == 0) {
  410. val = alertpanel(alert_title,
  411. _("Do you want to install theme for all users?"),
  412. GTK_STOCK_NO, GTK_STOCK_YES, NULL);
  413. switch (val) {
  414. case G_ALERTALTERNATE:
  415. cinfo->dest = g_strconcat(PACKAGE_DATA_DIR, G_DIR_SEPARATOR_S,
  416. PIXMAP_THEME_DIR, G_DIR_SEPARATOR_S,
  417. themename, NULL);
  418. break;
  419. case G_ALERTDEFAULT:
  420. break;
  421. default:
  422. goto end_inst;
  423. }
  424. }
  425. g_free(alert_title);
  426. if (cinfo->dest == NULL) {
  427. cinfo->dest = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, RC_DIR,
  428. G_DIR_SEPARATOR_S, PIXMAP_THEME_DIR, G_DIR_SEPARATOR_S,
  429. themename, NULL);
  430. }
  431. if (TRUE == is_dir_exist(cinfo->dest)) {
  432. alertpanel_error(_("A theme with the same name is\nalready installed in this location"));
  433. goto end_inst;
  434. }
  435. if (0 != make_dir_hier(cinfo->dest)) {
  436. alertpanel_error(_("Couldn't create destination directory"));
  437. goto end_inst;
  438. }
  439. prefs_themes_foreach_file(source, prefs_themes_file_install, cinfo);
  440. if (cinfo->status == NULL) {
  441. GList *insted;
  442. /* update interface to show newly installed theme */
  443. prefs_themes_get_themes_and_names(tdata);
  444. insted = g_list_find_custom(tdata->themes,
  445. (gpointer)(cinfo->dest),
  446. (GCompareFunc)strcmp2);
  447. if (NULL != insted) {
  448. alertpanel_notice(_("Theme installed succesfully"));
  449. tdata->displayed = (gchar *)(insted->data);
  450. prefs_themes_set_themes_menu(GTK_OPTION_MENU(tdata->page->op_menu), tdata);
  451. prefs_themes_display_global_stats(tdata);
  452. prefs_themes_get_theme_info(tdata);
  453. }
  454. else
  455. alertpanel_error(_("Failed installing theme"));
  456. }
  457. else
  458. alertpanel_error(_("File %s failed\nwhile installing theme."), cinfo->status);
  459. end_inst:
  460. g_free(cinfo->dest);
  461. g_free(filename);
  462. g_free(source);
  463. g_free(themeinfo);
  464. g_free(cinfo);
  465. g_free(themename);
  466. }
  467. static void prefs_themes_menu_item_activated_cb(GtkWidget *widget, gpointer data)
  468. {
  469. ThemesData *tdata = prefs_themes_data;
  470. gchar *path = (gchar *)data;
  471. g_return_if_fail(path != NULL);
  472. tdata->displayed = path;
  473. prefs_themes_get_theme_info(tdata);
  474. }
  475. static void prefs_themes_update_buttons(const ThemesData *tdata)
  476. {
  477. ThemesPage *theme = tdata->page;
  478. gboolean can_rem, can_use;
  479. can_use = !IS_CURRENT_THEME(tdata->displayed);
  480. can_rem = can_use && !IS_INTERNAL_THEME(tdata->displayed);
  481. if (theme->btn_use != NULL)
  482. gtk_widget_set_sensitive(theme->btn_use, can_use);
  483. if (theme->btn_remove != NULL)
  484. gtk_widget_set_sensitive(theme->btn_remove, can_rem);
  485. }
  486. /* placeholders may already be utf8 (i18n) */
  487. #define SET_LABEL_TEXT_UTF8(label, text) \
  488. { \
  489. gchar *tmpstr; \
  490. \
  491. if (!g_utf8_validate(text, -1, NULL)) \
  492. tmpstr = conv_codeset_strdup(text, \
  493. conv_get_locale_charset_str(), CS_UTF_8); \
  494. else \
  495. tmpstr = g_strdup(text); \
  496. \
  497. gtk_label_set_text(GTK_LABEL(label), tmpstr); \
  498. gtk_label_set_selectable(GTK_LABEL(label), TRUE); \
  499. g_free(tmpstr); \
  500. }
  501. static void prefs_themes_display_theme_info(ThemesData *tdata, const ThemeInfo *info)
  502. {
  503. ThemesPage *theme = tdata->page;
  504. gchar *save_prefs_path;
  505. gint i;
  506. SET_LABEL_TEXT_UTF8(theme->name, info->name);
  507. SET_LABEL_TEXT_UTF8(theme->author, info->author);
  508. SET_LABEL_TEXT_UTF8(theme->url, info->url);
  509. SET_LABEL_TEXT_UTF8(theme->status, info->status);
  510. save_prefs_path = prefs_common.pixmap_theme_path;
  511. prefs_common.pixmap_theme_path = tdata->displayed;
  512. for (i = 0; i < PREVIEW_ICONS; ++i) {
  513. stock_pixmap_gdk(theme->window, prefs_themes_icons[i],
  514. &(theme->pixmaps[i]), &(theme->masks[i]));
  515. gtk_image_set_from_pixmap(GTK_IMAGE(theme->icons[i]),
  516. theme->pixmaps[i], theme->masks[i]);
  517. }
  518. prefs_common.pixmap_theme_path = save_prefs_path;
  519. prefs_themes_update_buttons(tdata);
  520. }
  521. #undef SET_LABEL_TEXT_UTF8
  522. static void prefs_themes_display_global_stats(const ThemesData *tdata)
  523. {
  524. ThemesPage *theme = tdata->page;
  525. GList *tnames = tdata->names;
  526. gchar *gstats;
  527. gint sys = 0;
  528. gint usr = 0;
  529. gint all = 0;
  530. while (tnames != NULL) {
  531. ThemeName *tname = (ThemeName *)(tnames->data);
  532. gchar *tpath = (gchar *)(tname->item->data);
  533. if (IS_SYSTEM_THEME(tpath))
  534. ++sys;
  535. else if (!IS_INTERNAL_THEME(tpath))
  536. ++usr;
  537. ++all;
  538. tnames = g_list_next(tnames);
  539. }
  540. gstats = g_strdup_printf(_("%d themes available (%d user, %d system, 1 internal)"),
  541. all, usr, sys);
  542. gtk_label_set_text(GTK_LABEL(theme->global), gstats);
  543. gtk_label_set_justify (GTK_LABEL (theme->global), GTK_JUSTIFY_LEFT);
  544. gtkut_widget_set_small_font_size (theme->global);
  545. g_free(gstats);
  546. }
  547. #define INFOFILE_LINE_LEN 80
  548. #define FGETS_INFOFILE_LINE() \
  549. line[0] = '\0'; \
  550. fgets(line, INFOFILE_LINE_LEN, finfo); \
  551. if ((len = strlen(line)) > 0) { \
  552. if (line[len - 1] == '\n') line[len - 1] = '\0'; \
  553. } \
  554. else { \
  555. strcpy(line, _("Unknown")); \
  556. }
  557. static void prefs_themes_get_theme_info(ThemesData *tdata)
  558. {
  559. FILE *finfo;
  560. gchar *sinfo;
  561. gchar *path;
  562. gchar line[INFOFILE_LINE_LEN];
  563. gint len;
  564. ThemeInfo *info;
  565. ThemesPage *theme = tdata->page;
  566. g_return_if_fail(theme != NULL);
  567. path = tdata->displayed;
  568. g_return_if_fail(path != NULL);
  569. debug_print("Getting theme info for %s\n", path);
  570. info = g_new0(ThemeInfo, 1);
  571. if (IS_INTERNAL_THEME(path)) {
  572. info->name = g_strdup(_("Default internal theme"));
  573. info->author = g_strdup(_("The Claws Mail Team"));
  574. info->url = g_strdup(HOMEPAGE_URI);
  575. info->status = g_strdup_printf(_("Internal theme has %d icons"), N_STOCK_PIXMAPS);
  576. }
  577. else {
  578. sinfo = g_strconcat(path, G_DIR_SEPARATOR_S, THEMEINFO_FILE, NULL);
  579. finfo = g_fopen(sinfo, "r");
  580. if (finfo == NULL) {
  581. info->name = g_strdup(_("No info file available for this theme"));
  582. info->author = g_strdup(_("Unknown"));
  583. info->url = g_strdup(_("Unknown"));
  584. }
  585. else {
  586. FGETS_INFOFILE_LINE()
  587. info->name = g_strdup(line);
  588. FGETS_INFOFILE_LINE()
  589. info->author = g_strdup(line);
  590. FGETS_INFOFILE_LINE()
  591. info->url = g_strdup(line);
  592. fclose(finfo);
  593. }
  594. g_free(sinfo);
  595. info->status = prefs_themes_get_theme_stats(path);
  596. if (info->status == NULL) {
  597. info->status = g_strdup(_("Error: couldn't get theme status"));
  598. }
  599. }
  600. prefs_themes_display_theme_info(tdata, info);
  601. g_free(info->name);
  602. g_free(info->author);
  603. g_free(info->url);
  604. g_free(info->status);
  605. g_free(info);
  606. }
  607. #undef FGETS_INFOFILE_LINE
  608. static gchar *prefs_themes_get_theme_stats(const gchar *dirname)
  609. {
  610. gchar *stats;
  611. DirInfo *dinfo;
  612. dinfo = g_new0(DirInfo, 1);
  613. prefs_themes_foreach_file(dirname, prefs_themes_file_stats, dinfo);
  614. stats = g_strdup_printf(_("%d files (%d icons), size: %s"),
  615. dinfo->files, dinfo->pixms, to_human_readable(dinfo->bytes));
  616. g_free(dinfo);
  617. return stats;
  618. }
  619. /* BEGIN GLADE CODE */
  620. /* This is a dummy pixmap we use when a pixmap can't be found. */
  621. static char *dummy_pixmap_xpm[] = {
  622. /* columns rows colors chars-per-pixel */
  623. "1 1 1 1",
  624. " c None",
  625. /* pixels */
  626. " "
  627. };
  628. /* This is an internally used function to create pixmaps. */
  629. static GtkWidget* create_dummy_pixmap(GtkWidget *widget)
  630. {
  631. GdkColormap *colormap;
  632. GdkPixmap *gdkpixmap;
  633. GdkBitmap *mask;
  634. GtkWidget *pixmap;
  635. colormap = gtk_widget_get_colormap (widget);
  636. gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask,
  637. NULL, dummy_pixmap_xpm);
  638. if (gdkpixmap == NULL)
  639. g_error ("Couldn't create replacement pixmap.");
  640. pixmap = gtk_image_new_from_pixmap(gdkpixmap, mask);
  641. g_object_unref (gdkpixmap);
  642. g_object_unref (mask);
  643. return pixmap;
  644. }
  645. /* END GLADE CODE */
  646. /* glade generates some calls to a create_pixmap support function
  647. * we don't really need. */
  648. #define create_pixmap(widget,filename) create_dummy_pixmap(widget)
  649. static void prefs_themes_create_widget(PrefsPage *page, GtkWindow *window, gpointer data)
  650. {
  651. ThemesPage *prefs_themes = (ThemesPage *)page;
  652. ThemesData *tdata = prefs_themes_data;
  653. GtkWidget *vbox1;
  654. GtkWidget *frame1;
  655. GtkWidget *vbox2;
  656. GtkWidget *hbox3;
  657. GtkWidget *menu_themes;
  658. GtkWidget *menu_themes_menu;
  659. GtkWidget *glade_menuitem;
  660. GtkWidget *btn_install;
  661. GtkWidget *btn_more;
  662. GtkWidget *label_global_status;
  663. GtkWidget *frame_info;
  664. GtkWidget *table1;
  665. GtkWidget *label1;
  666. GtkWidget *label2;
  667. GtkWidget *label3;
  668. GtkWidget *label_name;
  669. GtkWidget *label_author;
  670. GtkWidget *label_url;
  671. GtkWidget *label4;
  672. GtkWidget *label_status;
  673. GtkWidget *frame_preview;
  674. GtkWidget *hbox1;
  675. GtkWidget *icon_1;
  676. GtkWidget *icon_2;
  677. GtkWidget *icon_3;
  678. GtkWidget *icon_4;
  679. GtkWidget *icon_5;
  680. GtkWidget *icon_6;
  681. GtkWidget *icon_7;
  682. GtkWidget *frame_buttons;
  683. GtkWidget *hbuttonbox1;
  684. GtkWidget *btn_use;
  685. GtkWidget *btn_remove;
  686. vbox1 = gtk_vbox_new (FALSE, VSPACING);
  687. gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER);
  688. gtk_widget_show (vbox1);
  689. PACK_FRAME (vbox1, frame1, _("Selector"));
  690. vbox2 = gtk_vbox_new (FALSE, VSPACING);
  691. gtk_container_set_border_width (GTK_CONTAINER (vbox2), VBOX_BORDER);
  692. gtk_widget_show (vbox2);
  693. gtk_container_add (GTK_CONTAINER (frame1), vbox2);
  694. hbox3 = gtk_hbox_new (FALSE, 5);
  695. gtk_widget_show (hbox3);
  696. gtk_box_pack_start (GTK_BOX (vbox2), hbox3, FALSE, FALSE, 0);
  697. gtk_container_set_border_width (GTK_CONTAINER (hbox3), 5);
  698. menu_themes = gtk_option_menu_new ();
  699. gtk_widget_show (menu_themes);
  700. gtk_box_pack_start (GTK_BOX (hbox3), menu_themes, FALSE, FALSE, 0);
  701. menu_themes_menu = gtk_menu_new ();
  702. glade_menuitem = gtk_menu_item_new_with_label ("");
  703. gtk_widget_show (glade_menuitem);
  704. gtk_menu_append (GTK_MENU (menu_themes_menu), glade_menuitem);
  705. gtk_option_menu_set_menu (GTK_OPTION_MENU (menu_themes), menu_themes_menu);
  706. btn_install = gtk_button_new_with_label (_("Install new..."));
  707. gtk_widget_show (btn_install);
  708. gtk_box_pack_start (GTK_BOX (hbox3), btn_install, FALSE, FALSE, 0);
  709. GTK_WIDGET_SET_FLAGS (btn_install, GTK_CAN_DEFAULT);
  710. btn_more = gtkut_get_link_btn((GtkWidget *)window, THEMES_URI, _("Get more..."));
  711. gtk_widget_show (btn_more);
  712. gtk_box_pack_start (GTK_BOX (hbox3), btn_more, FALSE, FALSE, 0);
  713. label_global_status = gtk_label_new ("");
  714. gtk_widget_show (label_global_status);
  715. gtk_box_pack_start (GTK_BOX (vbox2), label_global_status, FALSE, FALSE, 0);
  716. gtk_label_set_justify (GTK_LABEL (label_global_status), GTK_JUSTIFY_LEFT);
  717. gtk_misc_set_alignment (GTK_MISC (label_global_status), 0, 0.5);
  718. gtk_misc_set_padding (GTK_MISC (label_global_status), 6, 0);
  719. PACK_FRAME(vbox1, frame_info, _("Information"));
  720. table1 = gtk_table_new (4, 2, FALSE);
  721. gtk_widget_show (table1);
  722. gtk_container_add (GTK_CONTAINER (frame_info), table1);
  723. label1 = gtk_label_new (_("Name: "));
  724. gtk_widget_show (label1);
  725. gtk_table_attach (GTK_TABLE (table1), label1, 0, 1, 0, 1,
  726. (GtkAttachOptions) (GTK_FILL),
  727. (GtkAttachOptions) (0), 8, 2);
  728. gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
  729. gtk_misc_set_alignment (GTK_MISC (label1), 0, 0.5);
  730. label2 = gtk_label_new (_("Author: "));
  731. gtk_widget_show (label2);
  732. gtk_table_attach (GTK_TABLE (table1), label2, 0, 1, 1, 2,
  733. (GtkAttachOptions) (GTK_FILL),
  734. (GtkAttachOptions) (0), 8, 2);
  735. gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
  736. gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5);
  737. label3 = gtk_label_new (_("URL:"));
  738. gtk_widget_show (label3);
  739. gtk_table_attach (GTK_TABLE (table1), label3, 0, 1, 2, 3,
  740. (GtkAttachOptions) (GTK_FILL),
  741. (GtkAttachOptions) (0), 8, 2);
  742. gtk_misc_set_alignment (GTK_MISC (label3), 0, 0.5);
  743. label_name = gtk_label_new ("");
  744. gtk_widget_show (label_name);
  745. gtk_table_attach (GTK_TABLE (table1), label_name, 1, 2, 0, 1,
  746. (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
  747. (GtkAttachOptions) (0), 0, 0);
  748. gtk_misc_set_alignment (GTK_MISC (label_name), 0, 0.5);
  749. label_author = gtk_label_new ("");
  750. gtk_widget_show (label_author);
  751. gtk_table_attach (GTK_TABLE (table1), label_author, 1, 2, 1, 2,
  752. (GtkAttachOptions) (GTK_FILL),
  753. (GtkAttachOptions) (0), 0, 0);
  754. gtk_misc_set_alignment (GTK_MISC (label_author), 0, 0.5);
  755. label_url = gtk_label_new ("");
  756. gtk_widget_show (label_url);
  757. gtk_table_attach (GTK_TABLE (table1), label_url, 1, 2, 2, 3,
  758. (GtkAttachOptions) (GTK_FILL),
  759. (GtkAttachOptions) (0), 0, 0);
  760. gtk_misc_set_alignment (GTK_MISC (label_url), 0, 0.5);
  761. label4 = gtk_label_new (_("Status:"));
  762. gtk_widget_show (label4);
  763. gtk_table_attach (GTK_TABLE (table1), label4, 0, 1, 3, 4,
  764. (GtkAttachOptions) (GTK_FILL),
  765. (GtkAttachOptions) (0), 8, 2);
  766. gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5);
  767. label_status = gtk_label_new ("");
  768. gtk_widget_show (label_status);
  769. gtk_table_attach (GTK_TABLE (table1), label_status, 1, 2, 3, 4,
  770. (GtkAttachOptions) (GTK_FILL),
  771. (GtkAttachOptions) (0), 0, 0);
  772. gtk_misc_set_alignment (GTK_MISC (label_status), 0, 0.5);
  773. PACK_FRAME(vbox1, frame_preview, _("Preview"));
  774. hbox1 = gtk_hbox_new (FALSE, 0);
  775. gtk_widget_show (hbox1);
  776. gtk_container_add (GTK_CONTAINER (frame_preview), hbox1);
  777. icon_1 = create_pixmap (vbox1, NULL);
  778. gtk_widget_show (icon_1);
  779. gtk_box_pack_start (GTK_BOX (hbox1), icon_1, TRUE, TRUE, 2);
  780. gtk_misc_set_padding (GTK_MISC (icon_1), 0, 5);
  781. icon_2 = create_pixmap (vbox1, NULL);
  782. gtk_widget_show (icon_2);
  783. gtk_box_pack_start (GTK_BOX (hbox1), icon_2, TRUE, TRUE, 2);
  784. gtk_misc_set_padding (GTK_MISC (icon_2), 0, 5);
  785. icon_3 = create_pixmap (vbox1, NULL);
  786. gtk_widget_show (icon_3);
  787. gtk_box_pack_start (GTK_BOX (hbox1), icon_3, TRUE, TRUE, 2);
  788. gtk_misc_set_padding (GTK_MISC (icon_3), 0, 5);
  789. icon_4 = create_pixmap (vbox1, NULL);
  790. gtk_widget_show (icon_4);
  791. gtk_box_pack_start (GTK_BOX (hbox1), icon_4, TRUE, TRUE, 2);
  792. gtk_misc_set_padding (GTK_MISC (icon_4), 0, 5);
  793. icon_5 = create_pixmap (vbox1, NULL);
  794. gtk_widget_show (icon_5);
  795. gtk_box_pack_start (GTK_BOX (hbox1), icon_5, TRUE, TRUE, 2);
  796. gtk_misc_set_padding (GTK_MISC (icon_5), 0, 5);
  797. icon_6 = create_pixmap (vbox1, NULL);
  798. gtk_widget_show (icon_6);
  799. gtk_box_pack_start (GTK_BOX (hbox1), icon_6, TRUE, TRUE, 0);
  800. gtk_misc_set_padding (GTK_MISC (icon_6), 0, 5);
  801. icon_7 = create_pixmap (vbox1, NULL);
  802. gtk_widget_show (icon_7);
  803. gtk_box_pack_start (GTK_BOX (hbox1), icon_7, TRUE, TRUE, 0);
  804. gtk_misc_set_padding (GTK_MISC (icon_7), 0, 5);
  805. PACK_FRAME(vbox1, frame_buttons, _("Actions"));
  806. hbuttonbox1 = gtk_hbutton_box_new ();
  807. gtk_widget_show (hbuttonbox1);
  808. gtk_container_add (GTK_CONTAINER (frame_buttons), hbuttonbox1);
  809. gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox1), 5);
  810. gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_START);
  811. gtk_box_set_spacing (GTK_BOX (hbuttonbox1), 5);
  812. btn_use = gtk_button_new_with_label (_("Use this"));
  813. gtk_widget_show (btn_use);
  814. gtk_container_add (GTK_CONTAINER (hbuttonbox1), btn_use);
  815. GTK_WIDGET_SET_FLAGS (btn_use, GTK_CAN_DEFAULT);
  816. btn_remove = gtk_button_new_with_label (_("Remove"));
  817. gtk_widget_show (btn_remove);
  818. gtk_container_add (GTK_CONTAINER (hbuttonbox1), btn_remove);
  819. GTK_WIDGET_SET_FLAGS (btn_remove, GTK_CAN_DEFAULT);
  820. g_signal_connect(G_OBJECT(btn_use), "clicked",
  821. G_CALLBACK(prefs_themes_btn_use_clicked_cb),
  822. NULL);
  823. g_signal_connect(G_OBJECT(btn_remove), "clicked",
  824. G_CALLBACK(prefs_themes_btn_remove_clicked_cb),
  825. NULL);
  826. g_signal_connect(G_OBJECT(btn_install), "clicked",
  827. G_CALLBACK(prefs_themes_btn_install_clicked_cb),
  828. NULL);
  829. prefs_themes->window = GTK_WIDGET(window);
  830. prefs_themes->name = label_name;
  831. prefs_themes->author = label_author;
  832. prefs_themes->url = label_url;
  833. prefs_themes->status = label_status;
  834. prefs_themes->global = label_global_status;
  835. prefs_themes->icons[0] = icon_1;
  836. prefs_themes->icons[1] = icon_2;
  837. prefs_themes->icons[2] = icon_3;
  838. prefs_themes->icons[3] = icon_4;
  839. prefs_themes->icons[4] = icon_5;
  840. prefs_themes->icons[5] = icon_6;
  841. prefs_themes->icons[6] = icon_7;
  842. prefs_themes->btn_use = btn_use;
  843. prefs_themes->btn_remove = btn_remove;
  844. prefs_themes->btn_install = btn_install;
  845. prefs_themes->btn_more = btn_more;
  846. prefs_themes->op_menu = menu_themes;
  847. prefs_themes->page.widget = vbox1;
  848. prefs_themes_set_themes_menu(GTK_OPTION_MENU(prefs_themes->op_menu), tdata);
  849. prefs_themes_get_theme_info(tdata);
  850. prefs_themes_display_global_stats(tdata);
  851. }
  852. static void prefs_themes_destroy_widget(PrefsPage *page)
  853. {
  854. /* ThemesPage *theme = (ThemesPage *)page; */
  855. }
  856. static void prefs_themes_save(PrefsPage *page)
  857. {
  858. /* ThemesPage *theme = (ThemesPage *)page; */
  859. }