From 0b0e6bc987da4fd88a7854ebb12bde705e92c428 Mon Sep 17 00:00:00 2001 From: Perberos Date: Thu, 1 Dec 2011 21:51:44 -0300 Subject: moving from https://github.com/perberos/mate-desktop-environment --- capplets/appearance/Makefile.am | 58 + capplets/appearance/appearance-desktop.c | 1400 +++++++++++ capplets/appearance/appearance-desktop.h | 22 + capplets/appearance/appearance-font.c | 961 ++++++++ capplets/appearance/appearance-font.h | 22 + capplets/appearance/appearance-main.c | 218 ++ capplets/appearance/appearance-style.c | 1073 ++++++++ capplets/appearance/appearance-style.h | 22 + capplets/appearance/appearance-themes.c | 1179 +++++++++ capplets/appearance/appearance-themes.h | 22 + capplets/appearance/appearance.h | 90 + capplets/appearance/data/Makefile.am | 62 + capplets/appearance/data/Makefile.in | 660 +++++ capplets/appearance/data/appearance.ui | 2603 ++++++++++++++++++++ capplets/appearance/data/cursor-large-white.pcf | Bin 0 -> 18636 bytes capplets/appearance/data/cursor-large.pcf | Bin 0 -> 17432 bytes capplets/appearance/data/cursor-white.pcf | Bin 0 -> 13848 bytes .../appearance/data/gtk-theme-thumbnailing.png | Bin 0 -> 1764 bytes .../appearance/data/icon-theme-thumbnailing.png | Bin 0 -> 1167 bytes .../data/mate-appearance-properties.desktop.in.in | 14 + .../data/mate-theme-installer.desktop.in.in | 16 + capplets/appearance/data/mate-theme-package.xml.in | 9 + .../appearance/data/mouse-cursor-normal-large.png | Bin 0 -> 251 bytes capplets/appearance/data/mouse-cursor-normal.png | Bin 0 -> 241 bytes .../appearance/data/mouse-cursor-white-large.png | Bin 0 -> 268 bytes capplets/appearance/data/mouse-cursor-white.png | Bin 0 -> 221 bytes capplets/appearance/data/subpixel-bgr.png | Bin 0 -> 125 bytes capplets/appearance/data/subpixel-rgb.png | Bin 0 -> 125 bytes capplets/appearance/data/subpixel-vbgr.png | Bin 0 -> 138 bytes capplets/appearance/data/subpixel-vrgb.png | Bin 0 -> 138 bytes capplets/appearance/data/theme-thumbnailing.png | Bin 0 -> 4482 bytes .../appearance/data/window-theme-thumbnailing.png | Bin 0 -> 2183 bytes capplets/appearance/mate-wp-info.c | 87 + capplets/appearance/mate-wp-info.h | 45 + capplets/appearance/mate-wp-item.c | 307 +++ capplets/appearance/mate-wp-item.h | 91 + capplets/appearance/mate-wp-xml.c | 451 ++++ capplets/appearance/mate-wp-xml.h | 28 + capplets/appearance/theme-installer.c | 801 ++++++ capplets/appearance/theme-installer.h | 28 + capplets/appearance/theme-save.c | 381 +++ capplets/appearance/theme-save.h | 22 + capplets/appearance/theme-util.c | 263 ++ capplets/appearance/theme-util.h | 63 + 44 files changed, 10998 insertions(+) create mode 100644 capplets/appearance/Makefile.am create mode 100644 capplets/appearance/appearance-desktop.c create mode 100644 capplets/appearance/appearance-desktop.h create mode 100644 capplets/appearance/appearance-font.c create mode 100644 capplets/appearance/appearance-font.h create mode 100644 capplets/appearance/appearance-main.c create mode 100644 capplets/appearance/appearance-style.c create mode 100644 capplets/appearance/appearance-style.h create mode 100644 capplets/appearance/appearance-themes.c create mode 100644 capplets/appearance/appearance-themes.h create mode 100644 capplets/appearance/appearance.h create mode 100644 capplets/appearance/data/Makefile.am create mode 100644 capplets/appearance/data/Makefile.in create mode 100644 capplets/appearance/data/appearance.ui create mode 100644 capplets/appearance/data/cursor-large-white.pcf create mode 100644 capplets/appearance/data/cursor-large.pcf create mode 100644 capplets/appearance/data/cursor-white.pcf create mode 100644 capplets/appearance/data/gtk-theme-thumbnailing.png create mode 100644 capplets/appearance/data/icon-theme-thumbnailing.png create mode 100644 capplets/appearance/data/mate-appearance-properties.desktop.in.in create mode 100644 capplets/appearance/data/mate-theme-installer.desktop.in.in create mode 100644 capplets/appearance/data/mate-theme-package.xml.in create mode 100644 capplets/appearance/data/mouse-cursor-normal-large.png create mode 100644 capplets/appearance/data/mouse-cursor-normal.png create mode 100644 capplets/appearance/data/mouse-cursor-white-large.png create mode 100644 capplets/appearance/data/mouse-cursor-white.png create mode 100644 capplets/appearance/data/subpixel-bgr.png create mode 100644 capplets/appearance/data/subpixel-rgb.png create mode 100644 capplets/appearance/data/subpixel-vbgr.png create mode 100644 capplets/appearance/data/subpixel-vrgb.png create mode 100644 capplets/appearance/data/theme-thumbnailing.png create mode 100644 capplets/appearance/data/window-theme-thumbnailing.png create mode 100644 capplets/appearance/mate-wp-info.c create mode 100644 capplets/appearance/mate-wp-info.h create mode 100644 capplets/appearance/mate-wp-item.c create mode 100644 capplets/appearance/mate-wp-item.h create mode 100644 capplets/appearance/mate-wp-xml.c create mode 100644 capplets/appearance/mate-wp-xml.h create mode 100644 capplets/appearance/theme-installer.c create mode 100644 capplets/appearance/theme-installer.h create mode 100644 capplets/appearance/theme-save.c create mode 100644 capplets/appearance/theme-save.h create mode 100644 capplets/appearance/theme-util.c create mode 100644 capplets/appearance/theme-util.h (limited to 'capplets/appearance') diff --git a/capplets/appearance/Makefile.am b/capplets/appearance/Makefile.am new file mode 100644 index 00000000..5c5dc2ca --- /dev/null +++ b/capplets/appearance/Makefile.am @@ -0,0 +1,58 @@ +SUBDIRS = data + +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = appearance + +bin_PROGRAMS = mate-appearance-properties + +mate_appearance_properties_SOURCES = \ + appearance.h \ + appearance-desktop.c \ + appearance-desktop.h \ + appearance-font.c \ + appearance-font.h \ + appearance-main.c \ + appearance-themes.c \ + appearance-themes.h \ + appearance-style.c \ + appearance-style.h \ + mate-wp-info.c \ + mate-wp-info.h \ + mate-wp-item.c \ + mate-wp-item.h \ + mate-wp-xml.c \ + mate-wp-xml.h \ + theme-installer.c \ + theme-installer.h \ + theme-save.c \ + theme-save.h \ + theme-util.c \ + theme-util.h + +AM_CFLAGS = -DMATE_DESKTOP_USE_UNSTABLE_API + +mate_appearance_properties_LDADD = \ + $(top_builddir)/libwindow-settings/libmate-window-settings.la \ + $(top_builddir)/capplets/common/libcommon.la \ + $(MATECC_CAPPLETS_LIBS) \ + $(FONT_CAPPLET_LIBS) \ + $(MARCO_LIBS) +mate_appearance_properties_LDFLAGS = -export-dynamic + +gtkbuilderdir = $(pkgdatadir)/ui +pixmapdir = $(pkgdatadir)/pixmaps +wallpaperdir = $(datadir)/mate-background-properties + +INCLUDES = \ + $(MARCO_CFLAGS) \ + $(MATECC_CAPPLETS_CFLAGS) \ + $(FONT_CAPPLET_CFLAGS) \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATECC_GTKBUILDER_DIR="\"$(gtkbuilderdir)\"" \ + -DMATECC_PIXMAP_DIR="\"$(pixmapdir)\"" \ + -DWALLPAPER_DATADIR="\"$(wallpaperdir)\"" + +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) + +-include $(top_srcdir)/git.mk diff --git a/capplets/appearance/appearance-desktop.c b/capplets/appearance/appearance-desktop.c new file mode 100644 index 00000000..f0dc803e --- /dev/null +++ b/capplets/appearance/appearance-desktop.c @@ -0,0 +1,1400 @@ +/* + * Copyright (C) 2007,2008 The MATE Foundation + * Written by Rodney Dawes + * Denis Washington + * Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" +#include "mate-wp-info.h" +#include "mate-wp-item.h" +#include "mate-wp-xml.h" +#include +#include +#include +#include +#include +#include + +enum { + TARGET_URI_LIST, + TARGET_BGIMAGE +}; + +static const GtkTargetEntry drop_types[] = { + {"text/uri-list", 0, TARGET_URI_LIST}, + {"property/bgimage", 0, TARGET_BGIMAGE} +}; + +static const GtkTargetEntry drag_types[] = { + {"text/uri-list", GTK_TARGET_OTHER_WIDGET, TARGET_URI_LIST} +}; + + +static void wp_update_preview(GtkFileChooser* chooser, AppearanceData* data); + +static void select_item(AppearanceData* data, MateWPItem* item, gboolean scroll) +{ + GtkTreePath* path; + + g_return_if_fail(data != NULL); + + if (item == NULL) + return; + + path = gtk_tree_row_reference_get_path(item->rowref); + + gtk_icon_view_select_path(data->wp_view, path); + + if (scroll) + { + gtk_icon_view_scroll_to_path(data->wp_view, path, FALSE, 0.5, 0.0); + } + + gtk_tree_path_free(path); +} + +static MateWPItem* get_selected_item(AppearanceData* data, GtkTreeIter* iter) +{ + MateWPItem* item = NULL; + GList* selected; + + selected = gtk_icon_view_get_selected_items (data->wp_view); + + if (selected != NULL) + { + GtkTreeIter sel_iter; + + gtk_tree_model_get_iter(data->wp_model, &sel_iter, selected->data); + + g_list_foreach(selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free(selected); + + if (iter) + *iter = sel_iter; + + gtk_tree_model_get(data->wp_model, &sel_iter, 1, &item, -1); + } + + return item; +} + +static gboolean predicate (gpointer key, gpointer value, gpointer data) +{ + MateBG *bg = data; + MateWPItem *item = value; + + return item->bg == bg; +} + +static void on_item_changed (MateBG *bg, AppearanceData *data) { + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + MateWPItem *item; + + item = g_hash_table_find (data->wp_hash, predicate, bg); + + if (!item) + return; + + model = gtk_tree_row_reference_get_model (item->rowref); + path = gtk_tree_row_reference_get_path (item->rowref); + + if (gtk_tree_model_get_iter (model, &iter, path)) { + GdkPixbuf *pixbuf; + + g_signal_handlers_block_by_func (bg, G_CALLBACK (on_item_changed), data); + + pixbuf = mate_wp_item_get_thumbnail (item, + data->thumb_factory, + data->thumb_width, + data->thumb_height); + if (pixbuf) { + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, 0, pixbuf, -1); + g_object_unref (pixbuf); + } + + g_signal_handlers_unblock_by_func (bg, G_CALLBACK (on_item_changed), data); + } +} + +static void +wp_props_load_wallpaper (gchar *key, + MateWPItem *item, + AppearanceData *data) +{ + GtkTreeIter iter; + GtkTreePath *path; + GdkPixbuf *pixbuf; + + if (item->deleted == TRUE) + return; + + gtk_list_store_append (GTK_LIST_STORE (data->wp_model), &iter); + + pixbuf = mate_wp_item_get_thumbnail (item, data->thumb_factory, + data->thumb_width, + data->thumb_height); + mate_wp_item_update_description (item); + + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, + 0, pixbuf, + 1, item, + -1); + + if (pixbuf != NULL) + g_object_unref (pixbuf); + + path = gtk_tree_model_get_path (data->wp_model, &iter); + item->rowref = gtk_tree_row_reference_new (data->wp_model, path); + g_signal_connect (item->bg, "changed", G_CALLBACK (on_item_changed), data); + gtk_tree_path_free (path); +} + +static MateWPItem * +wp_add_image (AppearanceData *data, + const gchar *filename) +{ + MateWPItem *item; + + if (!filename) + return NULL; + + item = g_hash_table_lookup (data->wp_hash, filename); + + if (item != NULL) + { + if (item->deleted) + { + item->deleted = FALSE; + wp_props_load_wallpaper (item->filename, item, data); + } + } + else + { + item = mate_wp_item_new (filename, data->wp_hash, data->thumb_factory); + + if (item != NULL) + { + wp_props_load_wallpaper (item->filename, item, data); + } + } + + return item; +} + +static void +wp_add_images (AppearanceData *data, + GSList *images) +{ + GdkWindow *window; + GtkWidget *w; + GdkCursor *cursor; + MateWPItem *item; + + w = appearance_capplet_get_widget (data, "appearance_window"); + window = gtk_widget_get_window (w); + + item = NULL; + cursor = gdk_cursor_new_for_display (gdk_display_get_default (), + GDK_WATCH); + gdk_window_set_cursor (window, cursor); + gdk_cursor_unref (cursor); + + while (images != NULL) + { + gchar *uri = images->data; + + item = wp_add_image (data, uri); + images = g_slist_remove (images, uri); + g_free (uri); + } + + gdk_window_set_cursor (window, NULL); + + if (item != NULL) + { + select_item (data, item, TRUE); + } +} + +static void +wp_option_menu_set (AppearanceData *data, + int value, + gboolean shade_type) +{ + if (shade_type) + { + gtk_combo_box_set_active (GTK_COMBO_BOX (data->wp_color_menu), + value); + + if (value == MATE_BG_COLOR_SOLID) + gtk_widget_hide (data->wp_scpicker); + else + gtk_widget_show (data->wp_scpicker); + } + else + { + gtk_combo_box_set_active (GTK_COMBO_BOX (data->wp_style_menu), + value); + } +} + +static void +wp_set_sensitivities (AppearanceData *data) +{ + MateWPItem *item; + gchar *filename = NULL; + + item = get_selected_item (data, NULL); + + if (item != NULL) + filename = item->filename; + + if (!mateconf_client_key_is_writable (data->client, WP_OPTIONS_KEY, NULL) + || (filename && !strcmp (filename, "(none)"))) + gtk_widget_set_sensitive (data->wp_style_menu, FALSE); + else + gtk_widget_set_sensitive (data->wp_style_menu, TRUE); + + if (!mateconf_client_key_is_writable (data->client, WP_SHADING_KEY, NULL)) + gtk_widget_set_sensitive (data->wp_color_menu, FALSE); + else + gtk_widget_set_sensitive (data->wp_color_menu, TRUE); + + if (!mateconf_client_key_is_writable (data->client, WP_PCOLOR_KEY, NULL)) + gtk_widget_set_sensitive (data->wp_pcpicker, FALSE); + else + gtk_widget_set_sensitive (data->wp_pcpicker, TRUE); + + if (!mateconf_client_key_is_writable (data->client, WP_SCOLOR_KEY, NULL)) + gtk_widget_set_sensitive (data->wp_scpicker, FALSE); + else + gtk_widget_set_sensitive (data->wp_scpicker, TRUE); + + if (!filename || !strcmp (filename, "(none)")) + gtk_widget_set_sensitive (data->wp_rem_button, FALSE); + else + gtk_widget_set_sensitive (data->wp_rem_button, TRUE); +} + +static void +wp_scale_type_changed (GtkComboBox *combobox, + AppearanceData *data) +{ + MateWPItem *item; + GtkTreeIter iter; + GdkPixbuf *pixbuf; + + item = get_selected_item (data, &iter); + + if (item == NULL) + return; + + item->options = gtk_combo_box_get_active (GTK_COMBO_BOX (data->wp_style_menu)); + + pixbuf = mate_wp_item_get_thumbnail (item, data->thumb_factory, + data->thumb_width, + data->thumb_height); + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, 0, pixbuf, -1); + if (pixbuf != NULL) + g_object_unref (pixbuf); + + if (mateconf_client_key_is_writable (data->client, WP_OPTIONS_KEY, NULL)) + mateconf_client_set_string (data->client, WP_OPTIONS_KEY, + wp_item_option_to_string (item->options), NULL); +} + +static void +wp_shade_type_changed (GtkWidget *combobox, + AppearanceData *data) +{ + MateWPItem *item; + GtkTreeIter iter; + GdkPixbuf *pixbuf; + + item = get_selected_item (data, &iter); + + if (item == NULL) + return; + + item->shade_type = gtk_combo_box_get_active (GTK_COMBO_BOX (data->wp_color_menu)); + + pixbuf = mate_wp_item_get_thumbnail (item, data->thumb_factory, + data->thumb_width, + data->thumb_height); + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, 0, pixbuf, -1); + if (pixbuf != NULL) + g_object_unref (pixbuf); + + if (mateconf_client_key_is_writable (data->client, WP_SHADING_KEY, NULL)) + mateconf_client_set_string (data->client, WP_SHADING_KEY, + wp_item_shading_to_string (item->shade_type), NULL); +} + +static void +wp_color_changed (AppearanceData *data, + gboolean update) +{ + MateWPItem *item; + + item = get_selected_item (data, NULL); + + if (item == NULL) + return; + + gtk_color_button_get_color (GTK_COLOR_BUTTON (data->wp_pcpicker), item->pcolor); + gtk_color_button_get_color (GTK_COLOR_BUTTON (data->wp_scpicker), item->scolor); + + if (update) + { + gchar *pcolor, *scolor; + + pcolor = gdk_color_to_string (item->pcolor); + scolor = gdk_color_to_string (item->scolor); + mateconf_client_set_string (data->client, WP_PCOLOR_KEY, pcolor, NULL); + mateconf_client_set_string (data->client, WP_SCOLOR_KEY, scolor, NULL); + g_free (pcolor); + g_free (scolor); + } + + wp_shade_type_changed (NULL, data); +} + +static void +wp_scolor_changed (GtkWidget *widget, + AppearanceData *data) +{ + wp_color_changed (data, TRUE); +} + +static void +wp_remove_wallpaper (GtkWidget *widget, + AppearanceData *data) +{ + MateWPItem *item; + GtkTreeIter iter; + GtkTreePath *path; + + item = get_selected_item (data, &iter); + + if (item) + { + item->deleted = TRUE; + + if (gtk_list_store_remove (GTK_LIST_STORE (data->wp_model), &iter)) + path = gtk_tree_model_get_path (data->wp_model, &iter); + else + path = gtk_tree_path_new_first (); + + gtk_icon_view_select_path (data->wp_view, path); + gtk_icon_view_set_cursor (data->wp_view, path, NULL, FALSE); + gtk_tree_path_free (path); + } +} + +static void +wp_uri_changed (const gchar *uri, + AppearanceData *data) +{ + MateWPItem *item, *selected; + + item = g_hash_table_lookup (data->wp_hash, uri); + selected = get_selected_item (data, NULL); + + if (selected != NULL && strcmp (selected->filename, uri) != 0) + { + if (item == NULL) + item = wp_add_image (data, uri); + + if (item) + select_item (data, item, TRUE); + } +} + +static void +wp_file_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + const gchar *uri; + gchar *wpfile; + + uri = mateconf_value_get_string (entry->value); + + if (g_utf8_validate (uri, -1, NULL) && g_file_test (uri, G_FILE_TEST_EXISTS)) + wpfile = g_strdup (uri); + else + wpfile = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL); + + wp_uri_changed (wpfile, data); + + g_free (wpfile); +} + +static void +wp_options_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + MateWPItem *item; + const gchar *option; + + option = mateconf_value_get_string (entry->value); + + /* "none" means we don't use a background image */ + if (option == NULL || !strcmp (option, "none")) + { + /* temporarily disconnect so we don't override settings when + * updating the selection */ + data->wp_update_mateconf = FALSE; + wp_uri_changed ("(none)", data); + data->wp_update_mateconf = TRUE; + return; + } + + item = get_selected_item (data, NULL); + + if (item != NULL) + { + item->options = wp_item_string_to_option (option); + wp_option_menu_set (data, item->options, FALSE); + } +} + +static void +wp_shading_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + MateWPItem *item; + + wp_set_sensitivities (data); + + item = get_selected_item (data, NULL); + + if (item != NULL) + { + item->shade_type = wp_item_string_to_shading (mateconf_value_get_string (entry->value)); + wp_option_menu_set (data, item->shade_type, TRUE); + } +} + +static void +wp_color1_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + GdkColor color; + const gchar *colorhex; + + colorhex = mateconf_value_get_string (entry->value); + + gdk_color_parse (colorhex, &color); + + gtk_color_button_set_color (GTK_COLOR_BUTTON (data->wp_pcpicker), &color); + + wp_color_changed (data, FALSE); +} + +static void +wp_color2_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + GdkColor color; + const gchar *colorhex; + + wp_set_sensitivities (data); + + colorhex = mateconf_value_get_string (entry->value); + + gdk_color_parse (colorhex, &color); + + gtk_color_button_set_color (GTK_COLOR_BUTTON (data->wp_scpicker), &color); + + wp_color_changed (data, FALSE); +} + +static gboolean +wp_props_wp_set (AppearanceData *data, MateWPItem *item) +{ + MateConfChangeSet *cs; + gchar *pcolor, *scolor; + + cs = mateconf_change_set_new (); + + if (!strcmp (item->filename, "(none)")) + { + mateconf_change_set_set_string (cs, WP_OPTIONS_KEY, "none"); + mateconf_change_set_set_string (cs, WP_FILE_KEY, ""); + } + else + { + gchar *uri; + + if (g_utf8_validate (item->filename, -1, NULL)) + uri = g_strdup (item->filename); + else + uri = g_filename_to_utf8 (item->filename, -1, NULL, NULL, NULL); + + if (uri == NULL) { + g_warning ("Failed to convert filename to UTF-8: %s", item->filename); + } else { + mateconf_change_set_set_string (cs, WP_FILE_KEY, uri); + g_free (uri); + } + + mateconf_change_set_set_string (cs, WP_OPTIONS_KEY, + wp_item_option_to_string (item->options)); + } + + mateconf_change_set_set_string (cs, WP_SHADING_KEY, + wp_item_shading_to_string (item->shade_type)); + + pcolor = gdk_color_to_string (item->pcolor); + scolor = gdk_color_to_string (item->scolor); + mateconf_change_set_set_string (cs, WP_PCOLOR_KEY, pcolor); + mateconf_change_set_set_string (cs, WP_SCOLOR_KEY, scolor); + g_free (pcolor); + g_free (scolor); + + mateconf_client_commit_change_set (data->client, cs, TRUE, NULL); + + mateconf_change_set_unref (cs); + + return FALSE; +} + +static void +wp_props_wp_selected (GtkTreeSelection *selection, + AppearanceData *data) +{ + MateWPItem *item; + + item = get_selected_item (data, NULL); + + if (item != NULL) + { + wp_set_sensitivities (data); + + if (strcmp (item->filename, "(none)") != 0) + wp_option_menu_set (data, item->options, FALSE); + + wp_option_menu_set (data, item->shade_type, TRUE); + + gtk_color_button_set_color (GTK_COLOR_BUTTON (data->wp_pcpicker), + item->pcolor); + gtk_color_button_set_color (GTK_COLOR_BUTTON (data->wp_scpicker), + item->scolor); + + if (data->wp_update_mateconf) + wp_props_wp_set (data, item); + } + else + { + gtk_widget_set_sensitive (data->wp_rem_button, FALSE); + } +} + +void +wp_create_filechooser (AppearanceData *data) +{ + const char *start_dir, *pictures = NULL; + GtkFileFilter *filter; + + data->wp_filesel = GTK_FILE_CHOOSER ( + gtk_file_chooser_dialog_new (_("Add Wallpaper"), + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window")), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, + GTK_RESPONSE_OK, + NULL)); + + gtk_dialog_set_default_response (GTK_DIALOG (data->wp_filesel), GTK_RESPONSE_OK); + gtk_file_chooser_set_select_multiple (data->wp_filesel, TRUE); + gtk_file_chooser_set_use_preview_label (data->wp_filesel, FALSE); + + start_dir = g_get_home_dir (); + + if (g_file_test ("/usr/share/backgrounds", G_FILE_TEST_IS_DIR)) { + gtk_file_chooser_add_shortcut_folder (data->wp_filesel, + "/usr/share/backgrounds", NULL); + start_dir = "/usr/share/backgrounds"; + } + + pictures = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES); + if (pictures != NULL && g_file_test (pictures, G_FILE_TEST_IS_DIR)) { + gtk_file_chooser_add_shortcut_folder (data->wp_filesel, pictures, NULL); + start_dir = pictures; + } + + gtk_file_chooser_set_current_folder (data->wp_filesel, start_dir); + + filter = gtk_file_filter_new (); + gtk_file_filter_add_pixbuf_formats (filter); + gtk_file_filter_set_name (filter, _("Images")); + gtk_file_chooser_add_filter (data->wp_filesel, filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All files")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (data->wp_filesel, filter); + + data->wp_image = gtk_image_new (); + gtk_file_chooser_set_preview_widget (data->wp_filesel, data->wp_image); + gtk_widget_set_size_request (data->wp_image, 128, -1); + + gtk_widget_show (data->wp_image); + + g_signal_connect (data->wp_filesel, "update-preview", + (GCallback) wp_update_preview, data); +} + +static void +wp_file_open_dialog (GtkWidget *widget, + AppearanceData *data) +{ + GSList *files; + + if (!data->wp_filesel) + wp_create_filechooser (data); + + switch (gtk_dialog_run (GTK_DIALOG (data->wp_filesel))) + { + case GTK_RESPONSE_OK: + files = gtk_file_chooser_get_filenames (data->wp_filesel); + wp_add_images (data, files); + case GTK_RESPONSE_CANCEL: + default: + gtk_widget_hide (GTK_WIDGET (data->wp_filesel)); + break; + } +} + +static void +wp_drag_received (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, + AppearanceData *data) +{ + if (info == TARGET_URI_LIST || info == TARGET_BGIMAGE) + { + GSList *realuris = NULL; + gchar **uris; + + uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data)); + if (uris != NULL) + { + GtkWidget *w; + GdkWindow *window; + GdkCursor *cursor; + gchar **uri; + + w = appearance_capplet_get_widget (data, "appearance_window"); + window = gtk_widget_get_window (w); + + cursor = gdk_cursor_new_for_display (gdk_display_get_default (), + GDK_WATCH); + gdk_window_set_cursor (window, cursor); + gdk_cursor_unref (cursor); + + for (uri = uris; *uri; ++uri) + { + GFile *f; + + f = g_file_new_for_uri (*uri); + realuris = g_slist_append (realuris, g_file_get_path (f)); + g_object_unref (f); + } + + wp_add_images (data, realuris); + gdk_window_set_cursor (window, NULL); + + g_strfreev (uris); + } + } +} + +static void +wp_drag_get_data (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint type, guint time, + AppearanceData *data) +{ + if (type == TARGET_URI_LIST) { + MateWPItem *item = get_selected_item (data, NULL); + + if (item != NULL) { + char *uris[2]; + + uris[0] = g_filename_to_uri (item->filename, NULL, NULL); + uris[1] = NULL; + + gtk_selection_data_set_uris (selection_data, uris); + + g_free (uris[0]); + } + } +} + +static gboolean +wp_view_tooltip_cb (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_mode, + GtkTooltip *tooltip, + AppearanceData *data) +{ + GtkTreeIter iter; + MateWPItem *item; + + if (gtk_icon_view_get_tooltip_context (data->wp_view, + &x, &y, + keyboard_mode, + NULL, + NULL, + &iter)) + { + gtk_tree_model_get (data->wp_model, &iter, 1, &item, -1); + gtk_tooltip_set_markup (tooltip, item->description); + + return TRUE; + } + + return FALSE; +} + +static gint +wp_list_sort (GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + AppearanceData *data) +{ + MateWPItem *itema, *itemb; + gint retval; + + gtk_tree_model_get (model, a, 1, &itema, -1); + gtk_tree_model_get (model, b, 1, &itemb, -1); + + if (!strcmp (itema->filename, "(none)")) + { + retval = -1; + } + else if (!strcmp (itemb->filename, "(none)")) + { + retval = 1; + } + else + { + retval = g_utf8_collate (itema->description, itemb->description); + } + + return retval; +} + +static void +wp_update_preview (GtkFileChooser *chooser, + AppearanceData *data) +{ + gchar *uri; + + uri = gtk_file_chooser_get_preview_uri (chooser); + + if (uri) + { + GdkPixbuf *pixbuf = NULL; + const gchar *mime_type = NULL; + GFile *file; + GFileInfo *file_info; + + file = g_file_new_for_uri (uri); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + g_object_unref (file); + + if (file_info != NULL) + { + mime_type = g_file_info_get_content_type (file_info); + g_object_unref (file_info); + } + + if (mime_type) + { + pixbuf = mate_desktop_thumbnail_factory_generate_thumbnail (data->thumb_factory, + uri, + mime_type); + } + + if (pixbuf != NULL) + { + gtk_image_set_from_pixbuf (GTK_IMAGE (data->wp_image), pixbuf); + g_object_unref (pixbuf); + } + else + { + gtk_image_set_from_stock (GTK_IMAGE (data->wp_image), + "gtk-dialog-question", + GTK_ICON_SIZE_DIALOG); + } + } + + gtk_file_chooser_set_preview_widget_active (chooser, TRUE); +} + +static gboolean +reload_item (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + AppearanceData *data) +{ + MateWPItem *item; + GdkPixbuf *pixbuf; + + gtk_tree_model_get (model, iter, 1, &item, -1); + + pixbuf = mate_wp_item_get_thumbnail (item, + data->thumb_factory, + data->thumb_width, + data->thumb_height); + if (pixbuf) { + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), iter, 0, pixbuf, -1); + g_object_unref (pixbuf); + } + + return FALSE; +} + +static gdouble +get_monitor_aspect_ratio_for_widget (GtkWidget *widget) +{ + gdouble aspect; + gint monitor; + GdkRectangle rect; + + monitor = gdk_screen_get_monitor_at_window (gtk_widget_get_screen (widget), gtk_widget_get_window (widget)); + gdk_screen_get_monitor_geometry (gtk_widget_get_screen (widget), monitor, &rect); + aspect = rect.height / (gdouble)rect.width; + + return aspect; +} + +#define LIST_IMAGE_SIZE 108 + +static void +compute_thumbnail_sizes (AppearanceData *data) +{ + gdouble aspect; + + aspect = get_monitor_aspect_ratio_for_widget (GTK_WIDGET (data->wp_view)); + if (aspect > 1) { + /* portrait */ + data->thumb_width = LIST_IMAGE_SIZE / aspect; + data->thumb_height = LIST_IMAGE_SIZE; + } else { + data->thumb_width = LIST_IMAGE_SIZE; + data->thumb_height = LIST_IMAGE_SIZE * aspect; + } +} + +static void +reload_wallpapers (AppearanceData *data) +{ + compute_thumbnail_sizes (data); + gtk_tree_model_foreach (data->wp_model, (GtkTreeModelForeachFunc)reload_item, data); +} + +static gboolean +wp_load_stuffs (void *user_data) +{ + AppearanceData *data; + gchar *imagepath, *uri, *style; + MateWPItem *item; + + data = (AppearanceData *) user_data; + + compute_thumbnail_sizes (data); + + mate_wp_xml_load_list (data); + g_hash_table_foreach (data->wp_hash, (GHFunc) wp_props_load_wallpaper, + data); + + style = mateconf_client_get_string (data->client, + WP_OPTIONS_KEY, + NULL); + if (style == NULL) + style = g_strdup ("none"); + + uri = mateconf_client_get_string (data->client, + WP_FILE_KEY, + NULL); + + if (uri && *uri == '\0') + { + g_free (uri); + uri = NULL; + } + + if (uri == NULL) + uri = g_strdup ("(none)"); + + if (g_utf8_validate (uri, -1, NULL) && g_file_test (uri, G_FILE_TEST_EXISTS)) + imagepath = g_strdup (uri); + else + imagepath = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL); + + g_free (uri); + + item = g_hash_table_lookup (data->wp_hash, imagepath); + + if (item != NULL) + { + /* update with the current mateconf settings */ + mate_wp_item_update (item); + + if (strcmp (style, "none") != 0) + { + if (item->deleted == TRUE) + { + item->deleted = FALSE; + wp_props_load_wallpaper (item->filename, item, data); + } + + select_item (data, item, FALSE); + } + } + else if (strcmp (style, "none") != 0) + { + item = wp_add_image (data, imagepath); + if (item) + select_item (data, item, FALSE); + } + + item = g_hash_table_lookup (data->wp_hash, "(none)"); + if (item == NULL) + { + item = mate_wp_item_new ("(none)", data->wp_hash, data->thumb_factory); + if (item != NULL) + { + wp_props_load_wallpaper (item->filename, item, data); + } + } + else + { + if (item->deleted == TRUE) + { + item->deleted = FALSE; + wp_props_load_wallpaper (item->filename, item, data); + } + + if (!strcmp (style, "none")) + { + select_item (data, item, FALSE); + wp_option_menu_set (data, MATE_BG_PLACEMENT_SCALED, FALSE); + } + } + g_free (imagepath); + g_free (style); + + if (data->wp_uris) { + wp_add_images (data, data->wp_uris); + data->wp_uris = NULL; + } + + return FALSE; +} + +static void +wp_select_after_realize (GtkWidget *widget, + AppearanceData *data) +{ + MateWPItem *item; + + g_idle_add (wp_load_stuffs, data); + + item = get_selected_item (data, NULL); + if (item == NULL) + item = g_hash_table_lookup (data->wp_hash, "(none)"); + + select_item (data, item, TRUE); +} + +static GdkPixbuf *buttons[3]; + +static void +create_button_images (AppearanceData *data) +{ + GtkWidget *widget = (GtkWidget*)data->wp_view; + GtkStyle *style = gtk_widget_get_style (widget); + GtkIconSet *icon_set; + GdkPixbuf *pixbuf, *pb, *pb2; + gint i, w, h; + + icon_set = gtk_style_lookup_icon_set (style, "gtk-media-play"); + pb = gtk_icon_set_render_icon (icon_set, + style, + GTK_TEXT_DIR_RTL, + GTK_STATE_NORMAL, + GTK_ICON_SIZE_MENU, + widget, + NULL); + pb2 = gtk_icon_set_render_icon (icon_set, + style, + GTK_TEXT_DIR_LTR, + GTK_STATE_NORMAL, + GTK_ICON_SIZE_MENU, + widget, + NULL); + w = gdk_pixbuf_get_width (pb); + h = gdk_pixbuf_get_height (pb); + + for (i = 0; i < 3; i++) { + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 2 * w, h); + gdk_pixbuf_fill (pixbuf, 0); + if (i > 0) + gdk_pixbuf_composite (pb, pixbuf, 0, 0, w, h, 0, 0, 1, 1, GDK_INTERP_NEAREST, 255); + if (i < 2) + gdk_pixbuf_composite (pb2, pixbuf, w, 0, w, h, w, 0, 1, 1, GDK_INTERP_NEAREST, 255); + + buttons[i] = pixbuf; + } + + g_object_unref (pb); + g_object_unref (pb2); +} + +static void +next_frame (AppearanceData *data, + GtkCellRenderer *cr, + gint direction) +{ + MateWPItem *item; + GtkTreeIter iter; + GdkPixbuf *pixbuf, *pb; + gint frame; + + pixbuf = NULL; + + frame = data->frame + direction; + item = get_selected_item (data, &iter); + + if (frame >= 0) + pixbuf = mate_wp_item_get_frame_thumbnail (item, + data->thumb_factory, + data->thumb_width, + data->thumb_height, + frame); + if (pixbuf) { + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, 0, pixbuf, -1); + g_object_unref (pixbuf); + data->frame = frame; + } + + pb = buttons[1]; + if (direction < 0) { + if (frame == 0) + pb = buttons[0]; + } + else { + pixbuf = mate_wp_item_get_frame_thumbnail (item, + data->thumb_factory, + data->thumb_width, + data->thumb_height, + frame + 1); + if (pixbuf) + g_object_unref (pixbuf); + else + pb = buttons[2]; + } + g_object_set (cr, "pixbuf", pb, NULL); +} + +static gboolean +wp_button_press_cb (GtkWidget *widget, + GdkEventButton *event, + AppearanceData *data) +{ + GtkCellRenderer *cell; + GdkEventButton *button_event = (GdkEventButton *) event; + + if (event->type != GDK_BUTTON_PRESS) + return FALSE; + + if (gtk_icon_view_get_item_at_pos (GTK_ICON_VIEW (widget), + button_event->x, button_event->y, + NULL, &cell)) { + if (g_object_get_data (G_OBJECT (cell), "buttons")) { + gint w, h; + GtkCellRenderer *cell2 = NULL; + gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); + if (gtk_icon_view_get_item_at_pos (GTK_ICON_VIEW (widget), + button_event->x + w, button_event->y, + NULL, &cell2) && cell == cell2) + next_frame (data, cell, -1); + else + next_frame (data, cell, 1); + return TRUE; + } + } + + return FALSE; +} + +static void +wp_selected_changed_cb (GtkIconView *view, + AppearanceData *data) +{ + GtkCellRenderer *cr; + GList *cells, *l; + + data->frame = -1; + + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (data->wp_view)); + for (l = cells; l; l = l->next) { + cr = l->data; + if (g_object_get_data (G_OBJECT (cr), "buttons")) + g_object_set (cr, "pixbuf", buttons[0], NULL); + } + g_list_free (cells); +} + +static void +buttons_cell_data_func (GtkCellLayout *layout, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + AppearanceData *data = user_data; + GtkTreePath *path; + MateWPItem *item; + gboolean visible; + + path = gtk_tree_model_get_path (model, iter); + + if (gtk_icon_view_path_is_selected (GTK_ICON_VIEW (layout), path)) { + item = get_selected_item (data, NULL); + visible = mate_bg_changes_with_time (item->bg); + } + else + visible = FALSE; + + g_object_set (G_OBJECT (cell), "visible", visible, NULL); + + gtk_tree_path_free (path); +} + +static void +screen_monitors_changed (GdkScreen *screen, + AppearanceData *data) +{ + reload_wallpapers (data); +} + +void +desktop_init (AppearanceData *data, + const gchar **uris) +{ + GtkWidget *add_button, *w; + GtkCellRenderer *cr; + char *url; + + data->wp_update_mateconf = TRUE; + + data->wp_uris = NULL; + if (uris != NULL) { + while (*uris != NULL) { + data->wp_uris = g_slist_append (data->wp_uris, g_strdup (*uris)); + uris++; + } + } + + w = appearance_capplet_get_widget (data, "more_backgrounds_linkbutton"); + url = mateconf_client_get_string (data->client, MORE_BACKGROUNDS_URL_KEY, NULL); + if (url != NULL && url[0] != '\0') { + gtk_link_button_set_uri (GTK_LINK_BUTTON (w), url); + gtk_widget_show (w); + } else { + gtk_widget_hide (w); + } + g_free (url); + + data->wp_hash = g_hash_table_new (g_str_hash, g_str_equal); + + mateconf_client_add_dir (data->client, WP_PATH_KEY, + MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + + mateconf_client_notify_add (data->client, + WP_FILE_KEY, + (MateConfClientNotifyFunc) wp_file_changed, + data, NULL, NULL); + mateconf_client_notify_add (data->client, + WP_OPTIONS_KEY, + (MateConfClientNotifyFunc) wp_options_changed, + data, NULL, NULL); + mateconf_client_notify_add (data->client, + WP_SHADING_KEY, + (MateConfClientNotifyFunc) wp_shading_changed, + data, NULL, NULL); + mateconf_client_notify_add (data->client, + WP_PCOLOR_KEY, + (MateConfClientNotifyFunc) wp_color1_changed, + data, NULL, NULL); + mateconf_client_notify_add (data->client, + WP_SCOLOR_KEY, + (MateConfClientNotifyFunc) wp_color2_changed, + data, NULL, NULL); + + data->wp_model = GTK_TREE_MODEL (gtk_list_store_new (2, GDK_TYPE_PIXBUF, + G_TYPE_POINTER)); + + data->wp_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "wp_view")); + gtk_icon_view_set_model (data->wp_view, GTK_TREE_MODEL (data->wp_model)); + + g_signal_connect_after (data->wp_view, "realize", + (GCallback) wp_select_after_realize, data); + + gtk_cell_layout_clear (GTK_CELL_LAYOUT (data->wp_view)); + + cr = gtk_cell_renderer_pixbuf_new (); + g_object_set (cr, "xpad", 5, "ypad", 5, NULL); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->wp_view), cr, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (data->wp_view), cr, + "pixbuf", 0, + NULL); + + cr = gtk_cell_renderer_pixbuf_new (); + create_button_images (data); + g_object_set (cr, + "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, + "pixbuf", buttons[0], + NULL); + g_object_set_data (G_OBJECT (cr), "buttons", GINT_TO_POINTER (TRUE)); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->wp_view), cr, FALSE); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (data->wp_view), cr, + buttons_cell_data_func, data, NULL); + g_signal_connect (data->wp_view, "selection-changed", + (GCallback) wp_selected_changed_cb, data); + g_signal_connect (data->wp_view, "button-press-event", + G_CALLBACK (wp_button_press_cb), data); + + data->frame = -1; + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (data->wp_model), 1, + (GtkTreeIterCompareFunc) wp_list_sort, + data, NULL); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->wp_model), + 1, GTK_SORT_ASCENDING); + + gtk_drag_dest_set (GTK_WIDGET (data->wp_view), GTK_DEST_DEFAULT_ALL, drop_types, + G_N_ELEMENTS (drop_types), GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect (data->wp_view, "drag_data_received", + (GCallback) wp_drag_received, data); + + gtk_drag_source_set (GTK_WIDGET (data->wp_view), GDK_BUTTON1_MASK, + drag_types, G_N_ELEMENTS (drag_types), GDK_ACTION_COPY); + g_signal_connect (data->wp_view, "drag-data-get", + (GCallback) wp_drag_get_data, data); + + data->wp_style_menu = appearance_capplet_get_widget (data, "wp_style_menu"); + + g_signal_connect (data->wp_style_menu, "changed", + (GCallback) wp_scale_type_changed, data); + + data->wp_color_menu = appearance_capplet_get_widget (data, "wp_color_menu"); + + g_signal_connect (data->wp_color_menu, "changed", + (GCallback) wp_shade_type_changed, data); + + data->wp_scpicker = appearance_capplet_get_widget (data, "wp_scpicker"); + + g_signal_connect (data->wp_scpicker, "color-set", + (GCallback) wp_scolor_changed, data); + + data->wp_pcpicker = appearance_capplet_get_widget (data, "wp_pcpicker"); + + g_signal_connect (data->wp_pcpicker, "color-set", + (GCallback) wp_scolor_changed, data); + + add_button = appearance_capplet_get_widget (data, "wp_add_button"); + gtk_button_set_image (GTK_BUTTON (add_button), + gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_BUTTON)); + + g_signal_connect (add_button, "clicked", + (GCallback) wp_file_open_dialog, data); + + data->wp_rem_button = appearance_capplet_get_widget (data, "wp_rem_button"); + + g_signal_connect (data->wp_rem_button, "clicked", + (GCallback) wp_remove_wallpaper, data); + data->screen_monitors_handler = g_signal_connect (gtk_widget_get_screen (GTK_WIDGET (data->wp_view)), + "monitors-changed", + G_CALLBACK (screen_monitors_changed), + data); + data->screen_size_handler = g_signal_connect (gtk_widget_get_screen (GTK_WIDGET (data->wp_view)), + "size-changed", + G_CALLBACK (screen_monitors_changed), + data); + + g_signal_connect (data->wp_view, "selection-changed", + (GCallback) wp_props_wp_selected, data); + g_signal_connect (data->wp_view, "query-tooltip", + (GCallback) wp_view_tooltip_cb, data); + gtk_widget_set_has_tooltip (GTK_WIDGET (data->wp_view), TRUE); + + wp_set_sensitivities (data); + + /* create the file selector later to save time on startup */ + data->wp_filesel = NULL; + +} + +void +desktop_shutdown (AppearanceData *data) +{ + mate_wp_xml_save_list (data); + + if (data->screen_monitors_handler > 0) { + g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (data->wp_view)), + data->screen_monitors_handler); + data->screen_monitors_handler = 0; + } + if (data->screen_size_handler > 0) { + g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (data->wp_view)), + data->screen_size_handler); + data->screen_size_handler = 0; + } + + g_slist_foreach (data->wp_uris, (GFunc) g_free, NULL); + g_slist_free (data->wp_uris); + if (data->wp_filesel) + { + g_object_ref_sink (data->wp_filesel); + g_object_unref (data->wp_filesel); + } +} diff --git a/capplets/appearance/appearance-desktop.h b/capplets/appearance/appearance-desktop.h new file mode 100644 index 00000000..ae0b25a9 --- /dev/null +++ b/capplets/appearance/appearance-desktop.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Denis Washington + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void desktop_init (AppearanceData *data, const gchar **uris); +void desktop_shutdown (AppearanceData *data); diff --git a/capplets/appearance/appearance-font.c b/capplets/appearance/appearance-font.c new file mode 100644 index 00000000..d9f4d3c2 --- /dev/null +++ b/capplets/appearance/appearance-font.c @@ -0,0 +1,961 @@ +/* + * Copyright (C) 2007 The GNOME Foundation + * Written by Jonathan Blandford + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" + +#include +#include +#include + +#ifdef HAVE_XFT2 + #include + #include +#endif /* HAVE_XFT2 */ + +#include + +#include "capplet-util.h" +#include "mateconf-property-editor.h" + +#define GTK_FONT_KEY "/desktop/mate/interface/font_name" +#define DESKTOP_FONT_KEY "/apps/caja/preferences/desktop_font" + +#define MARCO_DIR "/apps/marco/general" +#define WINDOW_TITLE_FONT_KEY MARCO_DIR "/titlebar_font" +#define WINDOW_TITLE_USES_SYSTEM_KEY MARCO_DIR "/titlebar_uses_system_font" +#define MONOSPACE_FONT_KEY "/desktop/mate/interface/monospace_font_name" +#define DOCUMENT_FONT_KEY "/desktop/mate/interface/document_font_name" + +#ifdef HAVE_XFT2 +#define FONT_RENDER_DIR "/desktop/mate/font_rendering" +#define FONT_ANTIALIASING_KEY FONT_RENDER_DIR "/antialiasing" +#define FONT_HINTING_KEY FONT_RENDER_DIR "/hinting" +#define FONT_RGBA_ORDER_KEY FONT_RENDER_DIR "/rgba_order" +#define FONT_DPI_KEY FONT_RENDER_DIR "/dpi" + +/* X servers sometimes lie about the screen's physical dimensions, so we cannot + * compute an accurate DPI value. When this happens, the user gets fonts that + * are too huge or too tiny. So, we see what the server returns: if it reports + * something outside of the range [DPI_LOW_REASONABLE_VALUE, + * DPI_HIGH_REASONABLE_VALUE], then we assume that it is lying and we use + * DPI_FALLBACK instead. + * + * See get_dpi_from_mateconf_or_server() below, and also + * https://bugzilla.novell.com/show_bug.cgi?id=217790 + */ +#define DPI_FALLBACK 96 +#define DPI_LOW_REASONABLE_VALUE 50 +#define DPI_HIGH_REASONABLE_VALUE 500 +#endif /* HAVE_XFT2 */ + +static gboolean in_change = FALSE; +static gchar* old_font = NULL; + +#define MAX_FONT_POINT_WITHOUT_WARNING 32 +#define MAX_FONT_SIZE_WITHOUT_WARNING MAX_FONT_POINT_WITHOUT_WARNING * 1024 + +#ifdef HAVE_XFT2 + +/* + * Code for displaying previews of font rendering with various Xft options + */ + +static void sample_size_request(GtkWidget* darea, GtkRequisition* requisition) +{ + GdkPixbuf* pixbuf = g_object_get_data(G_OBJECT(darea), "sample-pixbuf"); + + requisition->width = gdk_pixbuf_get_width(pixbuf) + 2; + requisition->height = gdk_pixbuf_get_height(pixbuf) + 2; +} + +static void sample_expose(GtkWidget* darea, GdkEventExpose* expose) +{ + GtkAllocation allocation; + GdkPixbuf* pixbuf = g_object_get_data(G_OBJECT(darea), "sample-pixbuf"); + GdkWindow* window = gtk_widget_get_window(darea); + GtkStyle* style = gtk_widget_get_style(darea); + int width = gdk_pixbuf_get_width(pixbuf); + int height = gdk_pixbuf_get_height(pixbuf); + + gtk_widget_get_allocation (darea, &allocation); + + int x = (allocation.width - width) / 2; + int y = (allocation.height - height) / 2; + + gdk_draw_rectangle(window, style->white_gc, TRUE, 0, 0, allocation.width, allocation.height); + gdk_draw_rectangle(window, style->black_gc, FALSE, 0, 0, allocation.width - 1, allocation.height - 1); + + gdk_draw_pixbuf(window, NULL, pixbuf, 0, 0, x, y, width, height, GDK_RGB_DITHER_NORMAL, 0, 0); +} + +typedef enum { + ANTIALIAS_NONE, + ANTIALIAS_GRAYSCALE, + ANTIALIAS_RGBA +} Antialiasing; + +static MateConfEnumStringPair antialias_enums[] = { + {ANTIALIAS_NONE, "none"}, + {ANTIALIAS_GRAYSCALE, "grayscale"}, + {ANTIALIAS_RGBA, "rgba"}, + {-1, NULL} +}; + +typedef enum { + HINT_NONE, + HINT_SLIGHT, + HINT_MEDIUM, + HINT_FULL +} Hinting; + +static MateConfEnumStringPair hint_enums[] = { + {HINT_NONE, "none"}, + {HINT_SLIGHT, "slight"}, + {HINT_MEDIUM, "medium"}, + {HINT_FULL, "full"}, + {-1, NULL} +}; + +typedef enum { + RGBA_RGB, + RGBA_BGR, + RGBA_VRGB, + RGBA_VBGR +} RgbaOrder; + +static MateConfEnumStringPair rgba_order_enums[] = { + {RGBA_RGB, "rgb" }, + {RGBA_BGR, "bgr" }, + {RGBA_VRGB, "vrgb" }, + {RGBA_VBGR, "vbgr" }, + {-1, NULL } +}; + +static XftFont* open_pattern(FcPattern* pattern, Antialiasing antialiasing, Hinting hinting) +{ + #ifdef FC_HINT_STYLE + static const int hintstyles[] = { + FC_HINT_NONE, FC_HINT_SLIGHT, FC_HINT_MEDIUM, FC_HINT_FULL + }; + #endif /* FC_HINT_STYLE */ + + FcPattern* res_pattern; + FcResult result; + XftFont* font; + + Display* xdisplay = gdk_x11_get_default_xdisplay(); + int screen = gdk_x11_get_default_screen(); + + res_pattern = XftFontMatch(xdisplay, screen, pattern, &result); + + if (res_pattern == NULL) + { + return NULL; + } + + FcPatternDel(res_pattern, FC_HINTING); + FcPatternAddBool(res_pattern, FC_HINTING, hinting != HINT_NONE); + + #ifdef FC_HINT_STYLE + FcPatternDel(res_pattern, FC_HINT_STYLE); + FcPatternAddInteger(res_pattern, FC_HINT_STYLE, hintstyles[hinting]); + #endif /* FC_HINT_STYLE */ + + FcPatternDel(res_pattern, FC_ANTIALIAS); + FcPatternAddBool(res_pattern, FC_ANTIALIAS, antialiasing != ANTIALIAS_NONE); + + FcPatternDel(res_pattern, FC_RGBA); + FcPatternAddInteger(res_pattern, FC_RGBA, antialiasing == ANTIALIAS_RGBA ? FC_RGBA_RGB : FC_RGBA_NONE); + + FcPatternDel(res_pattern, FC_DPI); + FcPatternAddInteger(res_pattern, FC_DPI, 96); + + font = XftFontOpenPattern(xdisplay, res_pattern); + + if (!font) + { + FcPatternDestroy(res_pattern); + } + + return font; +} + +static void setup_font_sample(GtkWidget* darea, Antialiasing antialiasing, Hinting hinting) +{ + const char* string1 = "abcfgop AO "; + const char* string2 = "abcfgop"; + + XftColor black, white; + XRenderColor rendcolor; + + Display* xdisplay = gdk_x11_get_default_xdisplay(); + + GdkColormap* colormap = gdk_rgb_get_colormap(); + Colormap xcolormap = GDK_COLORMAP_XCOLORMAP(colormap); + + GdkVisual* visual = gdk_colormap_get_visual(colormap); + Visual* xvisual = GDK_VISUAL_XVISUAL(visual); + + FcPattern* pattern; + XftFont* font1; + XftFont* font2; + XGlyphInfo extents1 = { 0 }; + XGlyphInfo extents2 = { 0 }; + GdkPixmap* pixmap; + XftDraw* draw; + GdkPixbuf* tmp_pixbuf; + GdkPixbuf* pixbuf; + + int width, height; + int ascent, descent; + + pattern = FcPatternBuild (NULL, + FC_FAMILY, FcTypeString, "Serif", + FC_SLANT, FcTypeInteger, FC_SLANT_ROMAN, + FC_SIZE, FcTypeDouble, 18., + NULL); + font1 = open_pattern (pattern, antialiasing, hinting); + FcPatternDestroy (pattern); + + pattern = FcPatternBuild (NULL, + FC_FAMILY, FcTypeString, "Serif", + FC_SLANT, FcTypeInteger, FC_SLANT_ITALIC, + FC_SIZE, FcTypeDouble, 20., + NULL); + font2 = open_pattern (pattern, antialiasing, hinting); + FcPatternDestroy (pattern); + + ascent = 0; + descent = 0; + + if (font1) + { + XftTextExtentsUtf8 (xdisplay, font1, (unsigned char*) string1, + strlen (string1), &extents1); + ascent = MAX (ascent, font1->ascent); + descent = MAX (descent, font1->descent); + } + + if (font2) + { + XftTextExtentsUtf8 (xdisplay, font2, (unsigned char*) string2, strlen (string2), &extents2); + ascent = MAX (ascent, font2->ascent); + descent = MAX (descent, font2->descent); + } + + width = extents1.xOff + extents2.xOff + 4; + height = ascent + descent + 2; + + pixmap = gdk_pixmap_new (NULL, width, height, visual->depth); + + draw = XftDrawCreate (xdisplay, GDK_DRAWABLE_XID (pixmap), xvisual, xcolormap); + + rendcolor.red = 0; + rendcolor.green = 0; + rendcolor.blue = 0; + rendcolor.alpha = 0xffff; + + XftColorAllocValue(xdisplay, xvisual, xcolormap, &rendcolor, &black); + + rendcolor.red = 0xffff; + rendcolor.green = 0xffff; + rendcolor.blue = 0xffff; + rendcolor.alpha = 0xffff; + + XftColorAllocValue(xdisplay, xvisual, xcolormap, &rendcolor, &white); + XftDrawRect(draw, &white, 0, 0, width, height); + + if (font1) + { + XftDrawStringUtf8(draw, &black, font1, 2, 2 + ascent, (unsigned char*) string1, strlen(string1)); + } + + if (font2) + { + XftDrawStringUtf8(draw, &black, font2, 2 + extents1.xOff, 2 + ascent, (unsigned char*) string2, strlen(string2)); + } + + XftDrawDestroy(draw); + + if (font1) + { + XftFontClose(xdisplay, font1); + } + + if (font2) + { + XftFontClose(xdisplay, font2); + } + + tmp_pixbuf = gdk_pixbuf_get_from_drawable(NULL, pixmap, colormap, 0, 0, 0, 0, width, height); + pixbuf = gdk_pixbuf_scale_simple(tmp_pixbuf, 1 * width, 1 * height, GDK_INTERP_TILES); + + g_object_unref(pixmap); + g_object_unref(tmp_pixbuf); + + g_object_set_data_full(G_OBJECT(darea), "sample-pixbuf", pixbuf, (GDestroyNotify) g_object_unref); + + g_signal_connect(darea, "size_request", G_CALLBACK(sample_size_request), NULL); + g_signal_connect(darea, "expose_event", G_CALLBACK(sample_expose), NULL); +} + +/* + * Code implementing a group of radio buttons with different Xft option combinations. + * If one of the buttons is matched by the MateConf key, we pick it. Otherwise we + * show the group as inconsistent. + */ +static void +font_render_get_mateconf (MateConfClient *client, + Antialiasing *antialiasing, + Hinting *hinting) +{ + gchar *antialias_str = mateconf_client_get_string (client, FONT_ANTIALIASING_KEY, NULL); + gchar *hint_str = mateconf_client_get_string (client, FONT_HINTING_KEY, NULL); + gint val; + + val = ANTIALIAS_GRAYSCALE; + if (antialias_str) { + mateconf_string_to_enum (antialias_enums, antialias_str, &val); + g_free (antialias_str); + } + *antialiasing = val; + + val = HINT_FULL; + if (hint_str) { + mateconf_string_to_enum (hint_enums, hint_str, &val); + g_free (hint_str); + } + *hinting = val; +} + +typedef struct { + Antialiasing antialiasing; + Hinting hinting; + GtkToggleButton *radio; +} FontPair; + +static GSList *font_pairs = NULL; + +static void +font_render_load (MateConfClient *client) +{ + Antialiasing antialiasing; + Hinting hinting; + gboolean inconsistent = TRUE; + GSList *tmp_list; + + font_render_get_mateconf (client, &antialiasing, &hinting); + + in_change = TRUE; + + for (tmp_list = font_pairs; tmp_list; tmp_list = tmp_list->next) { + FontPair *pair = tmp_list->data; + + if (antialiasing == pair->antialiasing && hinting == pair->hinting) { + gtk_toggle_button_set_active (pair->radio, TRUE); + inconsistent = FALSE; + break; + } + } + + for (tmp_list = font_pairs; tmp_list; tmp_list = tmp_list->next) { + FontPair *pair = tmp_list->data; + + gtk_toggle_button_set_inconsistent (pair->radio, inconsistent); + } + + in_change = FALSE; +} + +static void +font_render_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + font_render_load (client); +} + +static void +font_radio_toggled (GtkToggleButton *toggle_button, + FontPair *pair) +{ + if (!in_change) { + MateConfClient *client = mateconf_client_get_default (); + + mateconf_client_set_string (client, FONT_ANTIALIASING_KEY, + mateconf_enum_to_string (antialias_enums, pair->antialiasing), + NULL); + mateconf_client_set_string (client, FONT_HINTING_KEY, + mateconf_enum_to_string (hint_enums, pair->hinting), + NULL); + + /* Restore back to the previous state until we get notification */ + font_render_load (client); + g_object_unref (client); + } +} + +static void +setup_font_pair (GtkWidget *radio, + GtkWidget *darea, + Antialiasing antialiasing, + Hinting hinting) +{ + FontPair *pair = g_new (FontPair, 1); + + pair->antialiasing = antialiasing; + pair->hinting = hinting; + pair->radio = GTK_TOGGLE_BUTTON (radio); + + setup_font_sample (darea, antialiasing, hinting); + font_pairs = g_slist_prepend (font_pairs, pair); + + g_signal_connect (radio, "toggled", + G_CALLBACK (font_radio_toggled), pair); +} +#endif /* HAVE_XFT2 */ + +static void +marco_titlebar_load_sensitivity (AppearanceData *data) +{ + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "window_title_font"), + !mateconf_client_get_bool (data->client, + WINDOW_TITLE_USES_SYSTEM_KEY, + NULL)); +} + +static void +marco_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + marco_titlebar_load_sensitivity (user_data); +} + +/* returns 0 if the font is safe, otherwise returns the size in points. */ +static gint +font_dangerous (const char *font) +{ + PangoFontDescription *pfd; + gboolean retval = 0; + + pfd = pango_font_description_from_string (font); + if (pfd == NULL) + /* an invalid font was passed in. This isn't our problem. */ + return 0; + + if ((pango_font_description_get_set_fields (pfd) & PANGO_FONT_MASK_SIZE) && + (pango_font_description_get_size (pfd) >= MAX_FONT_SIZE_WITHOUT_WARNING)) { + retval = pango_font_description_get_size (pfd)/1024; + } + pango_font_description_free (pfd); + + return retval; +} + +static MateConfValue * +application_font_to_mateconf (MateConfPropertyEditor *peditor, + MateConfValue *value) +{ + MateConfValue *new_value; + const char *new_font; + GtkWidget *font_button; + gint danger_level; + + font_button = GTK_WIDGET (mateconf_property_editor_get_ui_control (peditor)); + g_return_val_if_fail (font_button != NULL, NULL); + + new_value = mateconf_value_new (MATECONF_VALUE_STRING); + new_font = mateconf_value_get_string (value); + if (font_dangerous (old_font)) { + /* If we're already too large, we don't warn again. */ + mateconf_value_set_string (new_value, new_font); + return new_value; + } + + danger_level = font_dangerous (new_font); + if (danger_level) { + GtkWidget *warning_dialog, *apply_button; + const gchar *warning_label; + gchar *warning_label2; + + warning_label = _("Font may be too large"); + + if (danger_level > MAX_FONT_POINT_WITHOUT_WARNING) { + warning_label2 = g_strdup_printf (ngettext ( + "The font selected is %d point large, " + "and may make it difficult to effectively " + "use the computer. It is recommended that " + "you select a size smaller than %d.", + "The font selected is %d points large, " + "and may make it difficult to effectively " + "use the computer. It is recommended that " + "you select a size smaller than %d.", + danger_level), + danger_level, + MAX_FONT_POINT_WITHOUT_WARNING); + } else { + warning_label2 = g_strdup_printf (ngettext ( + "The font selected is %d point large, " + "and may make it difficult to effectively " + "use the computer. It is recommended that " + "you select a smaller sized font.", + "The font selected is %d points large, " + "and may make it difficult to effectively " + "use the computer. It is recommended that " + "you select a smaller sized font.", + danger_level), + danger_level); + } + + warning_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, + "%s", + warning_label); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (warning_dialog), + "%s", warning_label2); + + gtk_dialog_add_button (GTK_DIALOG (warning_dialog), + _("Use previous font"), GTK_RESPONSE_CLOSE); + + apply_button = gtk_button_new_with_label (_("Use selected font")); + + gtk_button_set_image (GTK_BUTTON (apply_button), gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON)); + gtk_dialog_add_action_widget (GTK_DIALOG (warning_dialog), apply_button, GTK_RESPONSE_APPLY); + gtk_widget_set_can_default (apply_button, TRUE); + gtk_widget_show (apply_button); + + gtk_dialog_set_default_response (GTK_DIALOG (warning_dialog), GTK_RESPONSE_CLOSE); + + g_free (warning_label2); + + if (gtk_dialog_run (GTK_DIALOG (warning_dialog)) == GTK_RESPONSE_APPLY) { + mateconf_value_set_string (new_value, new_font); + } else { + mateconf_value_set_string (new_value, old_font); + gtk_font_button_set_font_name (GTK_FONT_BUTTON (font_button), old_font); + } + + gtk_widget_destroy (warning_dialog); + } else { + mateconf_value_set_string (new_value, new_font); + } + + return new_value; +} + +static void +application_font_changed (GtkWidget *font_button) +{ + const gchar *font; + + font = gtk_font_button_get_font_name (GTK_FONT_BUTTON (font_button)); + g_free (old_font); + old_font = g_strdup (font); +} + +#ifdef HAVE_XFT2 +/* + * EnumGroup - a group of radio buttons tied to a string enumeration + * value. We add this here because the mateconf peditor + * equivalent of this is both painful to use (you have + * to supply functions to convert from enums to indices) + * and conceptually broken (the order of radio buttons + * in a group when using Glade is not predictable. + */ +typedef struct +{ + MateConfClient *client; + GSList *items; + gchar *mateconf_key; + MateConfEnumStringPair *enums; + int default_value; +} EnumGroup; + +typedef struct +{ + EnumGroup *group; + GtkToggleButton *widget; + int value; +} EnumItem; + +static void +enum_group_load (EnumGroup *group) +{ + gchar *str = mateconf_client_get_string (group->client, group->mateconf_key, NULL); + gint val = group->default_value; + GSList *tmp_list; + + if (str) + mateconf_string_to_enum (group->enums, str, &val); + + g_free (str); + + in_change = TRUE; + + for (tmp_list = group->items; tmp_list; tmp_list = tmp_list->next) { + EnumItem *item = tmp_list->data; + + if (val == item->value) + gtk_toggle_button_set_active (item->widget, TRUE); + } + + in_change = FALSE; +} + +static void +enum_group_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + enum_group_load (user_data); +} + +static void +enum_item_toggled (GtkToggleButton *toggle_button, + EnumItem *item) +{ + EnumGroup *group = item->group; + + if (!in_change) { + mateconf_client_set_string (group->client, group->mateconf_key, + mateconf_enum_to_string (group->enums, item->value), + NULL); + } + + /* Restore back to the previous state until we get notification */ + enum_group_load (group); +} + +static EnumGroup * +enum_group_create (const gchar *mateconf_key, + MateConfEnumStringPair *enums, + int default_value, + GtkWidget *first_widget, + ...) +{ + EnumGroup *group; + GtkWidget *widget; + va_list args; + + group = g_new (EnumGroup, 1); + + group->client = mateconf_client_get_default (); + group->mateconf_key = g_strdup (mateconf_key); + group->enums = enums; + group->default_value = default_value; + group->items = NULL; + + va_start (args, first_widget); + + widget = first_widget; + while (widget) { + EnumItem *item; + + item = g_new (EnumItem, 1); + item->group = group; + item->widget = GTK_TOGGLE_BUTTON (widget); + item->value = va_arg (args, int); + + g_signal_connect (item->widget, "toggled", + G_CALLBACK (enum_item_toggled), item); + + group->items = g_slist_prepend (group->items, item); + + widget = va_arg (args, GtkWidget *); + } + + va_end (args); + + enum_group_load (group); + + mateconf_client_notify_add (group->client, mateconf_key, + enum_group_changed, + group, NULL, NULL); + + return group; +} + +static void +enum_group_destroy (EnumGroup *group) +{ + g_object_unref (group->client); + g_free (group->mateconf_key); + + g_slist_foreach (group->items, (GFunc) g_free, NULL); + g_slist_free (group->items); + + g_free (group); +} + +static double +dpi_from_pixels_and_mm (int pixels, int mm) +{ + double dpi; + + if (mm >= 1) + dpi = pixels / (mm / 25.4); + else + dpi = 0; + + return dpi; +} + +static double +get_dpi_from_x_server (void) +{ + GdkScreen *screen; + double dpi; + + screen = gdk_screen_get_default (); + if (screen) { + double width_dpi, height_dpi; + + width_dpi = dpi_from_pixels_and_mm (gdk_screen_get_width (screen), + gdk_screen_get_width_mm (screen)); + height_dpi = dpi_from_pixels_and_mm (gdk_screen_get_height (screen), + gdk_screen_get_height_mm (screen)); + + if (width_dpi < DPI_LOW_REASONABLE_VALUE || width_dpi > DPI_HIGH_REASONABLE_VALUE || + height_dpi < DPI_LOW_REASONABLE_VALUE || height_dpi > DPI_HIGH_REASONABLE_VALUE) + dpi = DPI_FALLBACK; + else + dpi = (width_dpi + height_dpi) / 2.0; + } else { + /* Huh!? No screen? */ + dpi = DPI_FALLBACK; + } + + return dpi; +} + +/* + * The font rendering details dialog + */ +static void +dpi_load (MateConfClient *client, + GtkSpinButton *spinner) +{ + MateConfValue *value; + gdouble dpi; + + value = mateconf_client_get_without_default (client, FONT_DPI_KEY, NULL); + + if (value) { + dpi = mateconf_value_get_float (value); + mateconf_value_free (value); + } else + dpi = get_dpi_from_x_server (); + + if (dpi < DPI_LOW_REASONABLE_VALUE) + dpi = DPI_LOW_REASONABLE_VALUE; + + in_change = TRUE; + gtk_spin_button_set_value (spinner, dpi); + in_change = FALSE; +} + +static void +dpi_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + dpi_load (client, user_data); +} + +static void +dpi_value_changed (GtkSpinButton *spinner, + MateConfClient *client) +{ + /* Like any time when using a spin button with MateConf, there is + * a race condition here. When we change, we send the new + * value to MateConf, then restore to the old value until + * we get a response to emulate the proper model/view behavior. + * + * If the user changes the value faster than responses are + * received from MateConf, this may cause mildly strange effects. + */ + if (!in_change) { + gdouble new_dpi = gtk_spin_button_get_value (spinner); + + mateconf_client_set_float (client, FONT_DPI_KEY, new_dpi, NULL); + + dpi_load (client, spinner); + } +} + +static void +cb_details_response (GtkDialog *dialog, gint response_id) +{ + if (response_id == GTK_RESPONSE_HELP) { + capplet_help (GTK_WINDOW (dialog), + "goscustdesk-38"); + } else + gtk_widget_hide (GTK_WIDGET (dialog)); +} + +static void +cb_show_details (GtkWidget *button, + AppearanceData *data) +{ + if (!data->font_details) { + GtkAdjustment *adjustment; + GtkWidget *widget; + EnumGroup *group; + + data->font_details = appearance_capplet_get_widget (data, "render_details"); + + gtk_window_set_transient_for (GTK_WINDOW (data->font_details), + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window"))); + + widget = appearance_capplet_get_widget (data, "dpi_spinner"); + + /* pick a sensible maximum dpi */ + adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget)); + gtk_adjustment_set_lower (adjustment, DPI_LOW_REASONABLE_VALUE); + gtk_adjustment_set_upper (adjustment, DPI_HIGH_REASONABLE_VALUE); + gtk_adjustment_set_step_increment (adjustment, 1); + + dpi_load (data->client, GTK_SPIN_BUTTON (widget)); + g_signal_connect (widget, "value_changed", + G_CALLBACK (dpi_value_changed), data->client); + + mateconf_client_notify_add (data->client, FONT_DPI_KEY, + dpi_changed, widget, NULL, NULL); + + setup_font_sample (appearance_capplet_get_widget (data, "antialias_none_sample"), ANTIALIAS_NONE, HINT_FULL); + setup_font_sample (appearance_capplet_get_widget (data, "antialias_grayscale_sample"), ANTIALIAS_GRAYSCALE, HINT_FULL); + setup_font_sample (appearance_capplet_get_widget (data, "antialias_subpixel_sample"), ANTIALIAS_RGBA, HINT_FULL); + + group = enum_group_create ( + FONT_ANTIALIASING_KEY, antialias_enums, ANTIALIAS_GRAYSCALE, + appearance_capplet_get_widget (data, "antialias_none_radio"), ANTIALIAS_NONE, + appearance_capplet_get_widget (data, "antialias_grayscale_radio"), ANTIALIAS_GRAYSCALE, + appearance_capplet_get_widget (data, "antialias_subpixel_radio"), ANTIALIAS_RGBA, + NULL); + data->font_groups = g_slist_prepend (data->font_groups, group); + + setup_font_sample (appearance_capplet_get_widget (data, "hint_none_sample"), ANTIALIAS_GRAYSCALE, HINT_NONE); + setup_font_sample (appearance_capplet_get_widget (data, "hint_slight_sample"), ANTIALIAS_GRAYSCALE, HINT_SLIGHT); + setup_font_sample (appearance_capplet_get_widget (data, "hint_medium_sample"), ANTIALIAS_GRAYSCALE, HINT_MEDIUM); + setup_font_sample (appearance_capplet_get_widget (data, "hint_full_sample"), ANTIALIAS_GRAYSCALE, HINT_FULL); + + group = enum_group_create (FONT_HINTING_KEY, hint_enums, HINT_FULL, + appearance_capplet_get_widget (data, "hint_none_radio"), HINT_NONE, + appearance_capplet_get_widget (data, "hint_slight_radio"), HINT_SLIGHT, + appearance_capplet_get_widget (data, "hint_medium_radio"), HINT_MEDIUM, + appearance_capplet_get_widget (data, "hint_full_radio"), HINT_FULL, + NULL); + data->font_groups = g_slist_prepend (data->font_groups, group); + + gtk_image_set_from_file (GTK_IMAGE (appearance_capplet_get_widget (data, "subpixel_rgb_image")), + MATECC_PIXMAP_DIR "/subpixel-rgb.png"); + gtk_image_set_from_file (GTK_IMAGE (appearance_capplet_get_widget (data, "subpixel_bgr_image")), + MATECC_PIXMAP_DIR "/subpixel-bgr.png"); + gtk_image_set_from_file (GTK_IMAGE (appearance_capplet_get_widget (data, "subpixel_vrgb_image")), + MATECC_PIXMAP_DIR "/subpixel-vrgb.png"); + gtk_image_set_from_file (GTK_IMAGE (appearance_capplet_get_widget (data, "subpixel_vbgr_image")), + MATECC_PIXMAP_DIR "/subpixel-vbgr.png"); + + group = enum_group_create (FONT_RGBA_ORDER_KEY, rgba_order_enums, RGBA_RGB, + appearance_capplet_get_widget (data, "subpixel_rgb_radio"), RGBA_RGB, + appearance_capplet_get_widget (data, "subpixel_bgr_radio"), RGBA_BGR, + appearance_capplet_get_widget (data, "subpixel_vrgb_radio"), RGBA_VRGB, + appearance_capplet_get_widget (data, "subpixel_vbgr_radio"), RGBA_VBGR, + NULL); + data->font_groups = g_slist_prepend (data->font_groups, group); + + g_signal_connect (G_OBJECT (data->font_details), + "response", + G_CALLBACK (cb_details_response), NULL); + g_signal_connect (G_OBJECT (data->font_details), + "delete_event", + G_CALLBACK (gtk_true), NULL); + } + + gtk_window_present (GTK_WINDOW (data->font_details)); +} +#endif /* HAVE_XFT2 */ + +void font_init(AppearanceData* data) +{ + GObject* peditor; + GtkWidget* widget; + + data->font_details = NULL; + data->font_groups = NULL; + + mateconf_client_add_dir(data->client, "/desktop/mate/interface", MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_add_dir(data->client, "/apps/caja/preferences", MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_add_dir(data->client, MARCO_DIR, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + + #ifdef HAVE_XFT2 + mateconf_client_add_dir(data->client, FONT_RENDER_DIR, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + #endif /* HAVE_XFT2 */ + + widget = appearance_capplet_get_widget(data, "application_font"); + peditor = mateconf_peditor_new_font(NULL, GTK_FONT_KEY, widget, "conv-from-widget-cb", application_font_to_mateconf, NULL); + g_signal_connect_swapped(peditor, "value-changed", G_CALLBACK (application_font_changed), widget); + application_font_changed(widget); + + peditor = mateconf_peditor_new_font(NULL, DOCUMENT_FONT_KEY, appearance_capplet_get_widget (data, "document_font"), NULL); + + peditor = mateconf_peditor_new_font(NULL, DESKTOP_FONT_KEY, appearance_capplet_get_widget (data, "desktop_font"), NULL); + + peditor = mateconf_peditor_new_font(NULL, WINDOW_TITLE_FONT_KEY, appearance_capplet_get_widget (data, "window_title_font"), NULL); + + peditor = mateconf_peditor_new_font(NULL, MONOSPACE_FONT_KEY, appearance_capplet_get_widget (data, "monospace_font"), NULL); + + mateconf_client_notify_add (data->client, WINDOW_TITLE_USES_SYSTEM_KEY, marco_changed, data, NULL, NULL); + + marco_titlebar_load_sensitivity(data); + + #ifdef HAVE_XFT2 + setup_font_pair(appearance_capplet_get_widget(data, "monochrome_radio"), appearance_capplet_get_widget (data, "monochrome_sample"), ANTIALIAS_NONE, HINT_FULL); + setup_font_pair(appearance_capplet_get_widget(data, "best_shapes_radio"), appearance_capplet_get_widget (data, "best_shapes_sample"), ANTIALIAS_GRAYSCALE, HINT_MEDIUM); + setup_font_pair(appearance_capplet_get_widget(data, "best_contrast_radio"), appearance_capplet_get_widget (data, "best_contrast_sample"), ANTIALIAS_GRAYSCALE, HINT_FULL); + setup_font_pair(appearance_capplet_get_widget(data, "subpixel_radio"), appearance_capplet_get_widget (data, "subpixel_sample"), ANTIALIAS_RGBA, HINT_FULL); + + font_render_load (data->client); + + mateconf_client_notify_add (data->client, FONT_RENDER_DIR, font_render_changed, data->client, NULL, NULL); + + g_signal_connect (appearance_capplet_get_widget (data, "details_button"), "clicked", G_CALLBACK (cb_show_details), data); + #else /* !HAVE_XFT2 */ + gtk_widget_hide (appearance_capplet_get_widget (data, "font_render_frame")); + #endif /* HAVE_XFT2 */ +} + +void font_shutdown(AppearanceData* data) +{ + g_slist_foreach(data->font_groups, (GFunc) enum_group_destroy, NULL); + g_slist_free(data->font_groups); + g_slist_foreach(font_pairs, (GFunc) g_free, NULL); + g_slist_free(font_pairs); + g_free(old_font); +} diff --git a/capplets/appearance/appearance-font.h b/capplets/appearance/appearance-font.h new file mode 100644 index 00000000..995aa328 --- /dev/null +++ b/capplets/appearance/appearance-font.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The GNOME Foundation + * Written by Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void font_init(AppearanceData* data); +void font_shutdown(AppearanceData* data); diff --git a/capplets/appearance/appearance-main.c b/capplets/appearance/appearance-main.c new file mode 100644 index 00000000..a7995105 --- /dev/null +++ b/capplets/appearance/appearance-main.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include "appearance.h" +#include "appearance-desktop.h" +#include "appearance-font.h" +#include "appearance-themes.h" +#include "appearance-style.h" +#include "theme-installer.h" +#include "theme-thumbnail.h" +#include "activate-settings-daemon.h" +#include "capplet-util.h" + +static AppearanceData * +init_appearance_data (int *argc, char ***argv, GOptionContext *context) +{ + AppearanceData *data = NULL; + gchar *uifile; + GtkBuilder *ui; + GError *err = NULL; + + g_thread_init (NULL); + gdk_threads_init (); + gdk_threads_enter (); + theme_thumbnail_factory_init (*argc, *argv); + capplet_init (context, argc, argv); + activate_settings_daemon (); + + /* set up the data */ + uifile = g_build_filename (MATECC_GTKBUILDER_DIR, "appearance.ui", + NULL); + ui = gtk_builder_new (); + gtk_builder_add_from_file (ui, uifile, &err); + g_free (uifile); + + if (err) + { + g_warning (_("Could not load user interface file: %s"), err->message); + g_error_free (err); + g_object_unref (ui); + } + else + { + data = g_new (AppearanceData, 1); + data->client = mateconf_client_get_default (); + data->ui = ui; + data->thumb_factory = mate_desktop_thumbnail_factory_new (MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL); + } + + return data; +} + +static void +main_window_response (GtkWidget *widget, + gint response_id, + AppearanceData *data) +{ + if (response_id == GTK_RESPONSE_CLOSE || + response_id == GTK_RESPONSE_DELETE_EVENT) + { + gtk_main_quit (); + + themes_shutdown (data); + style_shutdown (data); + desktop_shutdown (data); + font_shutdown (data); + + g_object_unref (data->thumb_factory); + g_object_unref (data->client); + g_object_unref (data->ui); + } + else if (response_id == GTK_RESPONSE_HELP) + { + GtkNotebook *nb; + gint pindex; + + nb = GTK_NOTEBOOK (appearance_capplet_get_widget (data, "main_notebook")); + pindex = gtk_notebook_get_current_page (nb); + + switch (pindex) + { + case 0: /* theme */ + capplet_help (GTK_WINDOW (widget), "goscustdesk-12"); + break; + case 1: /* background */ + capplet_help (GTK_WINDOW (widget), "goscustdesk-7"); + break; + case 2: /* fonts */ + capplet_help (GTK_WINDOW (widget), "goscustdesk-38"); + break; + case 3: /* interface */ + capplet_help (GTK_WINDOW (widget), "goscustuserinter-2"); + break; + default: + capplet_help (GTK_WINDOW (widget), "prefs-look-and-feel"); + break; + } + } +} + +int +main (int argc, char **argv) +{ + AppearanceData *data; + GtkWidget *w; + + gchar *install_filename = NULL; + gchar *start_page = NULL; + gchar **wallpaper_files = NULL; + GOptionContext *option_context; + GOptionEntry option_entries[] = { + { "install-theme", + 'i', + G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_FILENAME, + &install_filename, + N_("Specify the filename of a theme to install"), + N_("filename") }, + { "show-page", + 'p', + G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_STRING, + &start_page, + /* TRANSLATORS: don't translate the terms in brackets */ + N_("Specify the name of the page to show (theme|background|fonts|interface)"), + N_("page") }, + { G_OPTION_REMAINING, + 0, + G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_FILENAME_ARRAY, + &wallpaper_files, + NULL, + N_("[WALLPAPER...]") }, + { NULL } + }; + + option_context = g_option_context_new (NULL); + g_option_context_add_main_entries (option_context, option_entries, GETTEXT_PACKAGE); + + /* init */ + data = init_appearance_data (&argc, &argv, option_context); + if (!data) + return 1; + + /* init tabs */ + themes_init (data); + style_init (data); + desktop_init (data, (const gchar **) wallpaper_files); + g_strfreev (wallpaper_files); + font_init (data); + + /* prepare the main window */ + w = appearance_capplet_get_widget (data, "appearance_window"); + capplet_set_icon (w, "preferences-desktop-theme"); + gtk_widget_show_all (w); + + g_signal_connect_after (w, "response", + (GCallback) main_window_response, data); + + /* default to background page if files were given on the command line */ + if (wallpaper_files && !install_filename && !start_page) + start_page = g_strdup ("background"); + + if (start_page != NULL) { + gchar *page_name; + + page_name = g_strconcat (start_page, "_vbox", NULL); + g_free (start_page); + + w = appearance_capplet_get_widget (data, page_name); + if (w != NULL) { + GtkNotebook *nb; + gint pindex; + + nb = GTK_NOTEBOOK (appearance_capplet_get_widget (data, "main_notebook")); + pindex = gtk_notebook_page_num (nb, w); + if (pindex != -1) + gtk_notebook_set_current_page (nb, pindex); + } + g_free (page_name); + } + + if (install_filename != NULL) { + GFile *inst = g_file_new_for_commandline_arg (install_filename); + g_free (install_filename); + mate_theme_install (inst, GTK_WINDOW (w)); + g_object_unref (inst); + } + + g_option_context_free (option_context); + + /* start the mainloop */ + gtk_main (); + gdk_threads_leave (); + + /* free stuff */ + g_free (data); + + return 0; +} diff --git a/capplets/appearance/appearance-style.c b/capplets/appearance/appearance-style.c new file mode 100644 index 00000000..21612248 --- /dev/null +++ b/capplets/appearance/appearance-style.c @@ -0,0 +1,1073 @@ +/* + * Copyright (C) 2007, 2010 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "appearance.h" + +#include +#include +#include + +#include "theme-util.h" +#include "gtkrc-utils.h" +#include "mateconf-property-editor.h" +#include "theme-thumbnail.h" +#include "capplet-util.h" + +typedef void (* ThumbnailGenFunc) (void *type, + ThemeThumbnailFunc theme, + AppearanceData *data, + GDestroyNotify *destroy); + +typedef struct { + AppearanceData *data; + GdkPixbuf *thumbnail; +} PEditorConvData; + +static void update_message_area (AppearanceData *data); +static void create_thumbnail (const gchar *name, GdkPixbuf *default_thumb, AppearanceData *data); + +static const gchar *symbolic_names[NUM_SYMBOLIC_COLORS] = { + "fg_color", "bg_color", + "text_color", "base_color", + "selected_fg_color", "selected_bg_color", + "tooltip_fg_color", "tooltip_bg_color" +}; + +static gchar * +find_string_in_model (GtkTreeModel *model, const gchar *value, gint column) +{ + GtkTreeIter iter; + gboolean valid; + gchar *path = NULL, *test; + + if (!value) + return NULL; + + for (valid = gtk_tree_model_get_iter_first (model, &iter); valid; + valid = gtk_tree_model_iter_next (model, &iter)) + { + gtk_tree_model_get (model, &iter, column, &test, -1); + + if (test) + { + gint cmp = strcmp (test, value); + g_free (test); + + if (!cmp) + { + path = gtk_tree_model_get_string_from_iter (model, &iter); + break; + } + } + } + + return path; +} + +static MateConfValue * +conv_to_widget_cb (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + GtkTreeModel *store; + GtkTreeView *list; + const gchar *curr_value; + MateConfValue *new_value; + gchar *path; + + /* find value in model */ + curr_value = mateconf_value_get_string (value); + list = GTK_TREE_VIEW (mateconf_property_editor_get_ui_control (peditor)); + store = gtk_tree_view_get_model (list); + + path = find_string_in_model (store, curr_value, COL_NAME); + + /* Add a temporary item if we can't find a match + * TODO: delete this item if it is no longer selected? + */ + if (!path) + { + GtkListStore *list_store; + GtkTreeIter iter, sort_iter; + PEditorConvData *conv; + + list_store = GTK_LIST_STORE (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (store))); + + g_object_get (peditor, "data", &conv, NULL); + gtk_list_store_insert_with_values (list_store, &iter, 0, + COL_LABEL, curr_value, + COL_NAME, curr_value, + COL_THUMBNAIL, conv->thumbnail, + -1); + /* convert the tree store iter for use with the sort model */ + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (store), + &sort_iter, &iter); + path = gtk_tree_model_get_string_from_iter (store, &sort_iter); + + create_thumbnail (curr_value, conv->thumbnail, conv->data); + } + + new_value = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (new_value, path); + g_free (path); + + return new_value; +} + +static MateConfValue * +conv_from_widget_cb (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *new_value = NULL; + GtkTreeIter iter; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeView *list; + + list = GTK_TREE_VIEW (mateconf_property_editor_get_ui_control (peditor)); + selection = gtk_tree_view_get_selection (list); + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gchar *list_value; + + gtk_tree_model_get (model, &iter, COL_NAME, &list_value, -1); + + if (list_value) { + new_value = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (new_value, list_value); + g_free (list_value); + } + } + + return new_value; +} + +static gint +cursor_theme_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *a_label = NULL; + gchar *b_label = NULL; + const gchar *default_label; + gint result; + + gtk_tree_model_get (model, a, COL_LABEL, &a_label, -1); + gtk_tree_model_get (model, b, COL_LABEL, &b_label, -1); + + default_label = _("Default Pointer"); + + if (!strcmp (a_label, default_label)) + result = -1; + else if (!strcmp (b_label, default_label)) + result = 1; + else + result = strcmp (a_label, b_label); + + g_free (a_label); + g_free (b_label); + + return result; +} + +static void +style_message_area_response_cb (GtkWidget *w, + gint response_id, + AppearanceData *data) +{ + GtkSettings *settings = gtk_settings_get_default (); + gchar *theme; + gchar *engine_path; + + g_object_get (settings, "gtk-theme-name", &theme, NULL); + engine_path = gtk_theme_info_missing_engine (theme, FALSE); + g_free (theme); + + if (engine_path != NULL) { + theme_install_file (GTK_WINDOW (gtk_widget_get_toplevel (data->style_message_area)), + engine_path); + g_free (engine_path); + } + update_message_area (data); +} + +static void update_message_area(AppearanceData* data) +{ + GtkSettings* settings = gtk_settings_get_default(); + gchar* theme = NULL; + gchar* engine; + + g_object_get(settings, "gtk-theme-name", &theme, NULL); + engine = gtk_theme_info_missing_engine(theme, TRUE); + g_free(theme); + + if (data->style_message_area == NULL) + { + GtkWidget* hbox; + GtkWidget* parent; + GtkWidget* icon; + GtkWidget* content; + + if (engine == NULL) + { + return; + } + + data->style_message_area = gtk_info_bar_new (); + + g_signal_connect (data->style_message_area, "response", (GCallback) style_message_area_response_cb, data); + + data->style_install_button = gtk_info_bar_add_button(GTK_INFO_BAR (data->style_message_area), _("Install"), GTK_RESPONSE_APPLY); + + data->style_message_label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (data->style_message_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (data->style_message_label), 0.0, 0.5); + + hbox = gtk_hbox_new (FALSE, 9); + icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0); + gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), data->style_message_label, TRUE, TRUE, 0); + content = gtk_info_bar_get_content_area (GTK_INFO_BAR (data->style_message_area)); + gtk_container_add (GTK_CONTAINER (content), hbox); + gtk_widget_show_all (data->style_message_area); + gtk_widget_set_no_show_all (data->style_message_area, TRUE); + + parent = appearance_capplet_get_widget (data, "gtk_themes_vbox"); + gtk_box_pack_start (GTK_BOX (parent), data->style_message_area, FALSE, FALSE, 0); + } + + if (engine != NULL) + { + gchar* message = g_strdup_printf(_("This theme will not look as intended because the required GTK+ theme engine '%s' is not installed."), engine); + gtk_label_set_text(GTK_LABEL(data->style_message_label), message); + g_free(message); + g_free(engine); + + if (packagekit_available()) + { + gtk_widget_show(data->style_install_button); + } + else + { + gtk_widget_hide(data->style_install_button); + } + + gtk_widget_show(data->style_message_area); + gtk_widget_queue_draw(data->style_message_area); + } + else + { + gtk_widget_hide(data->style_message_area); + } +} + +static void +update_color_buttons_from_string (const gchar *color_scheme, AppearanceData *data) +{ + GdkColor colors[NUM_SYMBOLIC_COLORS]; + GtkWidget *widget; + gint i; + + if (!mate_theme_color_scheme_parse (color_scheme, colors)) + return; + + /* now set all the buttons to the correct settings */ + for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) { + widget = appearance_capplet_get_widget (data, symbolic_names[i]); + gtk_color_button_set_color (GTK_COLOR_BUTTON (widget), &colors[i]); + } +} + +static void +update_color_buttons_from_settings (GtkSettings *settings, + AppearanceData *data) +{ + gchar *scheme, *setting; + + scheme = mateconf_client_get_string (data->client, COLOR_SCHEME_KEY, NULL); + g_object_get (settings, "gtk-color-scheme", &setting, NULL); + + if (scheme == NULL || strcmp (scheme, "") == 0) + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), FALSE); + + g_free (scheme); + update_color_buttons_from_string (setting, data); + g_free (setting); +} + +static void +color_scheme_changed (GObject *settings, + GParamSpec *pspec, + AppearanceData *data) +{ + update_color_buttons_from_settings (GTK_SETTINGS (settings), data); +} + +static void +check_color_schemes_enabled (GtkSettings *settings, + AppearanceData *data) +{ + gchar *theme = NULL; + gchar *filename; + GSList *symbolic_colors = NULL; + gboolean enable_colors = FALSE; + gint i; + + g_object_get (settings, "gtk-theme-name", &theme, NULL); + filename = gtkrc_find_named (theme); + g_free (theme); + + gtkrc_get_details (filename, NULL, &symbolic_colors); + g_free (filename); + + for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) { + gboolean found; + + found = (g_slist_find_custom (symbolic_colors, symbolic_names[i], (GCompareFunc) strcmp) != NULL); + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, symbolic_names[i]), found); + + enable_colors |= found; + } + + g_slist_foreach (symbolic_colors, (GFunc) g_free, NULL); + g_slist_free (symbolic_colors); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_table"), enable_colors); + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), enable_colors); + + if (enable_colors) + gtk_widget_hide (appearance_capplet_get_widget (data, "color_scheme_message_hbox")); + else + gtk_widget_show (appearance_capplet_get_widget (data, "color_scheme_message_hbox")); +} + +static void +color_button_clicked_cb (GtkWidget *colorbutton, AppearanceData *data) +{ + GtkWidget *widget; + GdkColor color; + GString *scheme = g_string_new (NULL); + gchar *colstr; + gchar *old_scheme = NULL; + gint i; + + for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) { + widget = appearance_capplet_get_widget (data, symbolic_names[i]); + gtk_color_button_get_color (GTK_COLOR_BUTTON (widget), &color); + + colstr = gdk_color_to_string (&color); + g_string_append_printf (scheme, "%s:%s\n", symbolic_names[i], colstr); + g_free (colstr); + } + /* remove the last newline */ + g_string_truncate (scheme, scheme->len - 1); + + /* verify that the scheme really has changed */ + g_object_get (gtk_settings_get_default (), "gtk-color-scheme", &old_scheme, NULL); + + if (!mate_theme_color_scheme_equal (old_scheme, scheme->str)) { + mateconf_client_set_string (data->client, COLOR_SCHEME_KEY, scheme->str, NULL); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), TRUE); + } + g_free (old_scheme); + g_string_free (scheme, TRUE); +} + +static void +color_scheme_defaults_button_clicked_cb (GtkWidget *button, AppearanceData *data) +{ + mateconf_client_unset (data->client, COLOR_SCHEME_KEY, NULL); + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), FALSE); +} + +static void +style_response_cb (GtkDialog *dialog, gint response_id) +{ + if (response_id == GTK_RESPONSE_HELP) { + capplet_help (GTK_WINDOW (dialog), "goscustdesk-61"); + } else { + gtk_widget_hide (GTK_WIDGET (dialog)); + } +} + +static void +gtk_theme_changed (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + AppearanceData *data) +{ + MateThemeInfo *theme = NULL; + const gchar *name; + GtkSettings *settings = gtk_settings_get_default (); + + if (value && (name = mateconf_value_get_string (value))) { + gchar *current; + + theme = mate_theme_info_find (name); + + /* Manually update GtkSettings to new gtk+ theme. + * This will eventually happen anyway, but we need the + * info for the color scheme updates already. */ + g_object_get (settings, "gtk-theme-name", ¤t, NULL); + + if (strcmp (current, name) != 0) { + g_object_set (settings, "gtk-theme-name", name, NULL); + update_message_area (data); + } + + g_free (current); + + check_color_schemes_enabled (settings, data); + update_color_buttons_from_settings (settings, data); + } + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "gtk_themes_delete"), + theme_is_writable (theme)); +} + +static void +window_theme_changed (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + AppearanceData *data) +{ + MateThemeInfo *theme = NULL; + const gchar *name; + + if (value && (name = mateconf_value_get_string (value))) + theme = mate_theme_info_find (name); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "window_themes_delete"), + theme_is_writable (theme)); +} + +static void +icon_theme_changed (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + AppearanceData *data) +{ + MateThemeIconInfo *theme = NULL; + const gchar *name; + + if (value && (name = mateconf_value_get_string (value))) + theme = mate_theme_icon_info_find (name); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "icon_themes_delete"), + theme_is_writable (theme)); +} + +#ifdef HAVE_XCURSOR +static void +cursor_size_changed_cb (int size, AppearanceData *data) +{ + mateconf_client_set_int (data->client, CURSOR_SIZE_KEY, size, NULL); +} + +static void +cursor_size_scale_value_changed_cb (GtkRange *range, AppearanceData *data) +{ + MateThemeCursorInfo *theme; + gchar *name; + + name = mateconf_client_get_string (data->client, CURSOR_THEME_KEY, NULL); + if (name == NULL) + return; + + theme = mate_theme_cursor_info_find (name); + g_free (name); + + if (theme) { + gint size; + + size = g_array_index (theme->sizes, gint, (int) gtk_range_get_value (range)); + cursor_size_changed_cb (size, data); + } +} +#endif + +static void +update_cursor_size_scale (MateThemeCursorInfo *theme, + AppearanceData *data) +{ +#ifdef HAVE_XCURSOR + GtkWidget *cursor_size_scale; + GtkWidget *cursor_size_label; + GtkWidget *cursor_size_small_label; + GtkWidget *cursor_size_large_label; + gboolean sensitive; + gint size, mateconf_size; + + cursor_size_scale = appearance_capplet_get_widget (data, "cursor_size_scale"); + cursor_size_label = appearance_capplet_get_widget (data, "cursor_size_label"); + cursor_size_small_label = appearance_capplet_get_widget (data, "cursor_size_small_label"); + cursor_size_large_label = appearance_capplet_get_widget (data, "cursor_size_large_label"); + + sensitive = theme && theme->sizes->len > 1; + gtk_widget_set_sensitive (cursor_size_scale, sensitive); + gtk_widget_set_sensitive (cursor_size_label, sensitive); + gtk_widget_set_sensitive (cursor_size_small_label, sensitive); + gtk_widget_set_sensitive (cursor_size_large_label, sensitive); + + mateconf_size = mateconf_client_get_int (data->client, CURSOR_SIZE_KEY, NULL); + + if (sensitive) { + GtkAdjustment *adjustment; + gint i, index; + GtkRange *range = GTK_RANGE (cursor_size_scale); + + adjustment = gtk_range_get_adjustment (range); + g_object_set (adjustment, "upper", (gdouble) theme->sizes->len - 1, NULL); + + + /* fallback if the mateconf value is bigger than all available sizes; + use the largest we have */ + index = theme->sizes->len - 1; + + /* set the slider to the cursor size which matches the mateconf setting best */ + for (i = 0; i < theme->sizes->len; i++) { + size = g_array_index (theme->sizes, gint, i); + + if (size == mateconf_size) { + index = i; + break; + } else if (size > mateconf_size) { + if (i == 0) { + index = 0; + } else { + gint diff, diff_to_last; + + diff = size - mateconf_size; + diff_to_last = mateconf_size - g_array_index (theme->sizes, gint, i - 1); + + index = (diff < diff_to_last) ? i : i - 1; + } + break; + } + } + + gtk_range_set_value (range, (gdouble) index); + + size = g_array_index (theme->sizes, gint, index); + } else { + if (theme && theme->sizes->len > 0) + size = g_array_index (theme->sizes, gint, 0); + else + size = 18; + } + + if (size != mateconf_size) + cursor_size_changed_cb (size, data); +#endif +} + +static void +cursor_theme_changed (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + AppearanceData *data) +{ + MateThemeCursorInfo *theme = NULL; + const gchar *name; + + if (value && (name = mateconf_value_get_string (value))) + theme = mate_theme_cursor_info_find (name); + + update_cursor_size_scale (theme, data); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "cursor_themes_delete"), + theme_is_writable (theme)); + +} + +static void +generic_theme_delete (const gchar *tv_name, ThemeType type, AppearanceData *data) +{ + GtkTreeView *treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview); + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gchar *name; + + gtk_tree_model_get (model, &iter, COL_NAME, &name, -1); + + if (name != NULL && theme_delete (name, type)) { + /* remove theme from the model, too */ + GtkTreeIter child; + GtkTreePath *path; + + path = gtk_tree_model_get_path (model, &iter); + gtk_tree_model_sort_convert_iter_to_child_iter ( + GTK_TREE_MODEL_SORT (model), &child, &iter); + gtk_list_store_remove (GTK_LIST_STORE ( + gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model))), &child); + + if (gtk_tree_model_get_iter (model, &iter, path) || + theme_model_iter_last (model, &iter)) { + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (model, &iter); + gtk_tree_selection_select_path (selection, path); + gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0); + } + gtk_tree_path_free (path); + } + g_free (name); + } +} + +static void +gtk_theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + generic_theme_delete ("gtk_themes_list", THEME_TYPE_GTK, data); +} + +static void +window_theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + generic_theme_delete ("window_themes_list", THEME_TYPE_WINDOW, data); +} + +static void +icon_theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + generic_theme_delete ("icon_themes_list", THEME_TYPE_ICON, data); +} + +static void +cursor_theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + generic_theme_delete ("cursor_themes_list", THEME_TYPE_CURSOR, data); +} + +static void +add_to_treeview (const gchar *tv_name, + const gchar *theme_name, + const gchar *theme_label, + GdkPixbuf *theme_thumbnail, + AppearanceData *data) +{ + GtkTreeView *treeview; + GtkListStore *model; + + treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + model = GTK_LIST_STORE ( + gtk_tree_model_sort_get_model ( + GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (treeview)))); + + gtk_list_store_insert_with_values (model, NULL, 0, + COL_LABEL, theme_label, + COL_NAME, theme_name, + COL_THUMBNAIL, theme_thumbnail, + -1); +} + +static void +remove_from_treeview (const gchar *tv_name, + const gchar *theme_name, + AppearanceData *data) +{ + GtkTreeView *treeview; + GtkListStore *model; + GtkTreeIter iter; + + treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + model = GTK_LIST_STORE ( + gtk_tree_model_sort_get_model ( + GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (treeview)))); + + if (theme_find_in_model (GTK_TREE_MODEL (model), theme_name, &iter)) + gtk_list_store_remove (model, &iter); +} + +static void +update_in_treeview (const gchar *tv_name, + const gchar *theme_name, + const gchar *theme_label, + AppearanceData *data) +{ + GtkTreeView *treeview; + GtkListStore *model; + GtkTreeIter iter; + + treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + model = GTK_LIST_STORE ( + gtk_tree_model_sort_get_model ( + GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (treeview)))); + + if (theme_find_in_model (GTK_TREE_MODEL (model), theme_name, &iter)) { + gtk_list_store_set (model, &iter, + COL_LABEL, theme_label, + COL_NAME, theme_name, + -1); + } +} + +static void +update_thumbnail_in_treeview (const gchar *tv_name, + const gchar *theme_name, + GdkPixbuf *theme_thumbnail, + AppearanceData *data) +{ + GtkTreeView *treeview; + GtkListStore *model; + GtkTreeIter iter; + + if (theme_thumbnail == NULL) + return; + + treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + model = GTK_LIST_STORE ( + gtk_tree_model_sort_get_model ( + GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (treeview)))); + + if (theme_find_in_model (GTK_TREE_MODEL (model), theme_name, &iter)) { + gtk_list_store_set (model, &iter, + COL_THUMBNAIL, theme_thumbnail, + -1); + } +} + +static void +gtk_theme_thumbnail_cb (GdkPixbuf *pixbuf, + gchar *theme_name, + AppearanceData *data) +{ + update_thumbnail_in_treeview ("gtk_themes_list", theme_name, pixbuf, data); +} + +static void +marco_theme_thumbnail_cb (GdkPixbuf *pixbuf, + gchar *theme_name, + AppearanceData *data) +{ + update_thumbnail_in_treeview ("window_themes_list", theme_name, pixbuf, data); +} + +static void +icon_theme_thumbnail_cb (GdkPixbuf *pixbuf, + gchar *theme_name, + AppearanceData *data) +{ + update_thumbnail_in_treeview ("icon_themes_list", theme_name, pixbuf, data); +} + +static void +create_thumbnail (const gchar *name, GdkPixbuf *default_thumb, AppearanceData *data) +{ + if (default_thumb == data->icon_theme_icon) { + MateThemeIconInfo *info; + info = mate_theme_icon_info_find (name); + if (info != NULL) { + generate_icon_theme_thumbnail_async (info, + (ThemeThumbnailFunc) icon_theme_thumbnail_cb, data, NULL); + } + } else if (default_thumb == data->gtk_theme_icon) { + MateThemeInfo *info; + info = mate_theme_info_find (name); + if (info != NULL && info->has_gtk) { + generate_gtk_theme_thumbnail_async (info, + (ThemeThumbnailFunc) gtk_theme_thumbnail_cb, data, NULL); + } + } else if (default_thumb == data->window_theme_icon) { + MateThemeInfo *info; + info = mate_theme_info_find (name); + if (info != NULL && info->has_marco) { + generate_marco_theme_thumbnail_async (info, + (ThemeThumbnailFunc) marco_theme_thumbnail_cb, data, NULL); + } + } +} + +static void +changed_on_disk_cb (MateThemeCommonInfo *theme, + MateThemeChangeType change_type, + MateThemeElement element_type, + AppearanceData *data) +{ + if (theme->type == MATE_THEME_TYPE_REGULAR) { + MateThemeInfo *info = (MateThemeInfo *) theme; + + if (change_type == MATE_THEME_CHANGE_DELETED) { + if (element_type & MATE_THEME_GTK_2) + remove_from_treeview ("gtk_themes_list", info->name, data); + if (element_type & MATE_THEME_MARCO) + remove_from_treeview ("window_themes_list", info->name, data); + + } else { + if (element_type & MATE_THEME_GTK_2) { + if (change_type == MATE_THEME_CHANGE_CREATED) + add_to_treeview ("gtk_themes_list", info->name, info->name, data->gtk_theme_icon, data); + else if (change_type == MATE_THEME_CHANGE_CHANGED) + update_in_treeview ("gtk_themes_list", info->name, info->name, data); + + generate_gtk_theme_thumbnail_async (info, + (ThemeThumbnailFunc) gtk_theme_thumbnail_cb, data, NULL); + } + + if (element_type & MATE_THEME_MARCO) { + if (change_type == MATE_THEME_CHANGE_CREATED) + add_to_treeview ("window_themes_list", info->name, info->name, data->window_theme_icon, data); + else if (change_type == MATE_THEME_CHANGE_CHANGED) + update_in_treeview ("window_themes_list", info->name, info->name, data); + + generate_marco_theme_thumbnail_async (info, + (ThemeThumbnailFunc) marco_theme_thumbnail_cb, data, NULL); + } + } + + } else if (theme->type == MATE_THEME_TYPE_ICON) { + MateThemeIconInfo *info = (MateThemeIconInfo *) theme; + + if (change_type == MATE_THEME_CHANGE_DELETED) { + remove_from_treeview ("icon_themes_list", info->name, data); + } else { + if (change_type == MATE_THEME_CHANGE_CREATED) + add_to_treeview ("icon_themes_list", info->name, info->readable_name, data->icon_theme_icon, data); + else if (change_type == MATE_THEME_CHANGE_CHANGED) + update_in_treeview ("icon_themes_list", info->name, info->readable_name, data); + + generate_icon_theme_thumbnail_async (info, + (ThemeThumbnailFunc) icon_theme_thumbnail_cb, data, NULL); + } + + } else if (theme->type == MATE_THEME_TYPE_CURSOR) { + MateThemeCursorInfo *info = (MateThemeCursorInfo *) theme; + + if (change_type == MATE_THEME_CHANGE_DELETED) { + remove_from_treeview ("cursor_themes_list", info->name, data); + } else { + if (change_type == MATE_THEME_CHANGE_CREATED) + add_to_treeview ("cursor_themes_list", info->name, info->readable_name, info->thumbnail, data); + else if (change_type == MATE_THEME_CHANGE_CHANGED) + update_in_treeview ("cursor_themes_list", info->name, info->readable_name, data); + } + } +} + +static void +prepare_list (AppearanceData *data, GtkWidget *list, ThemeType type, GCallback callback) +{ + GtkListStore *store; + GList *l, *themes = NULL; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeModel *sort_model; + GdkPixbuf *thumbnail; + const gchar *key; + GObject *peditor; + MateConfValue *value; + ThumbnailGenFunc generator; + ThemeThumbnailFunc thumb_cb; + PEditorConvData *conv_data; + + switch (type) + { + case THEME_TYPE_GTK: + themes = mate_theme_info_find_by_type (MATE_THEME_GTK_2); + thumbnail = data->gtk_theme_icon; + key = GTK_THEME_KEY; + generator = (ThumbnailGenFunc) generate_gtk_theme_thumbnail_async; + thumb_cb = (ThemeThumbnailFunc) gtk_theme_thumbnail_cb; + break; + + case THEME_TYPE_WINDOW: + themes = mate_theme_info_find_by_type (MATE_THEME_MARCO); + thumbnail = data->window_theme_icon; + key = MARCO_THEME_KEY; + generator = (ThumbnailGenFunc) generate_marco_theme_thumbnail_async; + thumb_cb = (ThemeThumbnailFunc) marco_theme_thumbnail_cb; + break; + + case THEME_TYPE_ICON: + themes = mate_theme_icon_info_find_all (); + thumbnail = data->icon_theme_icon; + key = ICON_THEME_KEY; + generator = (ThumbnailGenFunc) generate_icon_theme_thumbnail_async; + thumb_cb = (ThemeThumbnailFunc) icon_theme_thumbnail_cb; + break; + + case THEME_TYPE_CURSOR: + themes = mate_theme_cursor_info_find_all (); + thumbnail = NULL; + key = CURSOR_THEME_KEY; + generator = NULL; + thumb_cb = NULL; + break; + + default: + /* we don't deal with any other type of themes here */ + return; + } + + store = gtk_list_store_new (NUM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + for (l = themes; l; l = g_list_next (l)) + { + MateThemeCommonInfo *theme = (MateThemeCommonInfo *) l->data; + GtkTreeIter i; + + if (type == THEME_TYPE_CURSOR) { + thumbnail = ((MateThemeCursorInfo *) theme)->thumbnail; + } else { + generator (theme, thumb_cb, data, NULL); + } + + gtk_list_store_insert_with_values (store, &i, 0, + COL_LABEL, theme->readable_name, + COL_NAME, theme->name, + COL_THUMBNAIL, thumbnail, + -1); + + if (type == THEME_TYPE_CURSOR && thumbnail) { + g_object_unref (thumbnail); + thumbnail = NULL; + } + } + g_list_free (themes); + + sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + COL_LABEL, GTK_SORT_ASCENDING); + + if (type == THEME_TYPE_CURSOR) + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model), COL_LABEL, + (GtkTreeIterCompareFunc) cursor_theme_sort_func, + NULL, NULL); + + gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (sort_model)); + + renderer = gtk_cell_renderer_pixbuf_new (); + g_object_set (renderer, "xpad", 3, "ypad", 3, NULL); + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_add_attribute (column, renderer, "pixbuf", COL_THUMBNAIL); + gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); + + renderer = gtk_cell_renderer_text_new (); + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_add_attribute (column, renderer, "text", COL_LABEL); + gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); + + conv_data = g_new (PEditorConvData, 1); + conv_data->data = data; + conv_data->thumbnail = thumbnail; + peditor = mateconf_peditor_new_tree_view (NULL, key, list, + "conv-to-widget-cb", conv_to_widget_cb, + "conv-from-widget-cb", conv_from_widget_cb, + "data", conv_data, + "data-free-cb", g_free, + NULL); + g_signal_connect (peditor, "value-changed", callback, data); + + /* init the delete buttons */ + value = mateconf_client_get (data->client, key, NULL); + (*((void (*) (MateConfPropertyEditor *, const gchar *, const MateConfValue *, gpointer)) callback)) + (MATECONF_PROPERTY_EDITOR (peditor), key, value, data); + if (value) + mateconf_value_free (value); +} + +void +style_init (AppearanceData *data) +{ + GtkSettings *settings; + GtkWidget *w; + gchar *label; + gint i; + + data->gtk_theme_icon = gdk_pixbuf_new_from_file (MATECC_PIXMAP_DIR "/gtk-theme-thumbnailing.png", NULL); + data->window_theme_icon = gdk_pixbuf_new_from_file (MATECC_PIXMAP_DIR "/window-theme-thumbnailing.png", NULL); + data->icon_theme_icon = gdk_pixbuf_new_from_file (MATECC_PIXMAP_DIR "/icon-theme-thumbnailing.png", NULL); + data->style_message_area = NULL; + data->style_message_label = NULL; + data->style_install_button = NULL; + + w = appearance_capplet_get_widget (data, "theme_details"); + g_signal_connect (w, "response", (GCallback) style_response_cb, NULL); + g_signal_connect (w, "delete_event", (GCallback) gtk_true, NULL); + + prepare_list (data, appearance_capplet_get_widget (data, "window_themes_list"), THEME_TYPE_WINDOW, (GCallback) window_theme_changed); + prepare_list (data, appearance_capplet_get_widget (data, "gtk_themes_list"), THEME_TYPE_GTK, (GCallback) gtk_theme_changed); + prepare_list (data, appearance_capplet_get_widget (data, "icon_themes_list"), THEME_TYPE_ICON, (GCallback) icon_theme_changed); + prepare_list (data, appearance_capplet_get_widget (data, "cursor_themes_list"), THEME_TYPE_CURSOR, (GCallback) cursor_theme_changed); + + w = appearance_capplet_get_widget (data, "color_scheme_message_hbox"); + gtk_widget_set_no_show_all (w, TRUE); + + w = appearance_capplet_get_widget (data, "color_scheme_defaults_button"); + gtk_button_set_image (GTK_BUTTON (w), + gtk_image_new_from_stock (GTK_STOCK_REVERT_TO_SAVED, + GTK_ICON_SIZE_BUTTON)); + + settings = gtk_settings_get_default (); + g_signal_connect (settings, "notify::gtk-color-scheme", (GCallback) color_scheme_changed, data); + +#ifdef HAVE_XCURSOR + w = appearance_capplet_get_widget (data, "cursor_size_scale"); + g_signal_connect (w, "value-changed", (GCallback) cursor_size_scale_value_changed_cb, data); + + w = appearance_capplet_get_widget (data, "cursor_size_small_label"); + label = g_strdup_printf ("%s", gtk_label_get_text (GTK_LABEL (w))); + gtk_label_set_markup (GTK_LABEL (w), label); + g_free (label); + + w = appearance_capplet_get_widget (data, "cursor_size_large_label"); + label = g_strdup_printf ("%s", gtk_label_get_text (GTK_LABEL (w))); + gtk_label_set_markup (GTK_LABEL (w), label); + g_free (label); +#else + w = appearance_capplet_get_widget (data, "cursor_size_hbox"); + gtk_widget_set_no_show_all (w, TRUE); + gtk_widget_hide (w); + gtk_widget_show (appearance_capplet_get_widget (data, "cursor_message_hbox")); + gtk_box_set_spacing (GTK_BOX (appearance_capplet_get_widget (data, "cursor_vbox")), 12); +#endif + + /* connect signals */ + /* color buttons */ + for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) + g_signal_connect (appearance_capplet_get_widget (data, symbolic_names[i]), "color-set", (GCallback) color_button_clicked_cb, data); + + /* revert button */ + g_signal_connect (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), "clicked", (GCallback) color_scheme_defaults_button_clicked_cb, data); + /* delete buttons */ + g_signal_connect (appearance_capplet_get_widget (data, "gtk_themes_delete"), "clicked", (GCallback) gtk_theme_delete_cb, data); + g_signal_connect (appearance_capplet_get_widget (data, "window_themes_delete"), "clicked", (GCallback) window_theme_delete_cb, data); + g_signal_connect (appearance_capplet_get_widget (data, "icon_themes_delete"), "clicked", (GCallback) icon_theme_delete_cb, data); + g_signal_connect (appearance_capplet_get_widget (data, "cursor_themes_delete"), "clicked", (GCallback) cursor_theme_delete_cb, data); + + update_message_area (data); + mate_theme_info_register_theme_change ((ThemeChangedCallback) changed_on_disk_cb, data); +} + +void +style_shutdown (AppearanceData *data) +{ + if (data->gtk_theme_icon) + g_object_unref (data->gtk_theme_icon); + if (data->window_theme_icon) + g_object_unref (data->window_theme_icon); + if (data->icon_theme_icon) + g_object_unref (data->icon_theme_icon); +} diff --git a/capplets/appearance/appearance-style.h b/capplets/appearance/appearance-style.h new file mode 100644 index 00000000..65203104 --- /dev/null +++ b/capplets/appearance/appearance-style.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void style_init (AppearanceData *data); +void style_shutdown (AppearanceData *data); diff --git a/capplets/appearance/appearance-themes.c b/capplets/appearance/appearance-themes.c new file mode 100644 index 00000000..132aa4b4 --- /dev/null +++ b/capplets/appearance/appearance-themes.c @@ -0,0 +1,1179 @@ +/* + * Copyright (C) 2007, 2010 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" +#include "theme-thumbnail.h" +#include "mate-theme-apply.h" +#include "theme-installer.h" +#include "theme-save.h" +#include "theme-util.h" +#include "gtkrc-utils.h" + +#include +#include +#include +#include + +#define CUSTOM_THEME_NAME "__custom__" + +enum { + RESPONSE_APPLY_BG, + RESPONSE_REVERT_FONT, + RESPONSE_APPLY_FONT, + RESPONSE_INSTALL_ENGINE +}; + +enum { + TARGET_URI_LIST, + TARGET_NS_URL +}; + +static const GtkTargetEntry drop_types[] = +{ + {"text/uri-list", 0, TARGET_URI_LIST}, + {"_NETSCAPE_URL", 0, TARGET_NS_URL} +}; + +static void theme_message_area_update(AppearanceData* data); + +static time_t theme_get_mtime(const char* name) +{ + MateThemeMetaInfo* theme; + time_t mtime = -1; + + theme = mate_theme_meta_info_find(name); + if (theme != NULL) + { + GFile* file; + GFileInfo* file_info; + + file = g_file_new_for_path(theme->path); + file_info = g_file_query_info(file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL); + g_object_unref(file); + + if (file_info != NULL) + { + mtime = g_file_info_get_attribute_uint64(file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED); + g_object_unref(file_info); + } + } + + return mtime; +} + +static void theme_thumbnail_update(GdkPixbuf* pixbuf, gchar* theme_name, AppearanceData* data, gboolean cache) +{ + GtkTreeIter iter; + GtkTreeModel* model = GTK_TREE_MODEL(data->theme_store); + + /* find item in model and update thumbnail */ + if (!pixbuf) + return; + + if (theme_find_in_model(model, theme_name, &iter)) + { + time_t mtime; + + gtk_list_store_set(data->theme_store, &iter, COL_THUMBNAIL, pixbuf, -1); + + /* cache thumbnail */ + if (cache && (mtime = theme_get_mtime(theme_name)) != -1) + { + gchar* path; + + /* try to share thumbs with caja, use themes:/// */ + path = g_strconcat("themes:///", theme_name, NULL); + + mate_desktop_thumbnail_factory_save_thumbnail(data->thumb_factory, pixbuf, path, mtime); + + g_free(path); + } + } +} + +static GdkPixbuf* theme_get_thumbnail_from_cache(MateThemeMetaInfo* info, AppearanceData* data) +{ + GdkPixbuf* thumb = NULL; + gchar* path, *thumb_filename; + time_t mtime; + + if (info == data->theme_custom) + return NULL; + + mtime = theme_get_mtime(info->name); + + if (mtime == -1) + return NULL; + + /* try to share thumbs with caja, use themes:/// */ + path = g_strconcat ("themes:///", info->name, NULL); + thumb_filename = mate_desktop_thumbnail_factory_lookup(data->thumb_factory, path, mtime); + g_free(path); + + if (thumb_filename != NULL) + { + thumb = gdk_pixbuf_new_from_file(thumb_filename, NULL); + g_free(thumb_filename); + } + + return thumb; +} + +static void +theme_thumbnail_done_cb (GdkPixbuf *pixbuf, gchar *theme_name, AppearanceData *data) +{ + theme_thumbnail_update (pixbuf, theme_name, data, TRUE); +} + +static void theme_thumbnail_generate(MateThemeMetaInfo* info, AppearanceData* data) +{ + GdkPixbuf* thumb = theme_get_thumbnail_from_cache(info, data); + + if (thumb != NULL) + { + theme_thumbnail_update(thumb, info->name, data, FALSE); + g_object_unref(thumb); + } + else + { + generate_meta_theme_thumbnail_async(info, (ThemeThumbnailFunc) theme_thumbnail_done_cb, data, NULL); + } +} + +static void theme_changed_on_disk_cb(MateThemeCommonInfo* theme, MateThemeChangeType change_type, MateThemeElement element_type, AppearanceData* data) +{ + if (theme->type == MATE_THEME_TYPE_METATHEME) + { + MateThemeMetaInfo* meta = (MateThemeMetaInfo*) theme; + + if (change_type == MATE_THEME_CHANGE_CREATED) + { + gtk_list_store_insert_with_values (data->theme_store, NULL, 0, COL_LABEL, meta->readable_name, COL_NAME, meta->name, COL_THUMBNAIL, data->theme_icon, -1); + theme_thumbnail_generate(meta, data); + } + else if (change_type == MATE_THEME_CHANGE_DELETED) + { + GtkTreeIter iter; + + if (theme_find_in_model(GTK_TREE_MODEL(data->theme_store), meta->name, &iter)) + { + gtk_list_store_remove(data->theme_store, &iter); + } + } + else if (change_type == MATE_THEME_CHANGE_CHANGED) + { + theme_thumbnail_generate(meta, data); + } + } +} + +static gchar* get_default_string_from_key(MateConfClient* client, const char* key) +{ + gchar* str = NULL; + + MateConfValue* value = mateconf_client_get_default_from_schema(client, key, NULL); + + if (value) + { + if (value->type == MATECONF_VALUE_STRING) + { + str = mateconf_value_to_string (value); + } + + mateconf_value_free (value); + } + + return str; +} + +/* Find out if the lockdown key has been set. + * Currently returns false on error... */ +static gboolean is_locked_down(MateConfClient* client) +{ + return mateconf_client_get_bool(client, LOCKDOWN_KEY, NULL); +} + +static MateThemeMetaInfo * +theme_load_from_mateconf (MateConfClient *client) +{ + MateThemeMetaInfo *theme; + gchar *scheme; + + theme = mate_theme_meta_info_new (); + + theme->gtk_theme_name = mateconf_client_get_string (client, GTK_THEME_KEY, NULL); + if (theme->gtk_theme_name == NULL) + theme->gtk_theme_name = g_strdup ("Clearlooks"); + + scheme = mateconf_client_get_string (client, COLOR_SCHEME_KEY, NULL); + if (scheme == NULL || !strcmp (scheme, "")) { + g_free (scheme); + scheme = gtkrc_get_color_scheme_for_theme (theme->gtk_theme_name); + } + theme->gtk_color_scheme = scheme; + + theme->marco_theme_name = mateconf_client_get_string (client, MARCO_THEME_KEY, NULL); + if (theme->marco_theme_name == NULL) + theme->marco_theme_name = g_strdup ("Clearlooks"); + + theme->icon_theme_name = mateconf_client_get_string (client, ICON_THEME_KEY, NULL); + if (theme->icon_theme_name == NULL) + theme->icon_theme_name = g_strdup ("mate"); + + theme->notification_theme_name = mateconf_client_get_string (client, NOTIFICATION_THEME_KEY, NULL); + + theme->cursor_theme_name = mateconf_client_get_string (client, CURSOR_THEME_KEY, NULL); +#ifdef HAVE_XCURSOR + theme->cursor_size = mateconf_client_get_int (client, CURSOR_SIZE_KEY, NULL); +#endif + if (theme->cursor_theme_name == NULL) + theme->cursor_theme_name = g_strdup ("default"); + + theme->application_font = mateconf_client_get_string (client, APPLICATION_FONT_KEY, NULL); + + return theme; +} + +static gchar * +theme_get_selected_name (GtkIconView *icon_view, AppearanceData *data) +{ + gchar *name = NULL; + GList *selected = gtk_icon_view_get_selected_items (icon_view); + + if (selected) { + GtkTreePath *path = selected->data; + GtkTreeModel *model = gtk_icon_view_get_model (icon_view); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter (model, &iter, path)) + gtk_tree_model_get (model, &iter, COL_NAME, &name, -1); + + g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected); + } + + return name; +} + +static const MateThemeMetaInfo * +theme_get_selected (GtkIconView *icon_view, AppearanceData *data) +{ + MateThemeMetaInfo *theme = NULL; + gchar *name = theme_get_selected_name (icon_view, data); + + if (name != NULL) { + if (!strcmp (name, data->theme_custom->name)) { + theme = data->theme_custom; + } else { + theme = mate_theme_meta_info_find (name); + } + + g_free (name); + } + + return theme; +} + +static void +theme_select_iter (GtkIconView *icon_view, GtkTreeIter *iter) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path (gtk_icon_view_get_model (icon_view), iter); + gtk_icon_view_select_path (icon_view, path); + gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.5, 0.0); + gtk_tree_path_free (path); +} + +static void +theme_select_name (GtkIconView *icon_view, const gchar *theme) +{ + GtkTreeIter iter; + GtkTreeModel *model = gtk_icon_view_get_model (icon_view); + + if (theme_find_in_model (model, theme, &iter)) + theme_select_iter (icon_view, &iter); +} + +static gboolean +theme_is_equal (const MateThemeMetaInfo *a, const MateThemeMetaInfo *b) +{ + gboolean a_set, b_set; + + if (!(a->gtk_theme_name && b->gtk_theme_name) || + strcmp (a->gtk_theme_name, b->gtk_theme_name)) + return FALSE; + + if (!(a->icon_theme_name && b->icon_theme_name) || + strcmp (a->icon_theme_name, b->icon_theme_name)) + return FALSE; + + if (!(a->marco_theme_name && b->marco_theme_name) || + strcmp (a->marco_theme_name, b->marco_theme_name)) + return FALSE; + + if (!(a->cursor_theme_name && b->cursor_theme_name) || + strcmp (a->cursor_theme_name, b->cursor_theme_name)) + return FALSE; + + if (a->cursor_size != b->cursor_size) + return FALSE; + + a_set = a->gtk_color_scheme && strcmp (a->gtk_color_scheme, ""); + b_set = b->gtk_color_scheme && strcmp (b->gtk_color_scheme, ""); + if ((a_set != b_set) || + (a_set && !mate_theme_color_scheme_equal (a->gtk_color_scheme, b->gtk_color_scheme))) + return FALSE; + + return TRUE; +} + +static void +theme_set_custom_from_theme (const MateThemeMetaInfo *info, AppearanceData *data) +{ + MateThemeMetaInfo *custom = data->theme_custom; + GtkIconView *icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")); + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + + if (info == custom) + return; + + /* if info is not NULL, we'll copy those theme settings over */ + if (info != NULL) { + g_free (custom->gtk_theme_name); + g_free (custom->icon_theme_name); + g_free (custom->marco_theme_name); + g_free (custom->gtk_color_scheme); + g_free (custom->cursor_theme_name); + g_free (custom->application_font); + custom->gtk_color_scheme = NULL; + custom->application_font = NULL; + + /* these settings are guaranteed to be non-NULL */ + custom->gtk_theme_name = g_strdup (info->gtk_theme_name); + custom->icon_theme_name = g_strdup (info->icon_theme_name); + custom->marco_theme_name = g_strdup (info->marco_theme_name); + custom->cursor_theme_name = g_strdup (info->cursor_theme_name); + custom->cursor_size = info->cursor_size; + + /* these can be NULL */ + if (info->gtk_color_scheme) + custom->gtk_color_scheme = g_strdup (info->gtk_color_scheme); + else + custom->gtk_color_scheme = get_default_string_from_key (data->client, COLOR_SCHEME_KEY); + + if (info->application_font) + custom->application_font = g_strdup (info->application_font); + else + custom->application_font = get_default_string_from_key (data->client, APPLICATION_FONT_KEY); + } + + /* select the custom theme */ + model = gtk_icon_view_get_model (icon_view); + if (!theme_find_in_model (model, custom->name, &iter)) { + GtkTreeIter child; + + gtk_list_store_insert_with_values (data->theme_store, &child, 0, + COL_LABEL, custom->readable_name, + COL_NAME, custom->name, + COL_THUMBNAIL, data->theme_icon, + -1); + gtk_tree_model_sort_convert_child_iter_to_iter ( + GTK_TREE_MODEL_SORT (model), &iter, &child); + } + + path = gtk_tree_model_get_path (model, &iter); + gtk_icon_view_select_path (icon_view, path); + gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.5, 0.0); + gtk_tree_path_free (path); + + /* update the theme thumbnail */ + theme_thumbnail_generate (custom, data); +} + +/** GUI Callbacks **/ + +static void custom_font_cb(GtkWidget* button, AppearanceData* data) +{ + g_free(data->revert_application_font); + g_free(data->revert_documents_font); + g_free(data->revert_desktop_font); + g_free(data->revert_windowtitle_font); + g_free(data->revert_monospace_font); + data->revert_application_font = NULL; + data->revert_documents_font = NULL; + data->revert_desktop_font = NULL; + data->revert_windowtitle_font = NULL; + data->revert_monospace_font = NULL; +} + +static void +theme_message_area_response_cb (GtkWidget *w, + gint response_id, + AppearanceData *data) +{ + const MateThemeMetaInfo *theme; + gchar *tmpfont; + gchar *engine_path; + + theme = theme_get_selected (GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")), data); + if (!theme) + return; + + switch (response_id) + { + case RESPONSE_APPLY_BG: + mateconf_client_set_string (data->client, BACKGROUND_KEY, + theme->background_image, NULL); + break; + + case RESPONSE_REVERT_FONT: + if (data->revert_application_font != NULL) { + mateconf_client_set_string (data->client, APPLICATION_FONT_KEY, + data->revert_application_font, NULL); + g_free (data->revert_application_font); + data->revert_application_font = NULL; + } + + if (data->revert_documents_font != NULL) { + mateconf_client_set_string (data->client, DOCUMENTS_FONT_KEY, + data->revert_documents_font, NULL); + g_free (data->revert_documents_font); + data->revert_documents_font = NULL; + } + + if (data->revert_desktop_font != NULL) { + mateconf_client_set_string (data->client, DESKTOP_FONT_KEY, + data->revert_desktop_font, NULL); + g_free (data->revert_desktop_font); + data->revert_desktop_font = NULL; + } + + if (data->revert_windowtitle_font != NULL) { + mateconf_client_set_string (data->client, WINDOWTITLE_FONT_KEY, + data->revert_windowtitle_font, NULL); + g_free (data->revert_windowtitle_font); + data->revert_windowtitle_font = NULL; + } + + if (data->revert_monospace_font != NULL) { + mateconf_client_set_string (data->client, MONOSPACE_FONT_KEY, + data->revert_monospace_font, NULL); + g_free (data->revert_monospace_font); + data->revert_monospace_font = NULL; + } + break; + + case RESPONSE_APPLY_FONT: + if (theme->application_font) { + tmpfont = mateconf_client_get_string (data->client, APPLICATION_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_application_font); + + if (strcmp (theme->application_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_application_font = NULL; + } else + data->revert_application_font = tmpfont; + } + mateconf_client_set_string (data->client, APPLICATION_FONT_KEY, + theme->application_font, NULL); + } + + if (theme->documents_font) { + tmpfont = mateconf_client_get_string (data->client, DOCUMENTS_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_documents_font); + + if (strcmp (theme->documents_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_documents_font = NULL; + } else + data->revert_documents_font = tmpfont; + } + mateconf_client_set_string (data->client, DOCUMENTS_FONT_KEY, + theme->documents_font, NULL); + } + + if (theme->desktop_font) { + tmpfont = mateconf_client_get_string (data->client, DESKTOP_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_desktop_font); + + if (strcmp (theme->desktop_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_desktop_font = NULL; + } else + data->revert_desktop_font = tmpfont; + } + mateconf_client_set_string (data->client, DESKTOP_FONT_KEY, + theme->desktop_font, NULL); + } + + if (theme->windowtitle_font) { + tmpfont = mateconf_client_get_string (data->client, WINDOWTITLE_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_windowtitle_font); + + if (strcmp (theme->windowtitle_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_windowtitle_font = NULL; + } else + data->revert_windowtitle_font = tmpfont; + } + mateconf_client_set_string (data->client, WINDOWTITLE_FONT_KEY, + theme->windowtitle_font, NULL); + } + + if (theme->monospace_font) { + tmpfont = mateconf_client_get_string (data->client, MONOSPACE_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_monospace_font); + + if (strcmp (theme->monospace_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_monospace_font = NULL; + } else + data->revert_monospace_font = tmpfont; + } + mateconf_client_set_string (data->client, MONOSPACE_FONT_KEY, + theme->monospace_font, NULL); + } + break; + + case RESPONSE_INSTALL_ENGINE: + + engine_path = gtk_theme_info_missing_engine(theme->gtk_theme_name, FALSE); + + if (engine_path != NULL) + { + theme_install_file(GTK_WINDOW(gtk_widget_get_toplevel(data->install_button)), engine_path); + g_free (engine_path); + } + + theme_message_area_update(data); + break; + } +} + +static void +theme_message_area_update (AppearanceData *data) +{ + const MateThemeMetaInfo *theme; + gboolean show_apply_background = FALSE; + gboolean show_apply_font = FALSE; + gboolean show_revert_font = FALSE; + gboolean show_error; + const gchar *message; + gchar *font; + GError *error = NULL; + + theme = theme_get_selected (GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")), data); + + if (!theme) { + if (data->theme_message_area != NULL) + gtk_widget_hide (data->theme_message_area); + return; + } + + show_error = !mate_theme_meta_info_validate (theme, &error); + + if (!show_error) { + if (theme->background_image != NULL) { + gchar *background; + + background = mateconf_client_get_string (data->client, BACKGROUND_KEY, NULL); + show_apply_background = + (!background || strcmp (theme->background_image, background) != 0); + g_free (background); + } + + if (theme->application_font) { + font = mateconf_client_get_string (data->client, APPLICATION_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + if (!show_apply_font && theme->documents_font) { + font = mateconf_client_get_string (data->client, DOCUMENTS_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + if (!show_apply_font && theme->desktop_font) { + font = mateconf_client_get_string (data->client, DESKTOP_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + if (!show_apply_font && theme->windowtitle_font) { + font = mateconf_client_get_string (data->client, WINDOWTITLE_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + if (!show_apply_font && theme->monospace_font) { + font = mateconf_client_get_string (data->client, MONOSPACE_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + show_revert_font = (data->revert_application_font != NULL || + data->revert_documents_font != NULL || data->revert_desktop_font != NULL || + data->revert_windowtitle_font != NULL || data->revert_monospace_font != NULL); + } + + if (data->theme_message_area == NULL) { + GtkWidget *hbox; + GtkWidget *parent; + GtkWidget *content; + + if (!show_apply_background && !show_revert_font && !show_apply_font && !show_error) + return; + + data->theme_message_area = gtk_info_bar_new (); + gtk_widget_set_no_show_all (data->theme_message_area, TRUE); + + g_signal_connect (data->theme_message_area, "response", + (GCallback) theme_message_area_response_cb, data); + + data->apply_background_button = gtk_info_bar_add_button ( + GTK_INFO_BAR (data->theme_message_area), + _("Apply Background"), + RESPONSE_APPLY_BG); + data->apply_font_button = gtk_info_bar_add_button ( + GTK_INFO_BAR (data->theme_message_area), + _("Apply Font"), + RESPONSE_APPLY_FONT); + data->revert_font_button = gtk_info_bar_add_button ( + GTK_INFO_BAR (data->theme_message_area), + _("Revert Font"), + RESPONSE_REVERT_FONT); + data->install_button = gtk_info_bar_add_button ( + GTK_INFO_BAR (data->theme_message_area), + _("Install"), + RESPONSE_INSTALL_ENGINE); + + data->theme_message_label = gtk_label_new (NULL); + gtk_widget_show (data->theme_message_label); + gtk_label_set_line_wrap (GTK_LABEL (data->theme_message_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (data->theme_message_label), 0.0, 0.5); + + hbox = gtk_hbox_new (FALSE, 9); + gtk_widget_show (hbox); + data->theme_info_icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); + data->theme_error_icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (data->theme_info_icon), 0.5, 0); + gtk_misc_set_alignment (GTK_MISC (data->theme_error_icon), 0.5, 0); + gtk_box_pack_start (GTK_BOX (hbox), data->theme_info_icon, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), data->theme_error_icon, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), data->theme_message_label, TRUE, TRUE, 0); + content = gtk_info_bar_get_content_area (GTK_INFO_BAR (data->theme_message_area)); + gtk_container_add (GTK_CONTAINER (content), hbox); + + parent = appearance_capplet_get_widget (data, "theme_list_vbox"); + gtk_box_pack_start (GTK_BOX (parent), data->theme_message_area, FALSE, FALSE, 0); + } + + if (show_error) + message = error->message; + else if (show_apply_background && show_apply_font && show_revert_font) + message = _("The current theme suggests a background and a font. Also, the last applied font suggestion can be reverted."); + else if (show_apply_background && show_revert_font) + message = _("The current theme suggests a background. Also, the last applied font suggestion can be reverted."); + else if (show_apply_background && show_apply_font) + message = _("The current theme suggests a background and a font."); + else if (show_apply_font && show_revert_font) + message = _("The current theme suggests a font. Also, the last applied font suggestion can be reverted."); + else if (show_apply_background) + message = _("The current theme suggests a background."); + else if (show_revert_font) + message = _("The last applied font suggestion can be reverted."); + else if (show_apply_font) + message = _("The current theme suggests a font."); + else + message = NULL; + + if (show_apply_background) + gtk_widget_show (data->apply_background_button); + else + gtk_widget_hide (data->apply_background_button); + + if (show_apply_font) + gtk_widget_show (data->apply_font_button); + else + gtk_widget_hide (data->apply_font_button); + + if (show_revert_font) + gtk_widget_show (data->revert_font_button); + else + gtk_widget_hide (data->revert_font_button); + + if (show_error + && g_error_matches (error, MATE_THEME_ERROR, MATE_THEME_ERROR_GTK_ENGINE_NOT_AVAILABLE) + && packagekit_available ()) + gtk_widget_show (data->install_button); + else + gtk_widget_hide (data->install_button); + + if (show_error || show_apply_background || show_apply_font || show_revert_font) { + gtk_widget_show (data->theme_message_area); + gtk_widget_queue_draw (data->theme_message_area); + + if (show_error) { + gtk_widget_show (data->theme_error_icon); + gtk_widget_hide (data->theme_info_icon); + } else { + gtk_widget_show (data->theme_info_icon); + gtk_widget_hide (data->theme_error_icon); + } + } else { + gtk_widget_hide (data->theme_message_area); + } + + gtk_label_set_text (GTK_LABEL (data->theme_message_label), message); + g_clear_error (&error); +} + +static void +theme_selection_changed_cb (GtkWidget *icon_view, AppearanceData *data) +{ + GList *selection; + MateThemeMetaInfo *theme = NULL; + gboolean is_custom = FALSE; + + selection = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view)); + + if (selection) { + GtkTreeModel *model; + GtkTreeIter iter; + gchar *name; + + model = gtk_icon_view_get_model (GTK_ICON_VIEW (icon_view)); + gtk_tree_model_get_iter (model, &iter, selection->data); + gtk_tree_model_get (model, &iter, COL_NAME, &name, -1); + + is_custom = !strcmp (name, CUSTOM_THEME_NAME); + + if (is_custom) + theme = data->theme_custom; + else + theme = mate_theme_meta_info_find (name); + + if (theme) { + mate_meta_theme_set (theme); + theme_message_area_update (data); + } + + g_free (name); + g_list_foreach (selection, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selection); + } + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "theme_delete"), + theme_is_writable (theme)); + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "theme_save"), is_custom); +} + +static void +theme_custom_cb (GtkWidget *button, AppearanceData *data) +{ + GtkWidget *w, *parent; + + w = appearance_capplet_get_widget (data, "theme_details"); + parent = appearance_capplet_get_widget (data, "appearance_window"); + gtk_window_set_transient_for (GTK_WINDOW (w), GTK_WINDOW (parent)); + gtk_widget_show_all (w); +} + +static void +theme_save_cb (GtkWidget *button, AppearanceData *data) +{ + theme_save_dialog_run (data->theme_custom, data); +} + +static void +theme_install_cb (GtkWidget *button, AppearanceData *data) +{ + mate_theme_installer_run ( + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window")), NULL); +} + +static void +theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")); + GList *selected = gtk_icon_view_get_selected_items (icon_view); + + if (selected) { + GtkTreePath *path = selected->data; + GtkTreeModel *model = gtk_icon_view_get_model (icon_view); + GtkTreeIter iter; + gchar *name = NULL; + + if (gtk_tree_model_get_iter (model, &iter, path)) + gtk_tree_model_get (model, &iter, COL_NAME, &name, -1); + + if (name != NULL && + strcmp (name, data->theme_custom->name) && + theme_delete (name, THEME_TYPE_META)) { + /* remove theme from the model, too */ + GtkTreeIter child; + + if (gtk_tree_model_iter_next (model, &iter) || + theme_model_iter_last (model, &iter)) + theme_select_iter (icon_view, &iter); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_sort_convert_iter_to_child_iter ( + GTK_TREE_MODEL_SORT (model), &child, &iter); + gtk_list_store_remove (data->theme_store, &child); + } + + g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected); + g_free (name); + } +} + +static void +theme_details_changed_cb (AppearanceData *data) +{ + MateThemeMetaInfo *mateconf_theme; + const MateThemeMetaInfo *selected; + GtkIconView *icon_view; + gboolean done = FALSE; + + /* load new state from mateconf */ + mateconf_theme = theme_load_from_mateconf (data->client); + + /* check if it's our currently selected theme */ + icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")); + selected = theme_get_selected (icon_view, data); + + if (!selected || !(done = theme_is_equal (selected, mateconf_theme))) { + /* look for a matching metatheme */ + GList *theme_list, *l; + + theme_list = mate_theme_meta_info_find_all (); + + for (l = theme_list; l; l = l->next) { + MateThemeMetaInfo *info = l->data; + + if (theme_is_equal (mateconf_theme, info)) { + theme_select_name (icon_view, info->name); + done = TRUE; + break; + } + } + g_list_free (theme_list); + } + + if (!done) + /* didn't find a match, set or update custom */ + theme_set_custom_from_theme (mateconf_theme, data); + + mate_theme_meta_info_free (mateconf_theme); +} + +static void +theme_setting_changed_cb (GObject *settings, + GParamSpec *pspec, + AppearanceData *data) +{ + theme_details_changed_cb (data); +} + +static void +theme_mateconf_changed (MateConfClient *client, + guint conn_id, + MateConfEntry *entry, + AppearanceData *data) +{ + theme_details_changed_cb (data); +} + +static gint +theme_list_sort_func (MateThemeMetaInfo *a, + MateThemeMetaInfo *b) +{ + return strcmp (a->readable_name, b->readable_name); +} + +static gint +theme_store_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *a_name, *a_label; + gint rc; + + gtk_tree_model_get (model, a, COL_NAME, &a_name, COL_LABEL, &a_label, -1); + + if (!strcmp (a_name, CUSTOM_THEME_NAME)) { + rc = -1; + } else { + gchar *b_name, *b_label; + + gtk_tree_model_get (model, b, COL_NAME, &b_name, COL_LABEL, &b_label, -1); + + if (!strcmp (b_name, CUSTOM_THEME_NAME)) { + rc = 1; + } else { + gchar *a_case, *b_case; + + a_case = g_utf8_casefold (a_label, -1); + b_case = g_utf8_casefold (b_label, -1); + rc = g_utf8_collate (a_case, b_case); + g_free (a_case); + g_free (b_case); + } + + g_free (b_name); + g_free (b_label); + } + + g_free (a_name); + g_free (a_label); + + return rc; +} + +static void +theme_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, + AppearanceData *data) +{ + gchar **uris; + + if (!(info == TARGET_URI_LIST || info == TARGET_NS_URL)) + return; + + uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data)); + + if (uris != NULL && uris[0] != NULL) { + GFile *f = g_file_new_for_uri (uris[0]); + + mate_theme_install (f, + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window"))); + g_object_unref (f); + } + + g_strfreev (uris); +} + +static void background_or_font_changed(MateConfEngine* conf, guint cnxn_id, MateConfEntry* entry, AppearanceData* data) +{ + theme_message_area_update(data); +} + +void themes_init(AppearanceData* data) +{ + GtkWidget *w, *del_button; + GList *theme_list, *l; + GtkListStore *theme_store; + GtkTreeModel *sort_model; + MateThemeMetaInfo *meta_theme = NULL; + GtkIconView *icon_view; + GtkCellRenderer *renderer; + GtkSettings *settings; + char *url; + + /* initialise some stuff */ + mate_theme_init (); + mate_wm_manager_init (); + + data->revert_application_font = NULL; + data->revert_documents_font = NULL; + data->revert_desktop_font = NULL; + data->revert_windowtitle_font = NULL; + data->revert_monospace_font = NULL; + data->theme_save_dialog = NULL; + data->theme_message_area = NULL; + data->theme_info_icon = NULL; + data->theme_error_icon = NULL; + data->theme_custom = mate_theme_meta_info_new (); + data->theme_icon = gdk_pixbuf_new_from_file (MATECC_PIXMAP_DIR "/theme-thumbnailing.png", NULL); + data->theme_store = theme_store = + gtk_list_store_new (NUM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + /* set up theme list */ + theme_list = mate_theme_meta_info_find_all (); + mate_theme_info_register_theme_change ((ThemeChangedCallback) theme_changed_on_disk_cb, data); + + data->theme_custom = theme_load_from_mateconf (data->client); + data->theme_custom->name = g_strdup (CUSTOM_THEME_NAME); + data->theme_custom->readable_name = g_strdup_printf ("%s", _("Custom")); + + for (l = theme_list; l; l = l->next) { + MateThemeMetaInfo *info = l->data; + + gtk_list_store_insert_with_values (theme_store, NULL, 0, + COL_LABEL, info->readable_name, + COL_NAME, info->name, + COL_THUMBNAIL, data->theme_icon, + -1); + + if (!meta_theme && theme_is_equal (data->theme_custom, info)) + meta_theme = info; + } + + if (!meta_theme) { + /* add custom theme */ + meta_theme = data->theme_custom; + + gtk_list_store_insert_with_values (theme_store, NULL, 0, + COL_LABEL, meta_theme->readable_name, + COL_NAME, meta_theme->name, + COL_THUMBNAIL, data->theme_icon, + -1); + + theme_thumbnail_generate (meta_theme, data); + } + + theme_list = g_list_sort (theme_list, (GCompareFunc) theme_list_sort_func); + + g_list_foreach (theme_list, (GFunc) theme_thumbnail_generate, data); + g_list_free (theme_list); + + icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")); + + renderer = gtk_cell_renderer_pixbuf_new (); + g_object_set (renderer, "xpad", 5, "ypad", 5, + "xalign", 0.5, "yalign", 1.0, NULL); + gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), renderer, + "pixbuf", COL_THUMBNAIL, NULL); + + renderer = gtk_cell_renderer_text_new (); + g_object_set (renderer, "alignment", PANGO_ALIGN_CENTER, + "wrap-mode", PANGO_WRAP_WORD_CHAR, + "wrap-width", gtk_icon_view_get_item_width (icon_view), + "width", gtk_icon_view_get_item_width (icon_view), + "xalign", 0.0, "yalign", 0.0, NULL); + gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), renderer, + "markup", COL_LABEL, NULL); + + sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (theme_store)); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model), COL_LABEL, theme_store_sort_func, NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), COL_LABEL, GTK_SORT_ASCENDING); + gtk_icon_view_set_model (icon_view, GTK_TREE_MODEL (sort_model)); + + g_signal_connect (icon_view, "selection-changed", (GCallback) theme_selection_changed_cb, data); + g_signal_connect_after (icon_view, "realize", (GCallback) theme_select_name, meta_theme->name); + + w = appearance_capplet_get_widget (data, "theme_install"); + gtk_button_set_image (GTK_BUTTON (w), + gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON)); + g_signal_connect (w, "clicked", (GCallback) theme_install_cb, data); + + w = appearance_capplet_get_widget (data, "theme_save"); + gtk_button_set_image (GTK_BUTTON (w), + gtk_image_new_from_stock (GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_BUTTON)); + g_signal_connect (w, "clicked", (GCallback) theme_save_cb, data); + + w = appearance_capplet_get_widget (data, "theme_custom"); + gtk_button_set_image (GTK_BUTTON (w), + gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON)); + g_signal_connect (w, "clicked", (GCallback) theme_custom_cb, data); + + del_button = appearance_capplet_get_widget (data, "theme_delete"); + g_signal_connect (del_button, "clicked", (GCallback) theme_delete_cb, data); + + w = appearance_capplet_get_widget (data, "theme_vbox"); + gtk_drag_dest_set (w, GTK_DEST_DEFAULT_ALL, + drop_types, G_N_ELEMENTS (drop_types), + GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE); + g_signal_connect (w, "drag-data-received", (GCallback) theme_drag_data_received_cb, data); + if (is_locked_down (data->client)) + gtk_widget_set_sensitive (w, FALSE); + + w = appearance_capplet_get_widget (data, "more_themes_linkbutton"); + url = mateconf_client_get_string (data->client, MORE_THEMES_URL_KEY, NULL); + if (url != NULL && url[0] != '\0') { + gtk_link_button_set_uri (GTK_LINK_BUTTON (w), url); + gtk_widget_show (w); + } else { + gtk_widget_hide (w); + } + g_free (url); + + /* listen to mateconf changes, too */ + mateconf_client_add_dir (data->client, "/apps/marco/general", MATECONF_CLIENT_PRELOAD_NONE, NULL); + mateconf_client_add_dir (data->client, "/desktop/mate/interface", MATECONF_CLIENT_PRELOAD_NONE, NULL); + mateconf_client_notify_add (data->client, MARCO_THEME_KEY, (MateConfClientNotifyFunc) theme_mateconf_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, CURSOR_THEME_KEY, (MateConfClientNotifyFunc) theme_mateconf_changed, data, NULL, NULL); +#ifdef HAVE_XCURSOR + mateconf_client_notify_add (data->client, CURSOR_SIZE_KEY, (MateConfClientNotifyFunc) theme_mateconf_changed, data, NULL, NULL); +#endif + mateconf_client_notify_add (data->client, BACKGROUND_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, APPLICATION_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, DOCUMENTS_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, DESKTOP_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, WINDOWTITLE_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, MONOSPACE_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + + settings = gtk_settings_get_default (); + g_signal_connect (settings, "notify::gtk-color-scheme", (GCallback) theme_setting_changed_cb, data); + g_signal_connect (settings, "notify::gtk-theme-name", (GCallback) theme_setting_changed_cb, data); + g_signal_connect (settings, "notify::gtk-icon-theme-name", (GCallback) theme_setting_changed_cb, data); + + /* monitor individual font choice buttons, so + "revert font" option (if any) can be cleared */ + w = appearance_capplet_get_widget (data, "application_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); + w = appearance_capplet_get_widget (data, "document_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); + w = appearance_capplet_get_widget (data, "desktop_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); + w = appearance_capplet_get_widget (data, "window_title_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); + w = appearance_capplet_get_widget (data, "monospace_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); +} + +void +themes_shutdown (AppearanceData *data) +{ + mate_theme_meta_info_free (data->theme_custom); + + if (data->theme_icon) + g_object_unref (data->theme_icon); + if (data->theme_save_dialog) + gtk_widget_destroy (data->theme_save_dialog); + g_free (data->revert_application_font); + g_free (data->revert_documents_font); + g_free (data->revert_desktop_font); + g_free (data->revert_windowtitle_font); + g_free (data->revert_monospace_font); +} diff --git a/capplets/appearance/appearance-themes.h b/capplets/appearance/appearance-themes.h new file mode 100644 index 00000000..6d1c59ff --- /dev/null +++ b/capplets/appearance/appearance-themes.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void themes_init(AppearanceData* data); +void themes_shutdown(AppearanceData* data); diff --git a/capplets/appearance/appearance.h b/capplets/appearance/appearance.h new file mode 100644 index 00000000..3658ea0f --- /dev/null +++ b/capplets/appearance/appearance.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "mate-theme-info.h" + +#define APPEARANCE_KEY_DIR "/apps/control-center/appearance" +#define MORE_THEMES_URL_KEY APPEARANCE_KEY_DIR "/more_themes_url" +#define MORE_BACKGROUNDS_URL_KEY APPEARANCE_KEY_DIR "/more_backgrounds_url" + +typedef struct { + MateConfClient* client; + GtkBuilder* ui; + MateDesktopThumbnailFactory* thumb_factory; + gulong screen_size_handler; + gulong screen_monitors_handler; + + /* desktop */ + GHashTable* wp_hash; + gboolean wp_update_mateconf; + GtkIconView* wp_view; + GtkTreeModel* wp_model; + GtkWidget* wp_scpicker; + GtkWidget* wp_pcpicker; + GtkWidget* wp_style_menu; + GtkWidget* wp_color_menu; + GtkWidget* wp_rem_button; + GtkFileChooser* wp_filesel; + GtkWidget* wp_image; + GSList* wp_uris; + gint frame; + gint thumb_width; + gint thumb_height; + + /* font */ + GtkWidget* font_details; + GSList* font_groups; + + /* themes */ + GtkListStore* theme_store; + MateThemeMetaInfo* theme_custom; + GdkPixbuf* theme_icon; + GtkWidget* theme_save_dialog; + GtkWidget* theme_message_area; + GtkWidget* theme_message_label; + GtkWidget* apply_background_button; + GtkWidget* revert_font_button; + GtkWidget* apply_font_button; + GtkWidget* install_button; + GtkWidget* theme_info_icon; + GtkWidget* theme_error_icon; + gchar* revert_application_font; + gchar* revert_documents_font; + gchar* revert_desktop_font; + gchar* revert_windowtitle_font; + gchar* revert_monospace_font; + + /* style */ + GdkPixbuf* gtk_theme_icon; + GdkPixbuf* window_theme_icon; + GdkPixbuf* icon_theme_icon; + GtkWidget* style_message_area; + GtkWidget* style_message_label; + GtkWidget* style_install_button; +} AppearanceData; + +#define appearance_capplet_get_widget(x, y) (GtkWidget*) gtk_builder_get_object(x->ui, y) diff --git a/capplets/appearance/data/Makefile.am b/capplets/appearance/data/Makefile.am new file mode 100644 index 00000000..987213fb --- /dev/null +++ b/capplets/appearance/data/Makefile.am @@ -0,0 +1,62 @@ + +gtkbuilderdir = $(pkgdatadir)/ui +dist_gtkbuilder_DATA = appearance.ui + +pixmapdir = $(pkgdatadir)/pixmaps +dist_pixmap_DATA = \ + subpixel-bgr.png \ + subpixel-rgb.png \ + subpixel-vbgr.png \ + subpixel-vrgb.png \ + theme-thumbnailing.png \ + gtk-theme-thumbnailing.png \ + window-theme-thumbnailing.png \ + icon-theme-thumbnailing.png \ + mouse-cursor-normal.png \ + mouse-cursor-normal-large.png \ + mouse-cursor-white.png \ + mouse-cursor-white-large.png + +cursorfontdir = $(datadir)/mate/cursor-fonts +dist_cursorfont_DATA = \ + cursor-large.pcf \ + cursor-white.pcf \ + cursor-large-white.pcf + +@INTLTOOL_DESKTOP_RULE@ + +desktopdir = $(datadir)/applications +desktop_in_files = \ + mate-appearance-properties.desktop.in \ + mate-theme-installer.desktop.in +desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) + + + + +@INTLTOOL_XML_RULE@ + +xml_in_files = \ + mate-theme-package.xml.in + +mimedir = $(datadir)/mime/packages +mime_DATA = $(xml_in_files:.xml.in=.xml) + + +install-data-hook: +if ENABLE_UPDATE_MIMEDB + $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" +endif + +uninstall-hook: +if ENABLE_UPDATE_MIMEDB + $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" +endif + + +EXTRA_DIST = $(xml_in_files) + + +CLEANFILES = $(desktop_in_files) $(desktop_DATA) $(mime_DATA) + +-include $(top_srcdir)/git.mk diff --git a/capplets/appearance/data/Makefile.in b/capplets/appearance/data/Makefile.in new file mode 100644 index 00000000..84174029 --- /dev/null +++ b/capplets/appearance/data/Makefile.in @@ -0,0 +1,660 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = capplets/appearance/data +DIST_COMMON = $(dist_cursorfont_DATA) $(dist_gtkbuilder_DATA) \ + $(dist_pixmap_DATA) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in \ + $(srcdir)/mate-appearance-properties.desktop.in.in \ + $(srcdir)/mate-theme-installer.desktop.in.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/mate-doc-utils.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = mate-appearance-properties.desktop.in \ + mate-theme-installer.desktop.in +CONFIG_CLEAN_VPATH_FILES = +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +SOURCES = +DIST_SOURCES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(desktopdir)" \ + "$(DESTDIR)$(cursorfontdir)" "$(DESTDIR)$(gtkbuilderdir)" \ + "$(DESTDIR)$(pixmapdir)" "$(DESTDIR)$(mimedir)" +DATA = $(desktop_DATA) $(dist_cursorfont_DATA) $(dist_gtkbuilder_DATA) \ + $(dist_pixmap_DATA) $(mime_DATA) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APP_INDICATOR_CFLAGS = @APP_INDICATOR_CFLAGS@ +APP_INDICATOR_LIBS = @APP_INDICATOR_LIBS@ +AR = @AR@ +AT_CAPPLET_CFLAGS = @AT_CAPPLET_CFLAGS@ +AT_CAPPLET_LIBS = @AT_CAPPLET_LIBS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CAPPLET_CFLAGS = @CAPPLET_CFLAGS@ +CAPPLET_LIBS = @CAPPLET_LIBS@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DEFAULT_APPLICATIONS_CAPPLET_CFLAGS = @DEFAULT_APPLICATIONS_CAPPLET_CFLAGS@ +DEFAULT_APPLICATIONS_CAPPLET_LIBS = @DEFAULT_APPLICATIONS_CAPPLET_LIBS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DISPLAY_CAPPLET_CFLAGS = @DISPLAY_CAPPLET_CFLAGS@ +DISPLAY_CAPPLET_LIBS = @DISPLAY_CAPPLET_LIBS@ +DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ +DLLTOOL = @DLLTOOL@ +DOC_USER_FORMATS = @DOC_USER_FORMATS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTERNAL_LIBSLAB_CFLAGS = @EXTERNAL_LIBSLAB_CFLAGS@ +EXTERNAL_LIBSLAB_LIBS = @EXTERNAL_LIBSLAB_LIBS@ +FGREP = @FGREP@ +FONT_CAPPLET_CFLAGS = @FONT_CAPPLET_CFLAGS@ +FONT_CAPPLET_LIBS = @FONT_CAPPLET_LIBS@ +FONT_VIEWER_CFLAGS = @FONT_VIEWER_CFLAGS@ +FONT_VIEWER_LIBS = @FONT_VIEWER_LIBS@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +GSD_DBUS_CFLAGS = @GSD_DBUS_CFLAGS@ +GSD_DBUS_LIBS = @GSD_DBUS_LIBS@ +GTK_ENGINE_DIR = @GTK_ENGINE_DIR@ +HELP_DIR = @HELP_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCANBERRA_GTK_CFLAGS = @LIBCANBERRA_GTK_CFLAGS@ +LIBCANBERRA_GTK_LIBS = @LIBCANBERRA_GTK_LIBS@ +LIBEBOOK_CFLAGS = @LIBEBOOK_CFLAGS@ +LIBEBOOK_LIBS = @LIBEBOOK_LIBS@ +LIBMATEKBDUI_CFLAGS = @LIBMATEKBDUI_CFLAGS@ +LIBMATEKBDUI_LIBS = @LIBMATEKBDUI_LIBS@ +LIBMATEKBD_CFLAGS = @LIBMATEKBD_CFLAGS@ +LIBMATEKBD_LIBS = @LIBMATEKBD_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSLAB_CFLAGS = @LIBSLAB_CFLAGS@ +LIBSLAB_LIBS = @LIBSLAB_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MARCO_CFLAGS = @MARCO_CFLAGS@ +MARCO_LIBS = @MARCO_LIBS@ +MATECC_CAPPLETS_CFLAGS = @MATECC_CAPPLETS_CFLAGS@ +MATECC_CAPPLETS_CLEANFILES = @MATECC_CAPPLETS_CLEANFILES@ +MATECC_CAPPLETS_EXTRA_DIST = @MATECC_CAPPLETS_EXTRA_DIST@ +MATECC_CAPPLETS_LIBS = @MATECC_CAPPLETS_LIBS@ +MATECC_CFLAGS = @MATECC_CFLAGS@ +MATECC_LIBS = @MATECC_LIBS@ +MATECC_SHELL_CFLAGS = @MATECC_SHELL_CFLAGS@ +MATECC_SHELL_LIBS = @MATECC_SHELL_LIBS@ +MATECONFTOOL = @MATECONFTOOL@ +MATECONF_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MATE_DESKTOP_CFLAGS = @MATE_DESKTOP_CFLAGS@ +MATE_DESKTOP_LIBS = @MATE_DESKTOP_LIBS@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OMF_DIR = @OMF_DIR@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +SCREENSAVER_LIBS = @SCREENSAVER_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TYPING_BREAK = @TYPING_BREAK@ +TYPING_CFLAGS = @TYPING_CFLAGS@ +TYPING_LIBS = @TYPING_LIBS@ +UPDATE_MIME_DATABASE = @UPDATE_MIME_DATABASE@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +XCURSOR_CFLAGS = @XCURSOR_CFLAGS@ +XCURSOR_LIBS = @XCURSOR_LIBS@ +XF86MISC_LIBS = @XF86MISC_LIBS@ +XGETTEXT = @XGETTEXT@ +XINPUT_CFLAGS = @XINPUT_CFLAGS@ +XINPUT_LIBS = @XINPUT_LIBS@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +gtkbuilderdir = $(pkgdatadir)/ui +dist_gtkbuilder_DATA = appearance.ui +pixmapdir = $(pkgdatadir)/pixmaps +dist_pixmap_DATA = \ + subpixel-bgr.png \ + subpixel-rgb.png \ + subpixel-vbgr.png \ + subpixel-vrgb.png \ + theme-thumbnailing.png \ + gtk-theme-thumbnailing.png \ + window-theme-thumbnailing.png \ + icon-theme-thumbnailing.png \ + mouse-cursor-normal.png \ + mouse-cursor-normal-large.png \ + mouse-cursor-white.png \ + mouse-cursor-white-large.png + +cursorfontdir = $(datadir)/mate/cursor-fonts +dist_cursorfont_DATA = \ + cursor-large.pcf \ + cursor-white.pcf \ + cursor-large-white.pcf + +desktopdir = $(datadir)/applications +desktop_in_files = \ + mate-appearance-properties.desktop.in \ + mate-theme-installer.desktop.in + +desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) +xml_in_files = \ + mate-theme-package.xml.in + +mimedir = $(datadir)/mime/packages +mime_DATA = $(xml_in_files:.xml.in=.xml) +EXTRA_DIST = $(xml_in_files) +CLEANFILES = $(desktop_in_files) $(desktop_DATA) $(mime_DATA) +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu capplets/appearance/data/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu capplets/appearance/data/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +mate-appearance-properties.desktop.in: $(top_builddir)/config.status $(srcdir)/mate-appearance-properties.desktop.in.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +mate-theme-installer.desktop.in: $(top_builddir)/config.status $(srcdir)/mate-theme-installer.desktop.in.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-desktopDATA: $(desktop_DATA) + @$(NORMAL_INSTALL) + test -z "$(desktopdir)" || $(MKDIR_P) "$(DESTDIR)$(desktopdir)" + @list='$(desktop_DATA)'; test -n "$(desktopdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(desktopdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(desktopdir)" || exit $$?; \ + done + +uninstall-desktopDATA: + @$(NORMAL_UNINSTALL) + @list='$(desktop_DATA)'; test -n "$(desktopdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(desktopdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(desktopdir)" && rm -f $$files +install-dist_cursorfontDATA: $(dist_cursorfont_DATA) + @$(NORMAL_INSTALL) + test -z "$(cursorfontdir)" || $(MKDIR_P) "$(DESTDIR)$(cursorfontdir)" + @list='$(dist_cursorfont_DATA)'; test -n "$(cursorfontdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(cursorfontdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(cursorfontdir)" || exit $$?; \ + done + +uninstall-dist_cursorfontDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_cursorfont_DATA)'; test -n "$(cursorfontdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(cursorfontdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(cursorfontdir)" && rm -f $$files +install-dist_gtkbuilderDATA: $(dist_gtkbuilder_DATA) + @$(NORMAL_INSTALL) + test -z "$(gtkbuilderdir)" || $(MKDIR_P) "$(DESTDIR)$(gtkbuilderdir)" + @list='$(dist_gtkbuilder_DATA)'; test -n "$(gtkbuilderdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(gtkbuilderdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(gtkbuilderdir)" || exit $$?; \ + done + +uninstall-dist_gtkbuilderDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_gtkbuilder_DATA)'; test -n "$(gtkbuilderdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(gtkbuilderdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(gtkbuilderdir)" && rm -f $$files +install-dist_pixmapDATA: $(dist_pixmap_DATA) + @$(NORMAL_INSTALL) + test -z "$(pixmapdir)" || $(MKDIR_P) "$(DESTDIR)$(pixmapdir)" + @list='$(dist_pixmap_DATA)'; test -n "$(pixmapdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pixmapdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pixmapdir)" || exit $$?; \ + done + +uninstall-dist_pixmapDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_pixmap_DATA)'; test -n "$(pixmapdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(pixmapdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(pixmapdir)" && rm -f $$files +install-mimeDATA: $(mime_DATA) + @$(NORMAL_INSTALL) + test -z "$(mimedir)" || $(MKDIR_P) "$(DESTDIR)$(mimedir)" + @list='$(mime_DATA)'; test -n "$(mimedir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(mimedir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(mimedir)" || exit $$?; \ + done + +uninstall-mimeDATA: + @$(NORMAL_UNINSTALL) + @list='$(mime_DATA)'; test -n "$(mimedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(mimedir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(mimedir)" && rm -f $$files +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(DATA) +installdirs: + for dir in "$(DESTDIR)$(desktopdir)" "$(DESTDIR)$(cursorfontdir)" "$(DESTDIR)$(gtkbuilderdir)" "$(DESTDIR)$(pixmapdir)" "$(DESTDIR)$(mimedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-desktopDATA install-dist_cursorfontDATA \ + install-dist_gtkbuilderDATA install-dist_pixmapDATA \ + install-mimeDATA + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-desktopDATA uninstall-dist_cursorfontDATA \ + uninstall-dist_gtkbuilderDATA uninstall-dist_pixmapDATA \ + uninstall-mimeDATA + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-hook +.MAKE: install-am install-data-am install-strip uninstall-am + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + distclean distclean-generic distclean-libtool distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-data-hook \ + install-desktopDATA install-dist_cursorfontDATA \ + install-dist_gtkbuilderDATA install-dist_pixmapDATA \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-man install-mimeDATA install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \ + uninstall-desktopDATA uninstall-dist_cursorfontDATA \ + uninstall-dist_gtkbuilderDATA uninstall-dist_pixmapDATA \ + uninstall-hook uninstall-mimeDATA + + +@INTLTOOL_DESKTOP_RULE@ + +@INTLTOOL_XML_RULE@ + +install-data-hook: +@ENABLE_UPDATE_MIMEDB_TRUE@ $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" + +uninstall-hook: +@ENABLE_UPDATE_MIMEDB_TRUE@ $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" + +-include $(top_srcdir)/git.mk + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/capplets/appearance/data/appearance.ui b/capplets/appearance/data/appearance.ui new file mode 100644 index 00000000..d97b2371 --- /dev/null +++ b/capplets/appearance/data/appearance.ui @@ -0,0 +1,2603 @@ + + + + + + 5 + Font Rendering Details + False + dialog + False + + + True + vertical + 2 + + + True + 5 + vertical + 18 + + + True + 0 + 0 + + + True + 12 + + + True + R_esolution: + True + dpi_spinner + + + False + False + 0 + + + + + True + 6 + + + True + True + 1 + + + False + 1 + + + + + True + dots per inch + + + False + False + 2 + + + + + False + False + 1 + + + + + + + False + False + 0 + + + + + True + vertical + + + True + 0 + Smoothing + + + + + + False + False + 0 + + + + + True + 6 + 12 + + + True + 2 + 2 + 12 + 6 + + + True + vertical + 3 + + + _None + True + True + False + True + True + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + GTK_FILL + + + + + True + vertical + 3 + + + Gra_yscale + True + True + False + True + True + antialias_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + vertical + 3 + + + Sub_pixel (LCDs) + True + True + False + True + True + antialias_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + GTK_FILL + + + + + + + + + + 1 + + + + + 1 + + + + + True + vertical + + + True + 0 + Hinting + + + + + + False + False + 0 + + + + + True + 6 + 12 + + + True + 2 + 2 + 12 + 6 + + + True + vertical + 3 + + + N_one + True + True + False + True + True + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + + + True + vertical + 3 + + + _Slight + True + True + False + True + True + hint_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + vertical + 3 + + + _Medium + True + True + False + True + True + hint_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + vertical + 3 + + + _Full + True + True + False + True + True + hint_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + + + 1 + + + + + 2 + + + + + True + vertical + + + True + 0 + Subpixel Order + + + + + + False + False + 0 + + + + + True + 6 + 12 + + + True + 2 + 2 + 12 + 6 + + + True + + + _RGB + True + True + False + True + True + + + 0 + + + + + True + gtk-missing-image + + + False + False + 1 + + + + + + + True + + + _BGR + True + True + False + True + True + subpixel_rgb_radio + + + 0 + + + + + True + gtk-missing-image + + + False + False + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + + + _VRGB + True + True + False + True + True + subpixel_rgb_radio + + + 0 + + + + + True + gtk-missing-image + + + False + False + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + + + VB_GR + True + True + False + True + True + subpixel_rgb_radio + + + 0 + + + + + True + gtk-missing-image + + + False + False + 1 + + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + + + 1 + + + + + 3 + + + + + 1 + + + + + True + end + + + gtk-close + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + button3 + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + Appearance Preferences + dialog + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 2 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + 1 + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + browse + 138 + 3 + 18 + 18 + 18 + + + + + 0 + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + Save _As... + True + False + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 1 + + + + + C_ustomize... + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 2 + + + + + _Install... + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 3 + + + + + False + 1 + + + + + True + + + Get more themes online + True + True + True + True + True + none + http://gnome-look.org/ + + + False + False + 0 + + + + + False + False + end + 2 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Theme + + + False + + + + + True + 12 + vertical + 6 + + + True + vertical + 6 + + + True + 6 + + + True + True + never + automatic + in + + + 1 + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + browse + 3 + 3 + + + + + 0 + + + + + 0 + + + + + True + 24 + + + True + 12 + + + True + _Style: + True + wp_style_menu + + + False + False + 0 + + + + + True + wp_style_liststore + + + + 0 + + + + + False + False + 1 + + + + + False + False + 0 + + + + + True + 12 + + + True + C_olors: + True + wp_color_menu + + + False + False + 0 + + + + + True + 6 + + + True + wp_color_liststore + + + + 0 + + + + + False + False + 0 + + + + + True + True + True + #000000000000 + + + Open a dialog to specify the color + + + + + False + False + 1 + + + + + True + True + #000000000000 + + + Open a dialog to specify the color + + + + + False + False + 2 + + + + + 1 + + + + + False + False + 1 + + + + + False + False + 1 + + + + + 0 + + + + + True + + + False + False + 1 + + + + + True + + + Get more backgrounds online + True + True + True + True + none + http://gnome-look.org/ + + + False + False + 0 + + + + + True + 6 + True + end + + + _Add... + True + True + True + True + + + False + False + end + 1 + + + + + gtk-remove + True + True + True + True + + + False + False + end + 0 + + + + + False + False + end + 1 + + + + + False + False + 2 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Background + + + 1 + False + + + + + True + 12 + vertical + 18 + + + True + 5 + 2 + 12 + 6 + + + True + True + False + True + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Document font: + True + document_font + + + 1 + 2 + GTK_FILL + + + + + + True + True + False + True + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + True + False + True + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + + True + True + False + True + + + 1 + 2 + 4 + 5 + GTK_FILL + + + + + + True + 0 + Des_ktop font: + True + right + desktop_font + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + _Window title font: + True + right + window_title_font + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + _Fixed width font: + True + right + monospace_font + + + 4 + 5 + GTK_FILL + + + + + + True + True + False + True + + + 1 + 2 + + + + + + True + 0 + _Application font: + True + right + application_font + + + GTK_FILL + + + + + + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Rendering + + + + + + False + False + 0 + + + + + True + 12 + + + True + 2 + 2 + 12 + 6 + + + True + vertical + 6 + + + Sub_pixel smoothing (LCDs) + True + True + False + True + True + monochrome_radio + + + False + False + 0 + + + + + True + + + True + + + + + False + 1 + + + + + 1 + 2 + 1 + 2 + + + + + True + vertical + 6 + + + Best co_ntrast + True + True + False + True + True + monochrome_radio + + + False + False + 0 + + + + + True + + + True + + + + + False + 1 + + + + + 1 + 2 + + + + + True + vertical + 6 + + + Best _shapes + True + True + False + True + True + monochrome_radio + + + False + False + 0 + + + + + True + + + True + + + + + False + 1 + + + + + 1 + 2 + + + + + True + vertical + 6 + + + _Monochrome + True + True + False + True + True + + + False + False + 0 + + + + + True + + + True + + + + + False + 1 + + + + + + + + + False + 1 + + + + + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + D_etails... + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 2 + + + + + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Fonts + + + 2 + False + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + gtk-help + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + True + + + + + gtk-close + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + help_button + close_button + + + + + + + + + + Tile + + + Zoom + + + Center + + + Scale + + + Stretch + + + Span + + + + + + + + + + + Solid color + + + Horizontal gradient + + + Vertical gradient + + + + + + + + + + + Text below items + + + Text beside items + + + Icons only + + + Text only + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + Customize Theme + center-on-parent + dialog + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 2 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + 300 + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + + + 0 + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 1 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Controls + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 18 + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + gtk-dialog-info + 5 + + + False + 0 + + + + + 280 + True + The current controls theme does not support color schemes. + True + + + False + False + 1 + + + + + False + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + 3 + 12 + 12 + + + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 2 + 3 + 4 + 5 + + + + + + + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 1 + 2 + 4 + 5 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + _Tooltips: + True + tooltip_bg_color + + + 4 + 5 + + + + + True + True + False + + + 2 + 3 + 3 + 4 + + + + + + + True + True + False + + + 2 + 3 + 2 + 3 + + + + + + + True + True + False + + + 2 + 3 + 1 + 2 + + + + + + + True + 0 + _Selected items: + True + selected_bg_color + + + 3 + 4 + + + + + True + True + False + + + 1 + 2 + 3 + 4 + + + + + + + True + True + False + + + 1 + 2 + 2 + 3 + + + + + + + True + True + False + + + 1 + 2 + 1 + 2 + + + + + + + True + 0 + _Input boxes: + True + base_color + + + 2 + 3 + + + + + True + 0 + _Windows: + True + bg_color + + + 1 + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Text + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Background + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + + + + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + _Reset to Defaults + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 2 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Colors + + + 1 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 1 + + + + + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Window Border + + + 2 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 1 + + + + + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Icons + + + 3 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + 6 + + + True + gtk-dialog-info + 5 + + + False + 0 + + + + + 280 + True + Changing your cursor theme takes effect the next time you log in. + True + + + False + False + 1 + + + + + False + False + 0 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + _Size: + True + cursor_size_scale + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Small + True + + + False + 0 + + + + + 100 + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + False + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Large + True + + + False + 2 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + 2 + + + + + False + 2 + + + + + 4 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Pointer + + + 4 + False + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + gtk-help + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + gtk-close + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + theme_help_button + theme_close_button + + + + 6 + Save Theme As... + True + dialog + False + + + True + vertical + 2 + + + True + 6 + 3 + 2 + 12 + 6 + + + True + True + True + + + 1 + 2 + + + + + + True + 0 + _Name: + True + save_dialog_entry + + + GTK_FILL + GTK_FILL + + + + + True + True + never + automatic + in + + + True + True + word + False + + + + + 1 + 2 + 1 + 2 + + + + + True + 0 + 0 + _Description: + True + save_dialog_textview + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Save _background image + True + True + False + True + True + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + + + + 1 + + + + + True + end + + + gtk-cancel + True + True + True + False + True + + + False + False + 0 + + + + + gtk-save + True + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + save_dialog_cancel_button + save_dialog_save_button + + + diff --git a/capplets/appearance/data/cursor-large-white.pcf b/capplets/appearance/data/cursor-large-white.pcf new file mode 100644 index 00000000..e1d7b631 Binary files /dev/null and b/capplets/appearance/data/cursor-large-white.pcf differ diff --git a/capplets/appearance/data/cursor-large.pcf b/capplets/appearance/data/cursor-large.pcf new file mode 100644 index 00000000..6580b33d Binary files /dev/null and b/capplets/appearance/data/cursor-large.pcf differ diff --git a/capplets/appearance/data/cursor-white.pcf b/capplets/appearance/data/cursor-white.pcf new file mode 100644 index 00000000..bc9932c3 Binary files /dev/null and b/capplets/appearance/data/cursor-white.pcf differ diff --git a/capplets/appearance/data/gtk-theme-thumbnailing.png b/capplets/appearance/data/gtk-theme-thumbnailing.png new file mode 100644 index 00000000..52065c7f Binary files /dev/null and b/capplets/appearance/data/gtk-theme-thumbnailing.png differ diff --git a/capplets/appearance/data/icon-theme-thumbnailing.png b/capplets/appearance/data/icon-theme-thumbnailing.png new file mode 100644 index 00000000..6ede83f2 Binary files /dev/null and b/capplets/appearance/data/icon-theme-thumbnailing.png differ diff --git a/capplets/appearance/data/mate-appearance-properties.desktop.in.in b/capplets/appearance/data/mate-appearance-properties.desktop.in.in new file mode 100644 index 00000000..f6362193 --- /dev/null +++ b/capplets/appearance/data/mate-appearance-properties.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Appearance +_Comment=Customize the look of the desktop +Exec=mate-appearance-properties %F +Icon=preferences-desktop-theme +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;DesktopSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=Appearance +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/appearance/data/mate-theme-installer.desktop.in.in b/capplets/appearance/data/mate-theme-installer.desktop.in.in new file mode 100644 index 00000000..3bf6aab5 --- /dev/null +++ b/capplets/appearance/data/mate-theme-installer.desktop.in.in @@ -0,0 +1,16 @@ +[Desktop Entry] +_Name=Theme Installer +_Comment=Installs themes packages for various parts of the desktop +Exec=mate-appearance-properties -i %u +Icon=preferences-desktop-theme +Terminal=false +Type=Application +MimeType=application/x-mate-theme-package; +StartupNotify=true +Categories=MATE;GTK; +NoDisplay=true +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=Appearance +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/appearance/data/mate-theme-package.xml.in b/capplets/appearance/data/mate-theme-package.xml.in new file mode 100644 index 00000000..58cbdf43 --- /dev/null +++ b/capplets/appearance/data/mate-theme-package.xml.in @@ -0,0 +1,9 @@ + + + + + <_comment>Mate Theme Package + + + + diff --git a/capplets/appearance/data/mouse-cursor-normal-large.png b/capplets/appearance/data/mouse-cursor-normal-large.png new file mode 100644 index 00000000..afef4ea8 Binary files /dev/null and b/capplets/appearance/data/mouse-cursor-normal-large.png differ diff --git a/capplets/appearance/data/mouse-cursor-normal.png b/capplets/appearance/data/mouse-cursor-normal.png new file mode 100644 index 00000000..1d9351c8 Binary files /dev/null and b/capplets/appearance/data/mouse-cursor-normal.png differ diff --git a/capplets/appearance/data/mouse-cursor-white-large.png b/capplets/appearance/data/mouse-cursor-white-large.png new file mode 100644 index 00000000..8f98438a Binary files /dev/null and b/capplets/appearance/data/mouse-cursor-white-large.png differ diff --git a/capplets/appearance/data/mouse-cursor-white.png b/capplets/appearance/data/mouse-cursor-white.png new file mode 100644 index 00000000..2be63acd Binary files /dev/null and b/capplets/appearance/data/mouse-cursor-white.png differ diff --git a/capplets/appearance/data/subpixel-bgr.png b/capplets/appearance/data/subpixel-bgr.png new file mode 100644 index 00000000..7efd2624 Binary files /dev/null and b/capplets/appearance/data/subpixel-bgr.png differ diff --git a/capplets/appearance/data/subpixel-rgb.png b/capplets/appearance/data/subpixel-rgb.png new file mode 100644 index 00000000..58ac1eca Binary files /dev/null and b/capplets/appearance/data/subpixel-rgb.png differ diff --git a/capplets/appearance/data/subpixel-vbgr.png b/capplets/appearance/data/subpixel-vbgr.png new file mode 100644 index 00000000..abd8df01 Binary files /dev/null and b/capplets/appearance/data/subpixel-vbgr.png differ diff --git a/capplets/appearance/data/subpixel-vrgb.png b/capplets/appearance/data/subpixel-vrgb.png new file mode 100644 index 00000000..6e609060 Binary files /dev/null and b/capplets/appearance/data/subpixel-vrgb.png differ diff --git a/capplets/appearance/data/theme-thumbnailing.png b/capplets/appearance/data/theme-thumbnailing.png new file mode 100644 index 00000000..938edb0e Binary files /dev/null and b/capplets/appearance/data/theme-thumbnailing.png differ diff --git a/capplets/appearance/data/window-theme-thumbnailing.png b/capplets/appearance/data/window-theme-thumbnailing.png new file mode 100644 index 00000000..ce3bd1ab Binary files /dev/null and b/capplets/appearance/data/window-theme-thumbnailing.png differ diff --git a/capplets/appearance/mate-wp-info.c b/capplets/appearance/mate-wp-info.c new file mode 100644 index 00000000..5c799eab --- /dev/null +++ b/capplets/appearance/mate-wp-info.c @@ -0,0 +1,87 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include "mate-wp-info.h" + +MateWPInfo * mate_wp_info_new (const gchar * uri, + MateDesktopThumbnailFactory * thumbs) { + MateWPInfo *wp; + GFile *file; + GFileInfo *info; + + file = g_file_new_for_commandline_arg (uri); + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_NAME "," + G_FILE_ATTRIBUTE_STANDARD_SIZE "," + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," + G_FILE_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + g_object_unref (file); + + if (info == NULL || g_file_info_get_content_type (info) == NULL) { + if (!strcmp (uri, "(none)")) { + wp = g_new0 (MateWPInfo, 1); + + wp->mime_type = g_strdup ("image/x-no-data"); + wp->uri = g_strdup (uri); + wp->name = g_strdup (_("No Desktop Background")); + wp->size = 0; + } else { + wp = NULL; + } + } else { + wp = g_new0 (MateWPInfo, 1); + + wp->uri = g_strdup (uri); + + wp->name = g_strdup (g_file_info_get_name (info)); + if (g_file_info_get_content_type (info) != NULL) + wp->mime_type = g_strdup (g_file_info_get_content_type (info)); + wp->size = g_file_info_get_size (info); + wp->mtime = g_file_info_get_attribute_uint64 (info, + G_FILE_ATTRIBUTE_TIME_MODIFIED); + + wp->thumburi = mate_desktop_thumbnail_factory_lookup (thumbs, + uri, + wp->mtime); + } + + if (info != NULL) + g_object_unref (info); + + return wp; +} + +void mate_wp_info_free (MateWPInfo * info) { + if (info == NULL) { + return; + } + + g_free (info->uri); + g_free (info->thumburi); + g_free (info->name); + g_free (info->mime_type); +} diff --git a/capplets/appearance/mate-wp-info.h b/capplets/appearance/mate-wp-info.h new file mode 100644 index 00000000..95c94f89 --- /dev/null +++ b/capplets/appearance/mate-wp-info.h @@ -0,0 +1,45 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _MATE_WP_INFO_H_ +#define _MATE_WP_INFO_H_ + +#include +#include + +typedef struct _MateWPInfo MateWPInfo; + +struct _MateWPInfo { + gchar * uri; + gchar * thumburi; + gchar * name; + gchar * mime_type; + + goffset size; + + time_t mtime; +}; + +MateWPInfo * mate_wp_info_new (const gchar * uri, + MateDesktopThumbnailFactory * thumbs); +void mate_wp_info_free (MateWPInfo * info); + +#endif + diff --git a/capplets/appearance/mate-wp-item.c b/capplets/appearance/mate-wp-item.c new file mode 100644 index 00000000..de9623f1 --- /dev/null +++ b/capplets/appearance/mate-wp-item.c @@ -0,0 +1,307 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include +#include +#include +#include "mate-wp-item.h" + +static MateConfEnumStringPair options_lookup[] = { + { MATE_BG_PLACEMENT_CENTERED, "centered" }, + { MATE_BG_PLACEMENT_FILL_SCREEN, "stretched" }, + { MATE_BG_PLACEMENT_SCALED, "scaled" }, + { MATE_BG_PLACEMENT_ZOOMED, "zoom" }, + { MATE_BG_PLACEMENT_TILED, "wallpaper" }, + { MATE_BG_PLACEMENT_SPANNED, "spanned" }, + { 0, NULL } +}; + +static MateConfEnumStringPair shade_lookup[] = { + { MATE_BG_COLOR_SOLID, "solid" }, + { MATE_BG_COLOR_H_GRADIENT, "horizontal-gradient" }, + { MATE_BG_COLOR_V_GRADIENT, "vertical-gradient" }, + { 0, NULL } +}; + +const gchar *wp_item_option_to_string (MateBGPlacement type) +{ + return mateconf_enum_to_string (options_lookup, type); +} + +const gchar *wp_item_shading_to_string (MateBGColorType type) +{ + return mateconf_enum_to_string (shade_lookup, type); +} + +MateBGPlacement wp_item_string_to_option (const gchar *option) +{ + int i = MATE_BG_PLACEMENT_SCALED; + mateconf_string_to_enum (options_lookup, option, &i); + return i; +} + +MateBGColorType wp_item_string_to_shading (const gchar *shade_type) +{ + int i = MATE_BG_COLOR_SOLID; + mateconf_string_to_enum (shade_lookup, shade_type, &i); + return i; +} + +static void set_bg_properties (MateWPItem *item) +{ + if (item->filename) + mate_bg_set_filename (item->bg, item->filename); + + mate_bg_set_color (item->bg, item->shade_type, item->pcolor, item->scolor); + mate_bg_set_placement (item->bg, item->options); +} + +void mate_wp_item_ensure_mate_bg (MateWPItem *item) +{ + if (!item->bg) { + item->bg = mate_bg_new (); + + set_bg_properties (item); + } +} + +void mate_wp_item_update (MateWPItem *item) { + MateConfClient *client; + GdkColor color1 = { 0, 0, 0, 0 }, color2 = { 0, 0, 0, 0 }; + gchar *s; + + client = mateconf_client_get_default (); + + s = mateconf_client_get_string (client, WP_OPTIONS_KEY, NULL); + item->options = wp_item_string_to_option (s); + g_free (s); + + s = mateconf_client_get_string (client, WP_SHADING_KEY, NULL); + item->shade_type = wp_item_string_to_shading (s); + g_free (s); + + s = mateconf_client_get_string (client, WP_PCOLOR_KEY, NULL); + if (s != NULL) { + gdk_color_parse (s, &color1); + g_free (s); + } + + s = mateconf_client_get_string (client, WP_SCOLOR_KEY, NULL); + if (s != NULL) { + gdk_color_parse (s, &color2); + g_free (s); + } + + g_object_unref (client); + + if (item->pcolor != NULL) + gdk_color_free (item->pcolor); + + if (item->scolor != NULL) + gdk_color_free (item->scolor); + + item->pcolor = gdk_color_copy (&color1); + item->scolor = gdk_color_copy (&color2); +} + +MateWPItem * mate_wp_item_new (const gchar * filename, + GHashTable * wallpapers, + MateDesktopThumbnailFactory * thumbnails) { + MateWPItem *item = g_new0 (MateWPItem, 1); + + item->filename = g_strdup (filename); + item->fileinfo = mate_wp_info_new (filename, thumbnails); + + if (item->fileinfo != NULL && item->fileinfo->mime_type != NULL && + (g_str_has_prefix (item->fileinfo->mime_type, "image/") || + strcmp (item->fileinfo->mime_type, "application/xml") == 0)) { + + if (g_utf8_validate (item->fileinfo->name, -1, NULL)) + item->name = g_strdup (item->fileinfo->name); + else + item->name = g_filename_to_utf8 (item->fileinfo->name, -1, NULL, + NULL, NULL); + + mate_wp_item_update (item); + mate_wp_item_ensure_mate_bg (item); + mate_wp_item_update_description (item); + + g_hash_table_insert (wallpapers, item->filename, item); + } else { + mate_wp_item_free (item); + item = NULL; + } + + return item; +} + +void mate_wp_item_free (MateWPItem * item) { + if (item == NULL) { + return; + } + + g_free (item->name); + g_free (item->filename); + g_free (item->description); + + if (item->pcolor != NULL) + gdk_color_free (item->pcolor); + + if (item->scolor != NULL) + gdk_color_free (item->scolor); + + mate_wp_info_free (item->fileinfo); + if (item->bg) + g_object_unref (item->bg); + + gtk_tree_row_reference_free (item->rowref); + + g_free (item); +} + +static GdkPixbuf * +add_slideshow_frame (GdkPixbuf *pixbuf) +{ + GdkPixbuf *sheet, *sheet2; + GdkPixbuf *tmp; + gint w, h; + + w = gdk_pixbuf_get_width (pixbuf); + h = gdk_pixbuf_get_height (pixbuf); + + sheet = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, w, h); + gdk_pixbuf_fill (sheet, 0x00000000); + sheet2 = gdk_pixbuf_new_subpixbuf (sheet, 1, 1, w - 2, h - 2); + gdk_pixbuf_fill (sheet2, 0xffffffff); + g_object_unref (sheet2); + + tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, w + 6, h + 6); + + gdk_pixbuf_fill (tmp, 0x00000000); + gdk_pixbuf_composite (sheet, tmp, 6, 6, w, h, 6.0, 6.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255); + gdk_pixbuf_composite (sheet, tmp, 3, 3, w, h, 3.0, 3.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255); + gdk_pixbuf_composite (pixbuf, tmp, 0, 0, w, h, 0.0, 0.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + g_object_unref (sheet); + + return tmp; +} + +GdkPixbuf * mate_wp_item_get_frame_thumbnail (MateWPItem * item, + MateDesktopThumbnailFactory * thumbs, + int width, + int height, + gint frame) { + GdkPixbuf *pixbuf = NULL; + + set_bg_properties (item); + + if (frame != -1) + pixbuf = mate_bg_create_frame_thumbnail (item->bg, thumbs, gdk_screen_get_default (), width, height, frame); + else + pixbuf = mate_bg_create_thumbnail (item->bg, thumbs, gdk_screen_get_default(), width, height); + + if (pixbuf && mate_bg_changes_with_time (item->bg)) + { + GdkPixbuf *tmp; + + tmp = add_slideshow_frame (pixbuf); + g_object_unref (pixbuf); + pixbuf = tmp; + } + + mate_bg_get_image_size (item->bg, thumbs, width, height, &item->width, &item->height); + + return pixbuf; +} + + +GdkPixbuf * mate_wp_item_get_thumbnail (MateWPItem * item, + MateDesktopThumbnailFactory * thumbs, + gint width, + gint height) { + return mate_wp_item_get_frame_thumbnail (item, thumbs, width, height, -1); +} + +void mate_wp_item_update_description (MateWPItem * item) { + g_free (item->description); + + if (!strcmp (item->filename, "(none)")) { + item->description = g_strdup (item->name); + } else { + const gchar *description; + gchar *size; + gchar *dirname = g_path_get_dirname (item->filename); + + description = NULL; + size = NULL; + + if (strcmp (item->fileinfo->mime_type, "application/xml") == 0) + { + if (mate_bg_changes_with_time (item->bg)) + description = _("Slide Show"); + else if (item->width > 0 && item->height > 0) + description = _("Image"); + } + else + description = g_content_type_get_description (item->fileinfo->mime_type); + + if (mate_bg_has_multiple_sizes (item->bg)) + size = g_strdup (_("multiple sizes")); + else if (item->width > 0 && item->height > 0) { + /* translators: x pixel(s) by y pixel(s) */ + size = g_strdup_printf (_("%d %s by %d %s"), + item->width, + ngettext ("pixel", "pixels", item->width), + item->height, + ngettext ("pixel", "pixels", item->height)); + } + + if (description && size) { + /* translators: wallpaper name + * mime type, size + * Folder: /path/to/file + */ + item->description = g_markup_printf_escaped (_("%s\n" + "%s, %s\n" + "Folder: %s"), + item->name, + description, + size, + dirname); + } else { + /* translators: wallpaper name + * Image missing + * Folder: /path/to/file + */ + item->description = g_markup_printf_escaped (_("%s\n" + "%s\n" + "Folder: %s"), + item->name, + _("Image missing"), + dirname); + } + + g_free (size); + g_free (dirname); + } +} diff --git a/capplets/appearance/mate-wp-item.h b/capplets/appearance/mate-wp-item.h new file mode 100644 index 00000000..1dd726a3 --- /dev/null +++ b/capplets/appearance/mate-wp-item.h @@ -0,0 +1,91 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "mate-wp-info.h" + +#ifndef _MATE_WP_ITEM_H_ +#define _MATE_WP_ITEM_H_ + +#define WP_PATH_KEY "/desktop/mate/background" +#define WP_FILE_KEY WP_PATH_KEY "/picture_filename" +#define WP_OPTIONS_KEY WP_PATH_KEY "/picture_options" +#define WP_SHADING_KEY WP_PATH_KEY "/color_shading_type" +#define WP_PCOLOR_KEY WP_PATH_KEY "/primary_color" +#define WP_SCOLOR_KEY WP_PATH_KEY "/secondary_color" + +typedef struct _MateWPItem MateWPItem; + +struct _MateWPItem { + MateBG *bg; + + gchar * name; + gchar * filename; + gchar * description; + MateBGPlacement options; + MateBGColorType shade_type; + + /* Where the Item is in the List */ + GtkTreeRowReference * rowref; + + /* Real colors */ + GdkColor * pcolor; + GdkColor * scolor; + + MateWPInfo * fileinfo; + + /* Did the user remove us? */ + gboolean deleted; + + /* Width and Height of the original image */ + gint width; + gint height; +}; + +MateWPItem * mate_wp_item_new (const gchar *filename, + GHashTable *wallpapers, + MateDesktopThumbnailFactory *thumbnails); + +void mate_wp_item_free (MateWPItem *item); +GdkPixbuf * mate_wp_item_get_thumbnail (MateWPItem *item, + MateDesktopThumbnailFactory *thumbs, + gint width, + gint height); +GdkPixbuf * mate_wp_item_get_frame_thumbnail (MateWPItem *item, + MateDesktopThumbnailFactory *thumbs, + gint width, + gint height, + gint frame); +void mate_wp_item_update (MateWPItem *item); +void mate_wp_item_update_description (MateWPItem *item); +void mate_wp_item_ensure_mate_bg (MateWPItem *item); + +const gchar *wp_item_option_to_string (MateBGPlacement type); +const gchar *wp_item_shading_to_string (MateBGColorType type); +MateBGPlacement wp_item_string_to_option (const gchar *option); +MateBGColorType wp_item_string_to_shading (const gchar *shade_type); + +#endif diff --git a/capplets/appearance/mate-wp-xml.c b/capplets/appearance/mate-wp-xml.c new file mode 100644 index 00000000..2157acf7 --- /dev/null +++ b/capplets/appearance/mate-wp-xml.c @@ -0,0 +1,451 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include "appearance.h" +#include "mate-wp-item.h" +#include +#include +#include + +static gboolean mate_wp_xml_get_bool (const xmlNode * parent, + const gchar * prop_name) { + xmlChar * prop; + gboolean ret_val = FALSE; + + g_return_val_if_fail (parent != NULL, FALSE); + g_return_val_if_fail (prop_name != NULL, FALSE); + + prop = xmlGetProp ((xmlNode *) parent, (xmlChar*)prop_name); + if (prop != NULL) { + if (!g_ascii_strcasecmp ((gchar *)prop, "true") || !g_ascii_strcasecmp ((gchar *)prop, "1")) { + ret_val = TRUE; + } else { + ret_val = FALSE; + } + g_free (prop); + } + + return ret_val; +} + +static void mate_wp_xml_set_bool (const xmlNode * parent, + const xmlChar * prop_name, gboolean value) { + g_return_if_fail (parent != NULL); + g_return_if_fail (prop_name != NULL); + + if (value) { + xmlSetProp ((xmlNode *) parent, prop_name, (xmlChar *)"true"); + } else { + xmlSetProp ((xmlNode *) parent, prop_name, (xmlChar *)"false"); + } +} + +static void mate_wp_load_legacy (AppearanceData *data) { + FILE * fp; + gchar * foo, * filename; + + filename = g_build_filename (g_get_home_dir (), ".mate2", + "wallpapers.list", NULL); + + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + if ((fp = fopen (filename, "r")) != NULL) { + foo = (gchar *) g_malloc (sizeof (gchar) * 4096); + while (fgets (foo, 4096, fp)) { + MateWPItem * item; + + if (foo[strlen (foo) - 1] == '\n') { + foo[strlen (foo) - 1] = '\0'; + } + + item = g_hash_table_lookup (data->wp_hash, foo); + if (item != NULL) { + continue; + } + + if (!g_file_test (foo, G_FILE_TEST_EXISTS)) { + continue; + } + + item = mate_wp_item_new (foo, data->wp_hash, data->thumb_factory); + if (item != NULL && item->fileinfo == NULL) { + mate_wp_item_free (item); + } + } + fclose (fp); + g_free (foo); + } + } + + g_free (filename); +} + +static void mate_wp_xml_load_xml (AppearanceData *data, + const gchar * filename) { + xmlDoc * wplist; + xmlNode * root, * list, * wpa; + xmlChar * nodelang; + const gchar * const * syslangs; + GdkColor color1, color2; + gint i; + + wplist = xmlParseFile (filename); + + if (!wplist) + return; + + syslangs = g_get_language_names (); + + root = xmlDocGetRootElement (wplist); + + for (list = root->children; list != NULL; list = list->next) { + if (!strcmp ((gchar *)list->name, "wallpaper")) { + MateWPItem * wp; + gchar *pcolor = NULL, *scolor = NULL; + gchar *s; + gboolean have_scale = FALSE, have_shade = FALSE; + + wp = g_new0 (MateWPItem, 1); + + wp->deleted = mate_wp_xml_get_bool (list, "deleted"); + + for (wpa = list->children; wpa != NULL; wpa = wpa->next) { + if (wpa->type == XML_COMMENT_NODE) { + continue; + } else if (!strcmp ((gchar *)wpa->name, "filename")) { + if (wpa->last != NULL && wpa->last->content != NULL) { + const char * none = "(none)"; + gchar *content = g_strstrip ((gchar *)wpa->last->content); + + if (!strcmp (content, none)) + wp->filename = g_strdup (content); + else if (g_utf8_validate (content, -1, NULL) && + g_file_test (content, G_FILE_TEST_EXISTS)) + wp->filename = g_strdup (content); + else + wp->filename = g_filename_from_utf8 (content, -1, NULL, NULL, NULL); + } else { + break; + } + } else if (!strcmp ((gchar *)wpa->name, "name")) { + if (wpa->last != NULL && wpa->last->content != NULL) { + nodelang = xmlNodeGetLang (wpa->last); + + if (wp->name == NULL && nodelang == NULL) { + wp->name = g_strdup (g_strstrip ((gchar *)wpa->last->content)); + } else { + for (i = 0; syslangs[i] != NULL; i++) { + if (!strcmp (syslangs[i], (gchar *)nodelang)) { + g_free (wp->name); + wp->name = g_strdup (g_strstrip ((gchar *)wpa->last->content)); + break; + } + } + } + + xmlFree (nodelang); + } else { + break; + } + } else if (!strcmp ((gchar *)wpa->name, "options")) { + if (wpa->last != NULL) { + wp->options = wp_item_string_to_option (g_strstrip ((gchar *)wpa->last->content)); + have_scale = TRUE; + } + } else if (!strcmp ((gchar *)wpa->name, "shade_type")) { + if (wpa->last != NULL) { + wp->shade_type = wp_item_string_to_shading (g_strstrip ((gchar *)wpa->last->content)); + have_shade = TRUE; + } + } else if (!strcmp ((gchar *)wpa->name, "pcolor")) { + if (wpa->last != NULL) { + pcolor = g_strdup (g_strstrip ((gchar *)wpa->last->content)); + } + } else if (!strcmp ((gchar *)wpa->name, "scolor")) { + if (wpa->last != NULL) { + scolor = g_strdup (g_strstrip ((gchar *)wpa->last->content)); + } + } else if (!strcmp ((gchar *)wpa->name, "text")) { + /* Do nothing here, libxml2 is being weird */ + } else { + g_warning ("Unknown Tag: %s", wpa->name); + } + } + + /* Make sure we don't already have this one and that filename exists */ + if (wp->filename == NULL || + g_hash_table_lookup (data->wp_hash, wp->filename) != NULL) { + + mate_wp_item_free (wp); + g_free (pcolor); + g_free (scolor); + continue; + } + + /* Verify the colors and alloc some GdkColors here */ + if (!have_scale) { + s = mateconf_client_get_string (data->client, WP_OPTIONS_KEY, NULL); + wp->options = wp_item_string_to_option (s); + g_free (s); + } + + if (!have_shade) { + s = mateconf_client_get_string (data->client, WP_SHADING_KEY, NULL); + wp->shade_type = wp_item_string_to_shading (s); + g_free (s); + } + + if (pcolor == NULL) { + pcolor = mateconf_client_get_string (data->client, + WP_PCOLOR_KEY, NULL); + } + if (scolor == NULL) { + scolor = mateconf_client_get_string (data->client, + WP_SCOLOR_KEY, NULL); + } + gdk_color_parse (pcolor, &color1); + gdk_color_parse (scolor, &color2); + g_free (pcolor); + g_free (scolor); + + wp->pcolor = gdk_color_copy (&color1); + wp->scolor = gdk_color_copy (&color2); + + if ((wp->filename != NULL && + g_file_test (wp->filename, G_FILE_TEST_EXISTS)) || + !strcmp (wp->filename, "(none)")) { + wp->fileinfo = mate_wp_info_new (wp->filename, data->thumb_factory); + + if (wp->name == NULL || !strcmp (wp->filename, "(none)")) { + g_free (wp->name); + wp->name = g_strdup (wp->fileinfo->name); + } + + mate_wp_item_ensure_mate_bg (wp); + mate_wp_item_update_description (wp); + g_hash_table_insert (data->wp_hash, wp->filename, wp); + } else { + mate_wp_item_free (wp); + wp = NULL; + } + } + } + xmlFreeDoc (wplist); +} + +static void mate_wp_file_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + AppearanceData *data) { + gchar * filename; + + switch (event_type) { + case G_FILE_MONITOR_EVENT_CHANGED: + case G_FILE_MONITOR_EVENT_CREATED: + filename = g_file_get_path (file); + mate_wp_xml_load_xml (data, filename); + g_free (filename); + break; + default: + break; + } +} + +static void mate_wp_xml_add_monitor (GFile *directory, + AppearanceData *data) { + GFileMonitor *monitor; + GError *error = NULL; + + monitor = g_file_monitor_directory (directory, + G_FILE_MONITOR_NONE, + NULL, + &error); + if (error != NULL) { + gchar *path; + + path = g_file_get_parse_name (directory); + g_warning ("Unable to monitor directory %s: %s", + path, error->message); + g_error_free (error); + g_free (path); + return; + } + + g_signal_connect (monitor, "changed", + G_CALLBACK (mate_wp_file_changed), + data); +} + +static void mate_wp_xml_load_from_dir (const gchar *path, + AppearanceData *data) { + GFile *directory; + GFileEnumerator *enumerator; + GError *error = NULL; + GFileInfo *info; + + if (!g_file_test (path, G_FILE_TEST_IS_DIR)) { + return; + } + + directory = g_file_new_for_path (path); + enumerator = g_file_enumerate_children (directory, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + &error); + if (error != NULL) { + g_warning ("Unable to check directory %s: %s", path, error->message); + g_error_free (error); + g_object_unref (directory); + return; + } + + while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL))) { + const gchar *filename; + gchar *fullpath; + + filename = g_file_info_get_name (info); + fullpath = g_build_filename (path, filename, NULL); + g_object_unref (info); + + mate_wp_xml_load_xml (data, fullpath); + g_free (fullpath); + } + g_file_enumerator_close (enumerator, NULL, NULL); + + mate_wp_xml_add_monitor (directory, data); + + g_object_unref (directory); +} + +void mate_wp_xml_load_list (AppearanceData *data) { + const char * const *system_data_dirs; + gchar * datadir; + gchar * wpdbfile; + gint i; + + wpdbfile = g_build_filename (g_get_home_dir (), + ".mate2", + "backgrounds.xml", + NULL); + + if (g_file_test (wpdbfile, G_FILE_TEST_EXISTS)) { + mate_wp_xml_load_xml (data, wpdbfile); + } else { + g_free (wpdbfile); + wpdbfile = g_build_filename (g_get_home_dir (), + ".mate2", + "wp-list.xml", + NULL); + if (g_file_test (wpdbfile, G_FILE_TEST_EXISTS)) { + mate_wp_xml_load_xml (data, wpdbfile); + } + } + g_free (wpdbfile); + + datadir = g_build_filename (g_get_user_data_dir (), + "mate-background-properties", + NULL); + mate_wp_xml_load_from_dir (datadir, data); + g_free (datadir); + + system_data_dirs = g_get_system_data_dirs (); + for (i = 0; system_data_dirs[i]; i++) { + datadir = g_build_filename (system_data_dirs[i], + "mate-background-properties", + NULL); + mate_wp_xml_load_from_dir (datadir, data); + g_free (datadir); + } + + mate_wp_xml_load_from_dir (WALLPAPER_DATADIR, data); + + mate_wp_load_legacy (data); +} + +static void mate_wp_list_flatten (const gchar * key, MateWPItem * item, + GSList ** list) { + g_return_if_fail (key != NULL); + g_return_if_fail (item != NULL); + + *list = g_slist_prepend (*list, item); +} + +void mate_wp_xml_save_list (AppearanceData *data) { + xmlDoc * wplist; + xmlNode * root, * wallpaper, * item; + GSList * list = NULL; + gchar * wpfile; + + g_hash_table_foreach (data->wp_hash, + (GHFunc) mate_wp_list_flatten, &list); + g_hash_table_destroy (data->wp_hash); + list = g_slist_reverse (list); + + wpfile = g_build_filename (g_get_home_dir (), + "/.mate2", + "backgrounds.xml", + NULL); + + xmlKeepBlanksDefault (0); + + wplist = xmlNewDoc ((xmlChar *)"1.0"); + xmlCreateIntSubset (wplist, (xmlChar *)"wallpapers", NULL, (xmlChar *)"mate-wp-list.dtd"); + root = xmlNewNode (NULL, (xmlChar *)"wallpapers"); + xmlDocSetRootElement (wplist, root); + + while (list != NULL) { + MateWPItem * wpitem = list->data; + const char * none = "(none)"; + gchar * filename; + const gchar * scale, * shade; + gchar * pcolor, * scolor; + + if (!strcmp (wpitem->filename, none) || + (g_utf8_validate (wpitem->filename, -1, NULL) && + g_file_test (wpitem->filename, G_FILE_TEST_EXISTS))) + filename = g_strdup (wpitem->filename); + else + filename = g_filename_to_utf8 (wpitem->filename, -1, NULL, NULL, NULL); + + pcolor = gdk_color_to_string (wpitem->pcolor); + scolor = gdk_color_to_string (wpitem->scolor); + scale = wp_item_option_to_string (wpitem->options); + shade = wp_item_shading_to_string (wpitem->shade_type); + + wallpaper = xmlNewChild (root, NULL, (xmlChar *)"wallpaper", NULL); + mate_wp_xml_set_bool (wallpaper, (xmlChar *)"deleted", wpitem->deleted); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"name", (xmlChar *)wpitem->name); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"filename", (xmlChar *)filename); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"options", (xmlChar *)scale); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"shade_type", (xmlChar *)shade); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"pcolor", (xmlChar *)pcolor); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"scolor", (xmlChar *)scolor); + g_free (pcolor); + g_free (scolor); + g_free (filename); + + list = g_slist_delete_link (list, list); + mate_wp_item_free (wpitem); + } + xmlSaveFormatFile (wpfile, wplist, 1); + xmlFreeDoc (wplist); + g_free (wpfile); +} diff --git a/capplets/appearance/mate-wp-xml.h b/capplets/appearance/mate-wp-xml.h new file mode 100644 index 00000000..795487ff --- /dev/null +++ b/capplets/appearance/mate-wp-xml.h @@ -0,0 +1,28 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _MATE_WP_XML_H_ +#define _MATE_WP_XML_H_ + +void mate_wp_xml_load_list (AppearanceData *data); +void mate_wp_xml_save_list (AppearanceData *data); + +#endif + diff --git a/capplets/appearance/theme-installer.c b/capplets/appearance/theme-installer.c new file mode 100644 index 00000000..fec1f001 --- /dev/null +++ b/capplets/appearance/theme-installer.c @@ -0,0 +1,801 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" + +#include +#include +#include +#include +#include +#include + +#include "capplet-util.h" +#include "file-transfer-dialog.h" +#include "theme-installer.h" +#include "theme-util.h" + +enum { + THEME_INVALID, + THEME_ICON, + THEME_MATE, + THEME_GTK, + THEME_ENGINE, + THEME_MARCO, + THEME_CURSOR, + THEME_ICON_CURSOR +}; + +enum { + TARGZ, + TARBZ, + DIRECTORY +}; + +static gboolean +cleanup_tmp_dir (GIOSchedulerJob *job, + GCancellable *cancellable, + const gchar *tmp_dir) +{ + GFile *directory; + + directory = g_file_new_for_path (tmp_dir); + capplet_file_delete_recursive (directory, NULL); + g_object_unref (directory); + + return FALSE; +} + +static int +file_theme_type (const gchar *dir) +{ + gchar *filename = NULL; + gboolean exists; + + if (!dir) + return THEME_INVALID; + + filename = g_build_filename (dir, "index.theme", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); + + if (exists) { + GPatternSpec *pattern; + gchar *file_contents = NULL; + gsize file_size; + gboolean match; + + g_file_get_contents (filename, &file_contents, &file_size, NULL); + g_free (filename); + + pattern = g_pattern_spec_new ("*[Icon Theme]*"); + match = g_pattern_match_string (pattern, file_contents); + g_pattern_spec_free (pattern); + + if (match) { + pattern = g_pattern_spec_new ("*Directories=*"); + match = g_pattern_match_string (pattern, file_contents); + g_pattern_spec_free (pattern); + g_free (file_contents); + + if (match) { + /* check if we have a cursor, too */ + filename = g_build_filename (dir, "cursors", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_DIR); + g_free (filename); + + if (exists) + return THEME_ICON_CURSOR; + else + return THEME_ICON; + } + return THEME_CURSOR; + } + + pattern = g_pattern_spec_new ("*[X-GNOME-Metatheme]*"); + match = g_pattern_match_string (pattern, file_contents); + g_pattern_spec_free (pattern); + g_free (file_contents); + + if (match) + return THEME_MATE; + } else { + g_free (filename); + } + + filename = g_build_filename (dir, "gtk-2.0", "gtkrc", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); + g_free (filename); + + if (exists) + return THEME_GTK; + + filename = g_build_filename (dir, "metacity-1", "metacity-theme-1.xml", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); + g_free (filename); + + if (exists) + return THEME_MARCO; + + /* cursor themes don't necessarily have an index.theme */ + filename = g_build_filename (dir, "cursors", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_DIR); + g_free (filename); + + if (exists) + return THEME_CURSOR; + + filename = g_build_filename (dir, "configure", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE); + g_free (filename); + + if (exists) + return THEME_ENGINE; + + return THEME_INVALID; +} + +static void +transfer_cancel_cb (GtkWidget *dialog, + gchar *path) +{ + GFile *todelete; + + todelete = g_file_new_for_path (path); + capplet_file_delete_recursive (todelete, NULL); + + g_object_unref (todelete); + g_free (path); + gtk_widget_destroy (dialog); +} + +static void +missing_utility_message_dialog (GtkWindow *parent, + const gchar *utility) +{ + GtkWidget *dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot install theme")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("The %s utility is not installed."), utility); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +/* this works around problems when doing fork/exec in a threaded app + * with some locks being held/waited on in different threads. + * + * we do the idle callback so that the async xfer has finished and + * cleaned up its vfs job. otherwise it seems the slave thread gets + * woken up and it removes itself from the job queue before it is + * supposed to. very strange. + * + * see bugzilla.gnome.org #86141 for details + */ +static gboolean +process_local_theme_tgz_tbz (GtkWindow *parent, + const gchar *util, + const gchar *tmp_dir, + const gchar *archive) +{ + gboolean rc; + int status; + gchar *command, *filename, *zip, *tar; + + if (!(zip = g_find_program_in_path (util))) { + missing_utility_message_dialog (parent, util); + return FALSE; + } + if (!(tar = g_find_program_in_path ("tar"))) { + missing_utility_message_dialog (parent, "tar"); + g_free (zip); + return FALSE; + } + + filename = g_shell_quote (archive); + + /* this should be something more clever and nonblocking */ + command = g_strdup_printf ("sh -c 'cd \"%s\"; %s -d -c < \"%s\" | %s xf - '", + tmp_dir, zip, filename, tar); + g_free (zip); + g_free (tar); + g_free (filename); + + rc = (g_spawn_command_line_sync (command, NULL, NULL, &status, NULL) && status == 0); + g_free (command); + + if (rc == FALSE) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot install theme")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("There was a problem while extracting the theme.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } + + return rc; +} + +static gboolean +process_local_theme_archive (GtkWindow *parent, + gint filetype, + const gchar *tmp_dir, + const gchar *archive) +{ + if (filetype == TARGZ) + return process_local_theme_tgz_tbz (parent, "gzip", tmp_dir, archive); + else if (filetype == TARBZ) + return process_local_theme_tgz_tbz (parent, "bzip2", tmp_dir, archive); + else + return FALSE; +} + +static void +invalid_theme_dialog (GtkWindow *parent, + const gchar *filename, + gboolean maybe_theme_engine) +{ + GtkWidget *dialog; + const gchar *primary = _("There was an error installing the selected file"); + const gchar *secondary = _("\"%s\" does not appear to be a valid theme."); + const gchar *engine = _("\"%s\" does not appear to be a valid theme. It may be a theme engine which you need to compile."); + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", primary); + if (maybe_theme_engine) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), engine, filename); + else + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), secondary, filename); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static gboolean +mate_theme_install_real (GtkWindow *parent, + gint filetype, + const gchar *tmp_dir, + const gchar *theme_name, + gboolean ask_user) +{ + gboolean success = TRUE; + GtkWidget *dialog, *apply_button; + GFile *theme_source_dir, *theme_dest_dir; + GError *error = NULL; + gint theme_type; + gchar *target_dir = NULL; + + /* What type of theme is it? */ + theme_type = file_theme_type (tmp_dir); + switch (theme_type) { + case THEME_ICON: + case THEME_CURSOR: + case THEME_ICON_CURSOR: + target_dir = g_build_path (G_DIR_SEPARATOR_S, + g_get_home_dir (), ".icons", + theme_name, NULL); + break; + case THEME_MATE: + target_dir = g_build_path (G_DIR_SEPARATOR_S, + g_get_home_dir (), ".themes", + theme_name, NULL); + break; + case THEME_MARCO: + case THEME_GTK: + target_dir = g_build_path (G_DIR_SEPARATOR_S, + g_get_home_dir (), ".themes", + theme_name, NULL); + break; + case THEME_ENGINE: + invalid_theme_dialog (parent, theme_name, TRUE); + return FALSE; + default: + invalid_theme_dialog (parent, theme_name, FALSE); + return FALSE; + } + + /* see if there is an icon theme lurking in this package */ + if (theme_type == THEME_MATE) { + gchar *path; + + path = g_build_path (G_DIR_SEPARATOR_S, + tmp_dir, "icons", NULL); + if (g_file_test (path, G_FILE_TEST_IS_DIR) + && (file_theme_type (path) == THEME_ICON)) { + gchar *new_path, *update_icon_cache; + GFile *new_file; + GFile *src_file; + + src_file = g_file_new_for_path (path); + new_path = g_build_path (G_DIR_SEPARATOR_S, + g_get_home_dir (), + ".icons", + theme_name, NULL); + new_file = g_file_new_for_path (new_path); + + if (!g_file_move (src_file, new_file, G_FILE_COPY_NONE, + NULL, NULL, NULL, &error)) { + g_warning ("Error while moving from `%s' to `%s': %s", + path, new_path, error->message); + g_error_free (error); + error = NULL; + } + g_object_unref (new_file); + g_object_unref (src_file); + + /* update icon cache - shouldn't really matter if this fails */ + update_icon_cache = g_strdup_printf ("gtk-update-icon-cache %s", new_path); + g_spawn_command_line_async (update_icon_cache, NULL); + g_free (update_icon_cache); + + g_free (new_path); + } + g_free (path); + } + + /* Move the dir to the target dir */ + theme_source_dir = g_file_new_for_path (tmp_dir); + theme_dest_dir = g_file_new_for_path (target_dir); + + if (!g_file_move (theme_source_dir, theme_dest_dir, + G_FILE_COPY_OVERWRITE, NULL, NULL, + NULL, &error)) { + gchar *str; + + str = g_strdup_printf (_("Installation for theme \"%s\" failed."), theme_name); + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", + str); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", error->message); + + g_free (str); + g_error_free (error); + error = NULL; + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + success = FALSE; + } else { + if (theme_type == THEME_ICON || theme_type == THEME_ICON_CURSOR) + { + gchar *update_icon_cache; + + /* update icon cache - shouldn't really matter if this fails */ + update_icon_cache = g_strdup_printf ("gtk-update-icon-cache %s", target_dir); + g_spawn_command_line_async (update_icon_cache, NULL); + + g_free (update_icon_cache); + } + + if (ask_user) { + /* Ask to apply theme (if we can) */ + if (theme_type == THEME_GTK + || theme_type == THEME_MARCO + || theme_type == THEME_ICON + || theme_type == THEME_CURSOR + || theme_type == THEME_ICON_CURSOR) { + /* TODO: currently cannot apply "mate themes" */ + gchar *str; + + str = g_strdup_printf (_("The theme \"%s\" has been installed."), theme_name); + dialog = gtk_message_dialog_new_with_markup (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_NONE, + "%s", + str); + g_free (str); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Would you like to apply it now, or keep your current theme?")); + + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("Keep Current Theme"), + GTK_RESPONSE_CLOSE); + + apply_button = gtk_button_new_with_label (_("Apply New Theme")); + gtk_button_set_image (GTK_BUTTON (apply_button), + gtk_image_new_from_stock (GTK_STOCK_APPLY, + GTK_ICON_SIZE_BUTTON)); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), apply_button, GTK_RESPONSE_APPLY); + gtk_widget_set_can_default (apply_button, TRUE); + gtk_widget_show (apply_button); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_APPLY); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY) { + /* apply theme here! */ + MateConfClient *mateconf_client; + + mateconf_client = mateconf_client_get_default (); + + switch (theme_type) { + case THEME_GTK: + mateconf_client_set_string (mateconf_client, GTK_THEME_KEY, theme_name, NULL); + break; + case THEME_MARCO: + mateconf_client_set_string (mateconf_client, MARCO_THEME_KEY, theme_name, NULL); + break; + case THEME_ICON: + mateconf_client_set_string (mateconf_client, ICON_THEME_KEY, theme_name, NULL); + break; + case THEME_CURSOR: + mateconf_client_set_string (mateconf_client, CURSOR_THEME_KEY, theme_name, NULL); + break; + case THEME_ICON_CURSOR: + mateconf_client_set_string (mateconf_client, ICON_THEME_KEY, theme_name, NULL); + mateconf_client_set_string (mateconf_client, CURSOR_THEME_KEY, theme_name, NULL); + break; + default: + break; + } + + g_object_unref (mateconf_client); + } + } else { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + _("MATE Theme %s correctly installed"), + theme_name); + gtk_dialog_run (GTK_DIALOG (dialog)); + } + gtk_widget_destroy (dialog); + } + } + + g_free (target_dir); + + return success; +} + +static void +process_local_theme (GtkWindow *parent, + const char *path) +{ + GtkWidget *dialog; + gint filetype; + + if (g_str_has_suffix (path, ".tar.gz") + || g_str_has_suffix (path, ".tgz") + || g_str_has_suffix(path, ".gtp")) { + filetype = TARGZ; + } else if (g_str_has_suffix (path, ".tar.bz2")) { + filetype = TARBZ; + } else if (g_file_test (path, G_FILE_TEST_IS_DIR)) { + filetype = DIRECTORY; + } else { + gchar *filename; + filename = g_path_get_basename (path); + invalid_theme_dialog (parent, filename, FALSE); + g_free (filename); + return; + } + + if (filetype == DIRECTORY) { + gchar *name = g_path_get_basename (path); + mate_theme_install_real (parent, + filetype, + path, + name, + TRUE); + g_free (name); + } else { + /* Create a temp directory and uncompress file there */ + GDir *dir; + const gchar *name; + gchar *tmp_dir; + gboolean ok; + gint n_themes; + GFile *todelete; + + tmp_dir = g_strdup_printf ("%s/.themes/.theme-%u", + g_get_home_dir (), + g_random_int ()); + + if ((g_mkdir (tmp_dir, 0700)) != 0) { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Failed to create temporary directory")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_free (tmp_dir); + return; + } + + if (!process_local_theme_archive (parent, filetype, tmp_dir, path) + || ((dir = g_dir_open (tmp_dir, 0, NULL)) == NULL)) { + g_io_scheduler_push_job ((GIOSchedulerJobFunc) cleanup_tmp_dir, + g_strdup (tmp_dir), + g_free, + G_PRIORITY_DEFAULT, + NULL); + g_free (tmp_dir); + return; + } + + todelete = g_file_new_for_path (path); + g_file_delete (todelete, NULL, NULL); + g_object_unref (todelete); + + /* See whether we have multiple themes to install. If so, + * we won't ask the user whether to apply the new theme + * after installation. */ + n_themes = 0; + for (name = g_dir_read_name (dir); + name && n_themes <= 1; + name = g_dir_read_name (dir)) { + gchar *theme_dir; + + theme_dir = g_build_filename (tmp_dir, name, NULL); + + if (g_file_test (theme_dir, G_FILE_TEST_IS_DIR)) + ++n_themes; + + g_free (theme_dir); + } + g_dir_rewind (dir); + + ok = TRUE; + for (name = g_dir_read_name (dir); name && ok; + name = g_dir_read_name (dir)) { + gchar *theme_dir; + + theme_dir = g_build_filename (tmp_dir, name, NULL); + + if (g_file_test (theme_dir, G_FILE_TEST_IS_DIR)) + ok = mate_theme_install_real (parent, + filetype, + theme_dir, + name, + n_themes == 1); + + g_free (theme_dir); + } + g_dir_close (dir); + + if (ok && n_themes > 1) { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + _("New themes have been successfully installed.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } + g_io_scheduler_push_job ((GIOSchedulerJobFunc) cleanup_tmp_dir, + tmp_dir, g_free, + G_PRIORITY_DEFAULT, NULL); + } +} + +typedef struct +{ + GtkWindow *parent; + char *path; +} TransferData; + +static void +transfer_done_cb (GtkWidget *dialog, + TransferData *tdata) +{ + gdk_threads_enter (); + /* XXX: path should be on the local filesystem by now? */ + + if (dialog != NULL) { + gtk_widget_destroy (dialog); + } + + process_local_theme (tdata->parent, tdata->path); + + g_free (tdata->path); + g_free (tdata); + + gdk_threads_leave (); +} + +void +mate_theme_install (GFile *file, + GtkWindow *parent) +{ + GtkWidget *dialog; + gchar *path, *base; + GList *src, *target; + const gchar *template; + TransferData *tdata; + + if (file == NULL) { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("No theme file location specified to install")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + return; + } + + /* see if someone dropped a local directory */ + if (g_file_is_native (file) + && g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL) == G_FILE_TYPE_DIRECTORY) { + path = g_file_get_path (file); + process_local_theme (parent, path); + g_free (path); + return; + } + + /* we can't tell if this is an icon theme yet, so just make a + * temporary copy in .themes */ + path = g_build_filename (g_get_home_dir (), ".themes", NULL); + + if (access (path, X_OK | W_OK) != 0) { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Insufficient permissions to install the theme in:\n%s"), path); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_free (path); + return; + } + + base = g_file_get_basename (file); + + if (g_str_has_suffix (base, ".tar.gz") + || g_str_has_suffix (base, ".tgz") + || g_str_has_suffix (base, ".gtp")) + template = "mate-theme-%d.gtp"; + else if (g_str_has_suffix (base, ".tar.bz2")) + template = "mate-theme-%d.tar.bz2"; + else { + invalid_theme_dialog (parent, base, FALSE); + g_free (base); + return; + } + g_free (base); + + src = g_list_append (NULL, g_object_ref (file)); + + path = NULL; + do { + gchar *file_tmp; + + g_free (path); + file_tmp = g_strdup_printf (template, g_random_int ()); + path = g_build_filename (g_get_home_dir (), ".themes", file_tmp, NULL); + g_free (file_tmp); + } while (g_file_test (path, G_FILE_TEST_EXISTS)); + + tdata = g_new0 (TransferData, 1); + tdata->parent = parent; + tdata->path = path; + + dialog = file_transfer_dialog_new_with_parent (parent); + g_signal_connect (dialog, + "cancel", + (GCallback) transfer_cancel_cb, path); + g_signal_connect (dialog, + "done", + (GCallback) transfer_done_cb, tdata); + + target = g_list_append (NULL, g_file_new_for_path (path)); + file_transfer_dialog_copy_async (FILE_TRANSFER_DIALOG (dialog), + src, + target, + FILE_TRANSFER_DIALOG_DEFAULT, + G_PRIORITY_DEFAULT); + gtk_widget_show (dialog); + + /* don't free the path since we're using that for the signals */ + g_list_foreach (src, (GFunc) g_object_unref, NULL); + g_list_free (src); + g_list_foreach (target, (GFunc) g_object_unref, NULL); + g_list_free (target); +} + +void +mate_theme_installer_run (GtkWindow *parent, + const gchar *filename) +{ + static gboolean running_theme_install = FALSE; + static gchar old_folder[512] = ""; + GtkWidget *dialog; + GtkFileFilter *filter; + + if (running_theme_install) + return; + + running_theme_install = TRUE; + + if (filename == NULL) + filename = old_folder; + + dialog = gtk_file_chooser_dialog_new (_("Select Theme"), + parent, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OPEN, + GTK_RESPONSE_ACCEPT, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Theme Packages")); + gtk_file_filter_add_mime_type (filter, "application/x-bzip-compressed-tar"); + gtk_file_filter_add_mime_type (filter, "application/x-compressed-tar"); + gtk_file_filter_add_mime_type (filter, "application/x-mate-theme-package"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All Files")); + gtk_file_filter_add_pattern(filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + if (strcmp (old_folder, "")) + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), old_folder); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *uri_selected, *folder; + + uri_selected = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog)); + + folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog)); + g_strlcpy (old_folder, folder, 255); + g_free (folder); + + gtk_widget_destroy (dialog); + + if (uri_selected != NULL) { + GFile *file = g_file_new_for_uri (uri_selected); + g_free (uri_selected); + + mate_theme_install (file, parent); + g_object_unref (file); + } + } else { + gtk_widget_destroy (dialog); + } + + /* + * we're relying on the mate theme info module to pick up changes + * to the themes so we don't need to update the model here + */ + + running_theme_install = FALSE; +} diff --git a/capplets/appearance/theme-installer.h b/capplets/appearance/theme-installer.h new file mode 100644 index 00000000..c0b3f695 --- /dev/null +++ b/capplets/appearance/theme-installer.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef THEME_INSTALLER_H +#define THEME_INSTALLER_H + +void mate_theme_install (GFile *file, GtkWindow *parent); +void mate_theme_installer_run (GtkWindow *parent, const gchar *filename); + +#endif /* THEME_INSTALLER_H */ diff --git a/capplets/appearance/theme-save.c b/capplets/appearance/theme-save.c new file mode 100644 index 00000000..f4981468 --- /dev/null +++ b/capplets/appearance/theme-save.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" + +#include +#include +#include + +#include "theme-save.h" +#include "theme-util.h" +#include "capplet-util.h" + +static GQuark error_quark; +enum { + INVALID_THEME_NAME +}; + +/* taken from mate-desktop-item.c */ +static gchar * +escape_string_and_dup (const gchar *s) +{ + gchar *return_value, *p; + const gchar *q; + int len = 0; + + if (s == NULL) + return g_strdup(""); + + q = s; + while (*q) + { + len++; + if (strchr ("\n\r\t\\", *q) != NULL) + len++; + q++; + } + return_value = p = (gchar *) g_malloc (len + 1); + do + { + switch (*s) + { + case '\t': + *p++ = '\\'; + *p++ = 't'; + break; + case '\n': + *p++ = '\\'; + *p++ = 'n'; + break; + case '\r': + *p++ = '\\'; + *p++ = 'r'; + break; + case '\\': + *p++ = '\\'; + *p++ = '\\'; + break; + default: + *p++ = *s; + } + } + while (*s++); + return return_value; +} + +static gboolean +check_theme_name (const gchar *theme_name, + GError **error) +{ + if (theme_name == NULL) { + g_set_error (error, + error_quark, + INVALID_THEME_NAME, + _("Theme name must be present")); + return FALSE; + } + return TRUE; +} + +static gchar * +str_remove_slash (const gchar *src) +{ + const gchar *i; + gchar *rtn; + gint len = 0; + i = src; + + while (*i) { + if (*i != '/') + len++; + i++; + } + + rtn = (gchar *) g_malloc (len + 1); + while (*src) { + if (*src != '/') { + *rtn = *src; + rtn++; + } + src++; + } + *rtn = '\0'; + return rtn - len; +} + +static gboolean +setup_directory_structure (const gchar *theme_name, + GError **error) +{ + gchar *dir, *theme_name_dir; + gboolean retval = TRUE; + + theme_name_dir = str_remove_slash (theme_name); + + dir = g_build_filename (g_get_home_dir (), ".themes", NULL); + if (!g_file_test (dir, G_FILE_TEST_EXISTS)) + g_mkdir (dir, 0775); + g_free (dir); + + dir = g_build_filename (g_get_home_dir (), ".themes", theme_name_dir, NULL); + if (!g_file_test (dir, G_FILE_TEST_EXISTS)) + g_mkdir (dir, 0775); + g_free (dir); + + dir = g_build_filename (g_get_home_dir (), ".themes", theme_name_dir, "index.theme", NULL); + g_free (theme_name_dir); + + if (g_file_test (dir, G_FILE_TEST_EXISTS)) { + GtkDialog *dialog; + GtkWidget *button; + gint response; + + dialog = (GtkDialog *) gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + _("The theme already exists. Would you like to replace it?")); + button = gtk_dialog_add_button (dialog, _("_Overwrite"), GTK_RESPONSE_ACCEPT); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_stock (GTK_STOCK_SAVE, GTK_ICON_SIZE_BUTTON)); + response = gtk_dialog_run (dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); + retval = (response != GTK_RESPONSE_CANCEL); + } + g_free (dir); + + return retval; +} + +static gboolean +write_theme_to_disk (MateThemeMetaInfo *theme_info, + const gchar *theme_name, + const gchar *theme_description, + gboolean save_background, + GError **error) +{ + gchar* dir; + gchar* theme_name_dir; + GFile* tmp_file; + GFile* target_file; + GOutputStream* output; + + gchar* str; + gchar* current_background; + + MateConfClient* client; + const gchar* theme_header = "" + "[Desktop Entry]\n" + "Name=%s\n" + "Type=X-GNOME-Metatheme\n" + "Comment=%s\n" + "\n" + "[X-GNOME-Metatheme]\n" + "GtkTheme=%s\n" + "MetacityTheme=%s\n" + "IconTheme=%s\n"; + + theme_name_dir = str_remove_slash (theme_name); + dir = g_build_filename (g_get_home_dir (), ".themes", theme_name_dir, "index.theme~", NULL); + g_free (theme_name_dir); + + tmp_file = g_file_new_for_path (dir); + dir [strlen (dir) - 1] = '\000'; + target_file = g_file_new_for_path (dir); + g_free (dir); + + /* start making the theme file */ + str = g_strdup_printf(theme_header, theme_name, theme_description, theme_info->gtk_theme_name, theme_info->marco_theme_name, theme_info->icon_theme_name); + + output = G_OUTPUT_STREAM (g_file_replace (tmp_file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL)); + g_output_stream_write (output, str, strlen (str), NULL, NULL); + g_free (str); + + if (theme_info->gtk_color_scheme) { + gchar *a, *tmp; + tmp = g_strdup (theme_info->gtk_color_scheme); + for (a = tmp; *a != '\0'; a++) + if (*a == '\n') + *a = ','; + str = g_strdup_printf ("GtkColorScheme=%s\n", tmp); + g_output_stream_write (output, str, strlen (str), NULL, NULL); + + g_free (str); + g_free (tmp); + } + + if (theme_info->cursor_theme_name) { +#ifdef HAVE_XCURSOR + str = g_strdup_printf ("CursorTheme=%s\n" + "CursorSize=%i\n", + theme_info->cursor_theme_name, + theme_info->cursor_size); +#else + str = g_strdup_printf ("CursorFont=%s\n", theme_info->cursor_theme_name); +#endif + g_output_stream_write (output, str, strlen (str), NULL, NULL); + g_free (str); + } + + if (theme_info->notification_theme_name) { + str = g_strdup_printf ("NotificationTheme=%s\n", theme_info->notification_theme_name); + g_output_stream_write (output, str, strlen (str), NULL, NULL); + g_free (str); + } + + if (save_background) { + client = mateconf_client_get_default (); + current_background = mateconf_client_get_string (client, BACKGROUND_KEY, NULL); + + if (current_background != NULL) { + str = g_strdup_printf ("BackgroundImage=%s\n", current_background); + + g_output_stream_write (output, str, strlen (str), NULL, NULL); + + g_free (current_background); + g_free (str); + } + g_object_unref (client); + } + + g_file_move (tmp_file, target_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, NULL); + g_output_stream_close (output, NULL, NULL); + + g_object_unref (tmp_file); + g_object_unref (target_file); + + return TRUE; +} + +static gboolean +save_theme_to_disk (MateThemeMetaInfo *theme_info, + const gchar *theme_name, + const gchar *theme_description, + gboolean save_background, + GError **error) +{ + if (!check_theme_name (theme_name, error)) + return FALSE; + + if (!setup_directory_structure (theme_name, error)) + return FALSE; + + if (!write_theme_to_disk (theme_info, theme_name, theme_description, save_background, error)) + return FALSE; + + return TRUE; +} + +static void +save_dialog_response (GtkWidget *save_dialog, + gint response_id, + AppearanceData *data) +{ + if (response_id == GTK_RESPONSE_OK) { + GtkWidget *entry; + GtkWidget *text_view; + GtkTextBuffer *buffer; + GtkTextIter start_iter; + GtkTextIter end_iter; + gchar *buffer_text; + MateThemeMetaInfo *theme_info; + gchar *theme_description = NULL; + gchar *theme_name = NULL; + gboolean save_background; + GError *error = NULL; + + entry = appearance_capplet_get_widget (data, "save_dialog_entry"); + theme_name = escape_string_and_dup (gtk_entry_get_text (GTK_ENTRY (entry))); + + text_view = appearance_capplet_get_widget (data, "save_dialog_textview"); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); + gtk_text_buffer_get_start_iter (buffer, &start_iter); + gtk_text_buffer_get_end_iter (buffer, &end_iter); + buffer_text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE); + theme_description = escape_string_and_dup (buffer_text); + g_free (buffer_text); + theme_info = (MateThemeMetaInfo *) g_object_get_data (G_OBJECT (save_dialog), "meta-theme-info"); + save_background = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON ( + appearance_capplet_get_widget (data, "save_background_checkbutton"))); + + if (save_theme_to_disk (theme_info, theme_name, theme_description, save_background, &error)) { + /* remove the custom theme */ + GtkTreeIter iter; + + if (theme_find_in_model (GTK_TREE_MODEL (data->theme_store), "__custom__", &iter)) + gtk_list_store_remove (data->theme_store, &iter); + } + + g_free (theme_name); + g_free (theme_description); + g_clear_error (&error); + } + + gtk_widget_hide (save_dialog); +} + +static void +entry_text_changed (GtkEditable *editable, + AppearanceData *data) +{ + const gchar *text; + GtkWidget *button; + + text = gtk_entry_get_text (GTK_ENTRY (editable)); + button = appearance_capplet_get_widget (data, "save_dialog_save_button"); + + gtk_widget_set_sensitive (button, text != NULL && text[0] != '\000'); +} + +void +theme_save_dialog_run (MateThemeMetaInfo *theme_info, + AppearanceData *data) +{ + GtkWidget *entry; + GtkWidget *text_view; + GtkTextBuffer *text_buffer; + + entry = appearance_capplet_get_widget (data, "save_dialog_entry"); + text_view = appearance_capplet_get_widget (data, "save_dialog_textview"); + + if (data->theme_save_dialog == NULL) { + data->theme_save_dialog = appearance_capplet_get_widget (data, "theme_save_dialog"); + + g_signal_connect (data->theme_save_dialog, "response", (GCallback) save_dialog_response, data); + g_signal_connect (data->theme_save_dialog, "delete-event", (GCallback) gtk_true, NULL); + g_signal_connect (entry, "changed", (GCallback) entry_text_changed, data); + + error_quark = g_quark_from_string ("mate-theme-save"); + gtk_widget_set_size_request (text_view, 300, 100); + } + + gtk_entry_set_text (GTK_ENTRY (entry), ""); + entry_text_changed (GTK_EDITABLE (entry), data); + gtk_widget_grab_focus (entry); + + text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); + gtk_text_buffer_set_text (text_buffer, "", 0); + g_object_set_data (G_OBJECT (data->theme_save_dialog), "meta-theme-info", theme_info); + gtk_window_set_transient_for (GTK_WINDOW (data->theme_save_dialog), + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window"))); + gtk_widget_show (data->theme_save_dialog); +} diff --git a/capplets/appearance/theme-save.h b/capplets/appearance/theme-save.h new file mode 100644 index 00000000..e56f4041 --- /dev/null +++ b/capplets/appearance/theme-save.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void theme_save_dialog_run (MateThemeMetaInfo *theme_info, + AppearanceData *data); diff --git a/capplets/appearance/theme-util.c b/capplets/appearance/theme-util.c new file mode 100644 index 00000000..2305b0f9 --- /dev/null +++ b/capplets/appearance/theme-util.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" + +#include +#include +#include +#include + +#include "capplet-util.h" +#include "theme-util.h" + +gboolean +theme_is_writable (const gpointer theme) +{ + MateThemeCommonInfo *info = theme; + GFile *file; + GFileInfo *file_info; + gboolean writable; + + if (info == NULL || info->path == NULL) + return FALSE; + + file = g_file_new_for_path (info->path); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + g_object_unref (file); + + if (file_info == NULL) + return FALSE; + + writable = g_file_info_get_attribute_boolean (file_info, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); + g_object_unref (file_info); + + return writable; +} + +gboolean +theme_delete (const gchar *name, ThemeType type) +{ + gboolean rc; + GtkDialog *dialog; + gchar *theme_dir; + gint response; + MateThemeCommonInfo *theme; + GFile *dir; + gboolean del_empty_parent; + + dialog = (GtkDialog *) gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + _("Would you like to delete this theme?")); + gtk_dialog_add_button (dialog, GTK_STOCK_DELETE, GTK_RESPONSE_ACCEPT); + response = gtk_dialog_run (dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); + if (response != GTK_RESPONSE_ACCEPT) + return FALSE; + + /* Most theme types are put into separate subdirectories. For those + we want to delete those directories as well. */ + del_empty_parent = TRUE; + + switch (type) { + case THEME_TYPE_GTK: + theme = (MateThemeCommonInfo *) mate_theme_info_find (name); + theme_dir = g_build_filename (theme->path, "gtk-2.0", NULL); + break; + + case THEME_TYPE_ICON: + theme = (MateThemeCommonInfo *) mate_theme_icon_info_find (name); + theme_dir = g_path_get_dirname (theme->path); + del_empty_parent = FALSE; + break; + + case THEME_TYPE_WINDOW: + theme = (MateThemeCommonInfo *) mate_theme_info_find (name); + theme_dir = g_build_filename (theme->path, "marco-1", NULL); + break; + + case THEME_TYPE_META: + theme = (MateThemeCommonInfo *) mate_theme_meta_info_find (name); + theme_dir = g_strdup (theme->path); + break; + + case THEME_TYPE_CURSOR: + theme = (MateThemeCommonInfo *) mate_theme_cursor_info_find (name); + theme_dir = g_build_filename (theme->path, "cursors", NULL); + break; + + default: + return FALSE; + } + + dir = g_file_new_for_path (theme_dir); + g_free (theme_dir); + + if (!capplet_file_delete_recursive (dir, NULL)) { + GtkWidget *info_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Theme cannot be deleted")); + gtk_dialog_run (GTK_DIALOG (info_dialog)); + gtk_widget_destroy (info_dialog); + rc = FALSE; + } else { + if (del_empty_parent) { + /* also delete empty parent directories */ + GFile *parent = g_file_get_parent (dir); + g_file_delete (parent, NULL, NULL); + g_object_unref (parent); + } + rc = TRUE; + } + + g_object_unref (dir); + return rc; +} + +gboolean +theme_model_iter_last (GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter walk, prev; + gboolean valid; + + valid = gtk_tree_model_get_iter_first (model, &walk); + + if (valid) { + do { + prev = walk; + valid = gtk_tree_model_iter_next (model, &walk); + } while (valid); + + *iter = prev; + return TRUE; + } + return FALSE; +} + +gboolean +theme_find_in_model (GtkTreeModel *model, const gchar *name, GtkTreeIter *iter) +{ + GtkTreeIter walk; + gboolean valid; + gchar *test; + + if (!name) + return FALSE; + + for (valid = gtk_tree_model_get_iter_first (model, &walk); valid; + valid = gtk_tree_model_iter_next (model, &walk)) + { + gtk_tree_model_get (model, &walk, COL_NAME, &test, -1); + + if (test) { + gint cmp = strcmp (test, name); + g_free (test); + + if (!cmp) { + if (iter) + *iter = walk; + return TRUE; + } + } + } + + return FALSE; +} + +gboolean +packagekit_available (void) +{ + DBusGConnection *connection; + DBusGProxy *proxy; + gboolean available = FALSE; + + connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + if (connection == NULL) { + return FALSE; + } + + proxy = dbus_g_proxy_new_for_name (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + org_freedesktop_DBus_name_has_owner (proxy, + "org.freedesktop.PackageKit", + &available, + NULL); + + g_object_unref (proxy); + dbus_g_connection_unref (connection); + + return available; +} + +void theme_install_file(GtkWindow* parent, const gchar* path) +{ + DBusGConnection* connection; + DBusGProxy* proxy; + GError* error = NULL; + gboolean ret; + + connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + + if (connection == NULL) + { + g_warning("Could not get session bus"); + return; + } + + proxy = dbus_g_proxy_new_for_name(connection, + "org.freedesktop.PackageKit", + "/org/freedesktop/PackageKit", + "org.freedesktop.PackageKit"); + + + ret = dbus_g_proxy_call(proxy, "InstallProvideFile", &error, + G_TYPE_STRING, path, + G_TYPE_INVALID, G_TYPE_INVALID); + + g_object_unref(proxy); + + if (!ret) + { + GtkWidget* dialog = gtk_message_dialog_new(NULL, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Could not install theme engine")); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG (dialog), "%s", error->message); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + g_error_free(error); + } + + dbus_g_connection_unref(connection); +} diff --git a/capplets/appearance/theme-util.h b/capplets/appearance/theme-util.h new file mode 100644 index 00000000..8bf91302 --- /dev/null +++ b/capplets/appearance/theme-util.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define GTK_THEME_KEY "/desktop/mate/interface/gtk_theme" +#define MARCO_THEME_KEY "/apps/marco/general/theme" +#define ICON_THEME_KEY "/desktop/mate/interface/icon_theme" +#define NOTIFICATION_THEME_KEY "/apps/notification-daemon/theme" +#define COLOR_SCHEME_KEY "/desktop/mate/interface/gtk_color_scheme" +#define LOCKDOWN_KEY "/desktop/mate/lockdown/disable_theme_settings" +#define BACKGROUND_KEY "/desktop/mate/background/picture_filename" +#define APPLICATION_FONT_KEY "/desktop/mate/interface/font_name" +#define DOCUMENTS_FONT_KEY "/desktop/mate/interface/document_font_name" +#define DESKTOP_FONT_KEY "/apps/caja/preferences/desktop_font" +#define WINDOWTITLE_FONT_KEY "/apps/marco/general/titlebar_font" +#define MONOSPACE_FONT_KEY "/desktop/mate/interface/monospace_font_name" + +#ifdef HAVE_XCURSOR + #define CURSOR_THEME_KEY "/desktop/mate/peripherals/mouse/cursor_theme" + #define CURSOR_SIZE_KEY "/desktop/mate/peripherals/mouse/cursor_size" +#else + #define CURSOR_THEME_KEY "/desktop/mate/peripherals/mouse/cursor_font" +#endif + +enum { + COL_THUMBNAIL, + COL_LABEL, + COL_NAME, + NUM_COLS +}; + +typedef enum { + THEME_TYPE_GTK, + THEME_TYPE_WINDOW, + THEME_TYPE_ICON, + THEME_TYPE_META, + THEME_TYPE_CURSOR +} ThemeType; + +gboolean theme_is_writable(const gpointer theme); +gboolean theme_delete(const gchar* name, ThemeType type); + +gboolean theme_model_iter_last(GtkTreeModel* model, GtkTreeIter* iter); +gboolean theme_find_in_model(GtkTreeModel* model, const gchar* name, GtkTreeIter* iter); + +void theme_install_file(GtkWindow* parent, const gchar* path); +gboolean packagekit_available(void); -- cgit v1.2.1