[gnome-control-center/extensible-shell: 5/7] Add extension point support
- From: William Jon McCann <mccann src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnome-control-center/extensible-shell: 5/7] Add extension point support
- Date: Sat, 16 Jan 2010 10:12:37 +0000 (UTC)
commit bf072998d01973e41bcb0f7cfc7742b51d0ecea7
Author: William Jon McCann <jmccann redhat com>
Date: Mon Jan 11 21:26:47 2010 -0500
Add extension point support
Keyboard and some of appearance capplet have been ported to it.
capplets/appearance/Makefile.am | 73 +-
capplets/appearance/appearance-desktop.c | 5 +-
capplets/appearance/appearance-main.c | 2 +-
capplets/appearance/appearance-module.c | 42 +
capplets/appearance/cc-appearance-panel.c | 219 +++
capplets/appearance/cc-appearance-panel.h | 54 +
capplets/appearance/cc-background-item.c | 703 ++++++++
capplets/appearance/cc-background-item.h | 71 +
capplets/appearance/cc-background-page.c | 1811 ++++++++++++++++++++
capplets/appearance/cc-background-page.h | 55 +
capplets/appearance/cc-backgrounds-monitor.c | 714 ++++++++
capplets/appearance/cc-backgrounds-monitor.h | 69 +
capplets/appearance/gnome-wp-item.c | 3 -
capplets/appearance/gnome-wp-item.h | 1 -
capplets/appearance/gnome-wp-xml.c | 2 +-
capplets/keyboard/Makefile.am | 51 +-
capplets/keyboard/cc-keyboard-panel.c | 293 ++++
capplets/keyboard/cc-keyboard-panel.h | 54 +
.../keyboard/gnome-keyboard-properties-dialog.ui | 4 +-
capplets/keyboard/keyboard-module.c | 42 +
po/POTFILES.in | 5 +
shell/Makefile.am | 9 +-
shell/cc-page.c | 178 ++
shell/cc-page.h | 53 +
shell/cc-panel.c | 182 ++
shell/cc-panel.h | 55 +
shell/control-center.c | 155 ++-
27 files changed, 4811 insertions(+), 94 deletions(-)
---
diff --git a/capplets/appearance/Makefile.am b/capplets/appearance/Makefile.am
index b940d73..bfde011 100644
--- a/capplets/appearance/Makefile.am
+++ b/capplets/appearance/Makefile.am
@@ -3,15 +3,64 @@ SUBDIRS = data
# This is used in GNOMECC_CAPPLETS_CFLAGS
cappletname = appearance
+module_flags = -export_dynamic -avoid-version -module -no-undefined -export-symbols-regex '^g_io_module_(load|unload)'
+
+INCLUDES = \
+ $(METACITY_CFLAGS) \
+ $(GNOMECC_CAPPLETS_CFLAGS) \
+ $(FONT_CAPPLET_CFLAGS) \
+ -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \
+ -DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \
+ -DGNOMECC_UI_DIR="\"$(uidir)\"" \
+ -DGNOMECC_PIXMAP_DIR="\"$(pixmapdir)\"" \
+ -DWALLPAPER_DATADIR="\"$(wallpaperdir)\""
+
+AM_CFLAGS = -DGNOME_DESKTOP_USE_UNSTABLE_API
+
+noinst_LTLIBRARIES = libappearance-common.la
+
bin_PROGRAMS = gnome-appearance-properties
+libappearance_common_la_SOURCES = \
+ theme-installer.c \
+ theme-installer.h \
+ theme-save.c \
+ theme-save.h \
+ theme-util.c \
+ theme-util.h
+
+ccmodulesdir = $(libdir)/control-center-1/extensions
+ccmodules_LTLIBRARIES = libappearance.la
+
+libappearance_la_SOURCES = \
+ appearance-module.c \
+ cc-backgrounds-monitor.h \
+ cc-backgrounds-monitor.c \
+ cc-background-item.h \
+ cc-background-item.c \
+ cc-background-page.h \
+ cc-background-page.c \
+ cc-appearance-panel.h \
+ cc-appearance-panel.c
+
+libappearance_la_LDFLAGS = \
+ $(module_flags)
+
+libappearance_la_LIBADD = \
+ libappearance-common.la \
+ $(GNOMECC_CAPPLETS_LIBS) \
+ $(LIBGNOMEKBDUI_LIBS)
+
+libappearance_la_CFLAGS = \
+ -I$(top_builddir)/shell \
+ -I$(top_srcdir)/shell
+
gnome_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 \
@@ -24,16 +73,10 @@ gnome_appearance_properties_SOURCES = \
gnome-wp-item.h \
gnome-wp-xml.c \
gnome-wp-xml.h \
- theme-installer.c \
- theme-installer.h \
- theme-save.c \
- theme-save.h \
- theme-util.c \
- theme-util.h
-
-AM_CFLAGS = -DGNOME_DESKTOP_USE_UNSTABLE_API
+ appearance-main.c
gnome_appearance_properties_LDADD = \
+ libappearance-common.la \
$(top_builddir)/libwindow-settings/libgnome-window-settings.la \
$(top_builddir)/capplets/common/libcommon.la \
$(GNOMECC_CAPPLETS_LIBS) \
@@ -41,20 +84,10 @@ gnome_appearance_properties_LDADD = \
$(METACITY_LIBS)
gnome_appearance_properties_LDFLAGS = -export-dynamic
-gtkbuilderdir = $(pkgdatadir)/ui
+uidir = $(pkgdatadir)/ui
pixmapdir = $(pkgdatadir)/pixmaps
wallpaperdir = $(datadir)/gnome-background-properties
-INCLUDES = \
- $(METACITY_CFLAGS) \
- $(GNOMECC_CAPPLETS_CFLAGS) \
- $(FONT_CAPPLET_CFLAGS) \
- -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \
- -DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \
- -DGNOMECC_GTKBUILDER_DIR="\"$(gtkbuilderdir)\"" \
- -DGNOMECC_PIXMAP_DIR="\"$(pixmapdir)\"" \
- -DWALLPAPER_DATADIR="\"$(wallpaperdir)\""
-
CLEANFILES = $(GNOMECC_CAPPLETS_CLEANFILES)
-include $(top_srcdir)/git.mk
diff --git a/capplets/appearance/appearance-desktop.c b/capplets/appearance/appearance-desktop.c
index 2ae6bd6..401b4d2 100644
--- a/capplets/appearance/appearance-desktop.c
+++ b/capplets/appearance/appearance-desktop.c
@@ -192,10 +192,11 @@ wp_add_image (AppearanceData *data,
}
else
{
- item = gnome_wp_item_new (filename, data->wp_hash, data->thumb_factory);
+ item = gnome_wp_item_new (filename, data->thumb_factory);
if (item != NULL)
{
+ g_hash_table_insert (data->wp_hash, item->filename, item);
wp_props_load_wallpaper (item->filename, item, data);
}
}
@@ -999,7 +1000,7 @@ wp_load_stuffs (void *user_data)
item = g_hash_table_lookup (data->wp_hash, "(none)");
if (item == NULL)
{
- item = gnome_wp_item_new ("(none)", data->wp_hash, data->thumb_factory);
+ item = gnome_wp_item_new ("(none)", data->thumb_factory);
if (item != NULL)
{
wp_props_load_wallpaper (item->filename, item, data);
diff --git a/capplets/appearance/appearance-main.c b/capplets/appearance/appearance-main.c
index 4a4a718..0d7dbc6 100644
--- a/capplets/appearance/appearance-main.c
+++ b/capplets/appearance/appearance-main.c
@@ -43,7 +43,7 @@ init_appearance_data (int *argc, char ***argv, GOptionContext *context)
activate_settings_daemon ();
/* set up the data */
- uifile = g_build_filename (GNOMECC_GTKBUILDER_DIR, "appearance.ui",
+ uifile = g_build_filename (GNOMECC_UI_DIR, "appearance.ui",
NULL);
ui = gtk_builder_new ();
gtk_builder_add_from_file (ui, uifile, &err);
diff --git a/capplets/appearance/appearance-module.c b/capplets/appearance/appearance-module.c
new file mode 100644
index 0000000..8aae2fd
--- /dev/null
+++ b/capplets/appearance/appearance-module.c
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gio/gio.h>
+
+#include "cc-appearance-panel.h"
+
+void
+g_io_module_load (GIOModule *module)
+{
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+ cc_appearance_panel_register (module);
+}
+
+void
+g_io_module_unload (GIOModule *module)
+{
+}
diff --git a/capplets/appearance/cc-appearance-panel.c b/capplets/appearance/cc-appearance-panel.c
new file mode 100644
index 0000000..b0517a7
--- /dev/null
+++ b/capplets/appearance/cc-appearance-panel.c
@@ -0,0 +1,219 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnomeui/gnome-desktop-thumbnail.h>
+
+#include "cc-background-page.h"
+#include "cc-appearance-panel.h"
+
+#include "gconf-property-editor.h"
+#if 0
+#include "appearance-desktop.h"
+#include "appearance-font.h"
+#include "appearance-themes.h"
+#include "appearance-style.h"
+#endif
+#include "theme-installer.h"
+#include "theme-thumbnail.h"
+
+#define CC_APPEARANCE_PANEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_APPEARANCE_PANEL, CcAppearancePanelPrivate))
+
+#define WID(s) GTK_WIDGET (gtk_builder_get_object (builder, s))
+
+struct CcAppearancePanelPrivate
+{
+ GtkWidget *notebook;
+ CcPage *theme_page;
+ CcPage *background_page;
+ CcPage *font_page;
+};
+
+enum {
+ PROP_0,
+};
+
+static void cc_appearance_panel_class_init (CcAppearancePanelClass *klass);
+static void cc_appearance_panel_init (CcAppearancePanel *appearance_panel);
+static void cc_appearance_panel_finalize (GObject *object);
+
+G_DEFINE_DYNAMIC_TYPE (CcAppearancePanel, cc_appearance_panel, CC_TYPE_PANEL)
+
+static void
+cc_appearance_panel_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_appearance_panel_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+#if 0
+/* FIXME */
+static void
+install_theme (CcAppearancePanel *panel,
+ const char *filename)
+{
+ GFile *inst;
+ GtkWidget *toplevel;
+
+ g_assert (filename != NULL);
+
+ inst = g_file_new_for_commandline_arg (filename);
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (panel));
+ if (GTK_WIDGET_TOPLEVEL (toplevel)) {
+ gnome_theme_install (inst, GTK_WINDOW (toplevel));
+ } else {
+ gnome_theme_install (inst, NULL);
+ }
+ g_object_unref (inst);
+}
+#endif
+
+static void
+setup_panel (CcAppearancePanel *panel)
+{
+ GtkWidget *label;
+ char *display_name;
+
+ /* FIXME: should a panel subclass notebook? */
+ panel->priv->notebook = gtk_notebook_new ();
+ gtk_container_add (GTK_CONTAINER (panel), panel->priv->notebook);
+ gtk_widget_show (panel->priv->notebook);
+
+ /* FIXME: load pages */
+ panel->priv->background_page = cc_background_page_new ();
+ g_object_get (panel->priv->background_page,
+ "display-name", &display_name,
+ NULL);
+ label = gtk_label_new (display_name);
+ g_free (display_name);
+ gtk_notebook_append_page (GTK_NOTEBOOK (panel->priv->notebook), GTK_WIDGET (panel->priv->background_page), label);
+ gtk_widget_show (GTK_WIDGET (panel->priv->background_page));
+}
+
+static GObject *
+cc_appearance_panel_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ CcAppearancePanel *appearance_panel;
+
+ appearance_panel = CC_APPEARANCE_PANEL (G_OBJECT_CLASS (cc_appearance_panel_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ g_object_set (appearance_panel,
+ "display-name", _("Appearance"),
+ "id", "gnome-appearance-properties.desktop",
+ NULL);
+
+ //theme_thumbnail_factory_init (0, NULL);
+
+ setup_panel (appearance_panel);
+
+ return G_OBJECT (appearance_panel);
+}
+
+static void
+cc_appearance_panel_class_init (CcAppearancePanelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_appearance_panel_get_property;
+ object_class->set_property = cc_appearance_panel_set_property;
+ object_class->constructor = cc_appearance_panel_constructor;
+ object_class->finalize = cc_appearance_panel_finalize;
+
+ g_type_class_add_private (klass, sizeof (CcAppearancePanelPrivate));
+}
+
+static void
+cc_appearance_panel_class_finalize (CcAppearancePanelClass *klass)
+{
+}
+
+static void
+cc_appearance_panel_init (CcAppearancePanel *panel)
+{
+ GConfClient *client;
+
+ panel->priv = CC_APPEARANCE_PANEL_GET_PRIVATE (panel);
+
+ client = gconf_client_get_default ();
+ gconf_client_add_dir (client,
+ "/desktop/gnome/peripherals/appearance",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ gconf_client_add_dir (client, "/desktop/gnome/interface",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ g_object_unref (client);
+}
+
+static void
+cc_appearance_panel_finalize (GObject *object)
+{
+ CcAppearancePanel *appearance_panel;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CC_IS_APPEARANCE_PANEL (object));
+
+ appearance_panel = CC_APPEARANCE_PANEL (object);
+
+ g_return_if_fail (appearance_panel->priv != NULL);
+
+ G_OBJECT_CLASS (cc_appearance_panel_parent_class)->finalize (object);
+}
+
+void
+cc_appearance_panel_register (GIOModule *module)
+{
+ cc_appearance_panel_register_type (G_TYPE_MODULE (module));
+ g_io_extension_point_implement (CC_PANEL_EXTENSION_POINT_NAME,
+ CC_TYPE_APPEARANCE_PANEL,
+ "appearance",
+ 10);
+}
diff --git a/capplets/appearance/cc-appearance-panel.h b/capplets/appearance/cc-appearance-panel.h
new file mode 100644
index 0000000..c27629c
--- /dev/null
+++ b/capplets/appearance/cc-appearance-panel.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __CC_APPEARANCE_PANEL_H
+#define __CC_APPEARANCE_PANEL_H
+
+#include <gtk/gtk.h>
+#include "cc-panel.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_APPEARANCE_PANEL (cc_appearance_panel_get_type ())
+#define CC_APPEARANCE_PANEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_APPEARANCE_PANEL, CcAppearancePanel))
+#define CC_APPEARANCE_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_APPEARANCE_PANEL, CcAppearancePanelClass))
+#define CC_IS_APPEARANCE_PANEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_APPEARANCE_PANEL))
+#define CC_IS_APPEARANCE_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_APPEARANCE_PANEL))
+#define CC_APPEARANCE_PANEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_APPEARANCE_PANEL, CcAppearancePanelClass))
+
+typedef struct CcAppearancePanelPrivate CcAppearancePanelPrivate;
+
+typedef struct
+{
+ CcPanel parent;
+ CcAppearancePanelPrivate *priv;
+} CcAppearancePanel;
+
+typedef struct
+{
+ CcPanelClass parent_class;
+} CcAppearancePanelClass;
+
+GType cc_appearance_panel_get_type (void);
+void cc_appearance_panel_register (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* __CC_APPEARANCE_PANEL_H */
diff --git a/capplets/appearance/cc-background-item.c b/capplets/appearance/cc-background-item.c
new file mode 100644
index 0000000..ed48fef
--- /dev/null
+++ b/capplets/appearance/cc-background-item.c
@@ -0,0 +1,703 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+#include <gconf/gconf-client.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnomeui/gnome-bg.h>
+
+#include "cc-background-item.h"
+
+#define CC_BACKGROUND_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_BACKGROUND_ITEM, CcBackgroundItemPrivate))
+
+struct CcBackgroundItemPrivate
+{
+ /* properties */
+ char *name;
+ char *filename;
+ char *description;
+ char *placement;
+ char *shading;
+ char *primary_color;
+ char *secondary_color;
+ gboolean is_deleted;
+
+ /* internal */
+ GnomeBG *bg;
+ char *mime_type;
+ int width;
+ int height;
+};
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_FILENAME,
+ PROP_DESCRIPTION,
+ PROP_PLACEMENT,
+ PROP_SHADING,
+ PROP_PRIMARY_COLOR,
+ PROP_SECONDARY_COLOR,
+ PROP_IS_DELETED,
+};
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void cc_background_item_class_init (CcBackgroundItemClass *klass);
+static void cc_background_item_init (CcBackgroundItem *background_item);
+static void cc_background_item_finalize (GObject *object);
+
+G_DEFINE_TYPE (CcBackgroundItem, cc_background_item, G_TYPE_OBJECT)
+
+static GConfEnumStringPair placement_lookup[] = {
+ { GNOME_BG_PLACEMENT_CENTERED, "centered" },
+ { GNOME_BG_PLACEMENT_FILL_SCREEN, "stretched" },
+ { GNOME_BG_PLACEMENT_SCALED, "scaled" },
+ { GNOME_BG_PLACEMENT_ZOOMED, "zoom" },
+ { GNOME_BG_PLACEMENT_TILED, "wallpaper" },
+ { 0, NULL }
+};
+
+static GConfEnumStringPair shading_lookup[] = {
+ { GNOME_BG_COLOR_SOLID, "solid" },
+ { GNOME_BG_COLOR_H_GRADIENT, "horizontal-gradient" },
+ { GNOME_BG_COLOR_V_GRADIENT, "vertical-gradient" },
+ { 0, NULL }
+};
+
+static const char *
+placement_to_string (GnomeBGPlacement type)
+{
+ return gconf_enum_to_string (placement_lookup, type);
+}
+
+static const char *
+shading_to_string (GnomeBGColorType type)
+{
+ return gconf_enum_to_string (shading_lookup, type);
+}
+
+static GnomeBGPlacement
+string_to_placement (const char *option)
+{
+ int i = GNOME_BG_PLACEMENT_SCALED;
+ gconf_string_to_enum (placement_lookup, option, &i);
+ return i;
+}
+
+static GnomeBGColorType
+string_to_shading (const char *shading)
+{
+ int i = GNOME_BG_COLOR_SOLID;
+ gconf_string_to_enum (shading_lookup, shading, &i);
+ return i;
+}
+
+static GdkPixbuf *
+add_slideshow_frame (GdkPixbuf *pixbuf)
+{
+ GdkPixbuf *sheet;
+ GdkPixbuf *sheet2;
+ GdkPixbuf *tmp;
+ int w;
+ int 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;
+}
+
+static void
+set_bg_properties (CcBackgroundItem *item)
+{
+ int shading;
+ int placement;
+ GdkColor pcolor = { 0, 0, 0, 0 };
+ GdkColor scolor = { 0, 0, 0, 0 };
+
+ if (item->priv->filename)
+ gnome_bg_set_filename (item->priv->bg, item->priv->filename);
+
+ gdk_color_parse (item->priv->primary_color, &pcolor);
+ gdk_color_parse (item->priv->secondary_color, &scolor);
+ placement = string_to_placement (item->priv->placement);
+ shading = string_to_shading (item->priv->shading);
+
+ gnome_bg_set_color (item->priv->bg, shading, &pcolor, &scolor);
+ gnome_bg_set_placement (item->priv->bg, placement);
+}
+
+
+gboolean
+cc_background_item_changes_with_time (CcBackgroundItem *item)
+{
+ gboolean changes;
+
+ changes = FALSE;
+ if (item->priv->bg != NULL) {
+ changes = gnome_bg_changes_with_time (item->priv->bg);
+ }
+ return changes;
+}
+
+static void
+update_description (CcBackgroundItem *item)
+{
+ g_free (item->priv->description);
+ item->priv->description = NULL;
+
+ if (strcmp (item->priv->filename, "(none)") == 0) {
+ item->priv->description = g_strdup (item->priv->name);
+ } else {
+ const char *description;
+ char *size;
+ char *dirname;
+
+ dirname = g_path_get_dirname (item->priv->filename);
+
+ description = NULL;
+ size = NULL;
+
+ if (item->priv->mime_type != NULL) {
+ if (strcmp (item->priv->mime_type, "application/xml") == 0) {
+ if (gnome_bg_changes_with_time (item->priv->bg))
+ description = _("Slide Show");
+ else if (item->priv->width > 0 && item->priv->height > 0)
+ description = _("Image");
+ } else {
+ description = g_content_type_get_description (item->priv->mime_type);
+ }
+ }
+
+ if (gnome_bg_has_multiple_sizes (item->priv->bg)) {
+ size = g_strdup (_("multiple sizes"));
+ } else if (item->priv->width > 0 && item->priv->height > 0) {
+ /* translators: x pixel(s) by y pixel(s) */
+ size = g_strdup_printf ("%d %s by %d %s",
+ item->priv->width,
+ ngettext ("pixel", "pixels", item->priv->width),
+ item->priv->height,
+ ngettext ("pixel", "pixels", item->priv->height));
+ }
+
+ if (description != NULL && size != NULL) {
+ /* translators: <b>wallpaper name</b>
+ * mime type, size
+ * Folder: /path/to/file
+ */
+ item->priv->description = g_markup_printf_escaped (_("<b>%s</b>\n"
+ "%s, %s\n"
+ "Folder: %s"),
+ item->priv->name,
+ description,
+ size,
+ dirname);
+ } else {
+ /* translators: <b>wallpaper name</b>
+ * Image missing
+ * Folder: /path/to/file
+ */
+ item->priv->description = g_markup_printf_escaped (_("<b>%s</b>\n"
+ "%s\n"
+ "Folder: %s"),
+ item->priv->name,
+ _("Image missing"),
+ dirname);
+ }
+
+ g_free (size);
+ g_free (dirname);
+ }
+}
+
+GdkPixbuf *
+cc_background_item_get_frame_thumbnail (CcBackgroundItem *item,
+ GnomeDesktopThumbnailFactory *thumbs,
+ int width,
+ int height,
+ int frame)
+{
+ GdkPixbuf *pixbuf = NULL;
+
+ set_bg_properties (item);
+
+ if (frame != -1)
+ pixbuf = gnome_bg_create_frame_thumbnail (item->priv->bg,
+ thumbs,
+ gdk_screen_get_default (),
+ width,
+ height,
+ frame);
+ else
+ pixbuf = gnome_bg_create_thumbnail (item->priv->bg,
+ thumbs,
+ gdk_screen_get_default(),
+ width,
+ height);
+
+ if (pixbuf != NULL
+ && gnome_bg_changes_with_time (item->priv->bg)) {
+ GdkPixbuf *tmp;
+
+ tmp = add_slideshow_frame (pixbuf);
+ g_object_unref (pixbuf);
+ pixbuf = tmp;
+ }
+
+ gnome_bg_get_image_size (item->priv->bg,
+ thumbs,
+ width,
+ height,
+ &item->priv->width,
+ &item->priv->height);
+
+ update_description (item);
+
+ return pixbuf;
+}
+
+
+GdkPixbuf *
+cc_background_item_get_thumbnail (CcBackgroundItem *item,
+ GnomeDesktopThumbnailFactory *thumbs,
+ int width,
+ int height)
+{
+ return cc_background_item_get_frame_thumbnail (item, thumbs, width, height, -1);
+}
+
+static void
+update_info (CcBackgroundItem *item)
+{
+ GFile *file;
+ GFileInfo *info;
+
+ file = g_file_new_for_commandline_arg (item->priv->filename);
+
+ 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);
+
+ g_free (item->priv->mime_type);
+ item->priv->mime_type = NULL;
+
+ if (info == NULL
+ || g_file_info_get_content_type (info) == NULL) {
+ if (strcmp (item->priv->filename, "(none)") == 0) {
+ item->priv->mime_type = g_strdup ("image/x-no-data");
+ g_free (item->priv->name);
+ item->priv->name = g_strdup (_("No Desktop Background"));
+ //item->priv->size = 0;
+ }
+ } else {
+ if (item->priv->name == NULL) {
+ const char *name;
+
+ g_free (item->priv->name);
+
+ name = g_file_info_get_name (info);
+ if (g_utf8_validate (name, -1, NULL))
+ item->priv->name = g_strdup (name);
+ else
+ item->priv->name = g_filename_to_utf8 (name,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ }
+
+ item->priv->mime_type = g_strdup (g_file_info_get_content_type (info));
+
+#if 0
+ item->priv->size = g_file_info_get_size (info);
+ item->priv->mtime = g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+#endif
+ }
+
+ if (info != NULL)
+ g_object_unref (info);
+
+}
+
+static void
+on_bg_changed (GnomeBG *bg,
+ CcBackgroundItem *item)
+{
+ g_signal_emit (item, signals[CHANGED], 0);
+}
+
+gboolean
+cc_background_item_load (CcBackgroundItem *item)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ update_info (item);
+
+ ret = FALSE;
+ if (item->priv->mime_type != NULL
+ && (g_str_has_prefix (item->priv->mime_type, "image/")
+ || strcmp (item->priv->mime_type, "application/xml") == 0)) {
+ ret = TRUE;
+
+ set_bg_properties (item);
+ } else {
+ /* FIXME: return error message? */
+ /* unknown mime type */
+ }
+
+ update_description (item);
+
+ return TRUE;
+}
+
+static void
+_set_name (CcBackgroundItem *item,
+ const char *value)
+{
+ g_free (item->priv->name);
+ item->priv->name = g_strdup (value);
+}
+
+static void
+_set_filename (CcBackgroundItem *item,
+ const char *value)
+{
+ g_free (item->priv->filename);
+ item->priv->filename = g_strdup (value);
+}
+
+static void
+_set_description (CcBackgroundItem *item,
+ const char *value)
+{
+ g_free (item->priv->description);
+ item->priv->description = g_strdup (value);
+}
+
+static void
+_set_placement (CcBackgroundItem *item,
+ const char *value)
+{
+ g_free (item->priv->placement);
+ item->priv->placement = g_strdup (value);
+}
+
+static void
+_set_shading (CcBackgroundItem *item,
+ const char *value)
+{
+ g_free (item->priv->shading);
+ item->priv->shading = g_strdup (value);
+}
+
+static void
+_set_primary_color (CcBackgroundItem *item,
+ const char *value)
+{
+ g_free (item->priv->primary_color);
+ item->priv->primary_color = g_strdup (value);
+}
+
+static void
+_set_secondary_color (CcBackgroundItem *item,
+ const char *value)
+{
+ g_free (item->priv->secondary_color);
+ item->priv->secondary_color = g_strdup (value);
+}
+
+static void
+_set_is_deleted (CcBackgroundItem *item,
+ gboolean value)
+{
+ item->priv->is_deleted = value;
+}
+
+static void
+cc_background_item_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcBackgroundItem *self;
+
+ self = CC_BACKGROUND_ITEM (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ _set_name (self, g_value_get_string (value));
+ break;
+ case PROP_FILENAME:
+ _set_filename (self, g_value_get_string (value));
+ break;
+ case PROP_DESCRIPTION:
+ _set_description (self, g_value_get_string (value));
+ break;
+ case PROP_PLACEMENT:
+ _set_placement (self, g_value_get_string (value));
+ break;
+ case PROP_SHADING:
+ _set_shading (self, g_value_get_string (value));
+ break;
+ case PROP_PRIMARY_COLOR:
+ _set_primary_color (self, g_value_get_string (value));
+ break;
+ case PROP_SECONDARY_COLOR:
+ _set_secondary_color (self, g_value_get_string (value));
+ break;
+ case PROP_IS_DELETED:
+ _set_is_deleted (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_background_item_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcBackgroundItem *self;
+
+ self = CC_BACKGROUND_ITEM (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, self->priv->name);
+ break;
+ case PROP_FILENAME:
+ g_value_set_string (value, self->priv->filename);
+ break;
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, self->priv->description);
+ break;
+ case PROP_PLACEMENT:
+ g_value_set_string (value, self->priv->placement);
+ break;
+ case PROP_SHADING:
+ g_value_set_string (value, self->priv->shading);
+ break;
+ case PROP_PRIMARY_COLOR:
+ g_value_set_string (value, self->priv->primary_color);
+ break;
+ case PROP_SECONDARY_COLOR:
+ g_value_set_string (value, self->priv->secondary_color);
+ break;
+ case PROP_IS_DELETED:
+ g_value_set_boolean (value, self->priv->is_deleted);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+cc_background_item_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ CcBackgroundItem *background_item;
+
+ background_item = CC_BACKGROUND_ITEM (G_OBJECT_CLASS (cc_background_item_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ return G_OBJECT (background_item);
+}
+
+static void
+cc_background_item_class_init (CcBackgroundItemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_background_item_get_property;
+ object_class->set_property = cc_background_item_set_property;
+ object_class->constructor = cc_background_item_constructor;
+ object_class->finalize = cc_background_item_finalize;
+
+ signals [CHANGED]
+ = g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_object_class_install_property (object_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ "name",
+ "name",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_FILENAME,
+ g_param_spec_string ("filename",
+ "filename",
+ "filename",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_DESCRIPTION,
+ g_param_spec_string ("description",
+ "description",
+ "description",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_PLACEMENT,
+ g_param_spec_string ("placement",
+ "placement",
+ "placement",
+ "scaled",
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_SHADING,
+ g_param_spec_string ("shading",
+ "shading",
+ "shading",
+ "solid",
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_PRIMARY_COLOR,
+ g_param_spec_string ("primary-color",
+ "primary-color",
+ "primary-color",
+ "#000000000000",
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_SECONDARY_COLOR,
+ g_param_spec_string ("secondary-color",
+ "secondary-color",
+ "secondary-color",
+ "#000000000000",
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_IS_DELETED,
+ g_param_spec_boolean ("is-deleted",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (klass, sizeof (CcBackgroundItemPrivate));
+}
+
+static void
+cc_background_item_init (CcBackgroundItem *item)
+{
+ item->priv = CC_BACKGROUND_ITEM_GET_PRIVATE (item);
+
+ item->priv->bg = gnome_bg_new ();
+
+ g_signal_connect (item->priv->bg,
+ "changed",
+ G_CALLBACK (on_bg_changed),
+ item);
+
+ item->priv->shading = g_strdup ("solid");
+ item->priv->placement = g_strdup ("scaled");
+ item->priv->primary_color = g_strdup ("#000000000000");
+ item->priv->secondary_color = g_strdup ("#000000000000");
+}
+
+static void
+cc_background_item_finalize (GObject *object)
+{
+ CcBackgroundItem *item;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CC_IS_BACKGROUND_ITEM (object));
+
+ item = CC_BACKGROUND_ITEM (object);
+
+ g_return_if_fail (item->priv != NULL);
+
+ g_free (item->priv->name);
+ g_free (item->priv->filename);
+ g_free (item->priv->description);
+ g_free (item->priv->primary_color);
+ g_free (item->priv->secondary_color);
+ g_free (item->priv->mime_type);
+
+ if (item->priv->bg != NULL)
+ g_object_unref (item->priv->bg);
+
+ G_OBJECT_CLASS (cc_background_item_parent_class)->finalize (object);
+}
+
+CcBackgroundItem *
+cc_background_item_new (const char *filename)
+{
+ GObject *object;
+
+ object = g_object_new (CC_TYPE_BACKGROUND_ITEM,
+ "filename", filename,
+ NULL);
+
+ return CC_BACKGROUND_ITEM (object);
+}
diff --git a/capplets/appearance/cc-background-item.h b/capplets/appearance/cc-background-item.h
new file mode 100644
index 0000000..e8cc9d7
--- /dev/null
+++ b/capplets/appearance/cc-background-item.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __CC_BACKGROUND_ITEM_H
+#define __CC_BACKGROUND_ITEM_H
+
+#include <glib-object.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnomeui/gnome-desktop-thumbnail.h>
+#include "cc-background-item.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_BACKGROUND_ITEM (cc_background_item_get_type ())
+#define CC_BACKGROUND_ITEM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_BACKGROUND_ITEM, CcBackgroundItem))
+#define CC_BACKGROUND_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_BACKGROUND_ITEM, CcBackgroundItemClass))
+#define CC_IS_BACKGROUND_ITEM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_BACKGROUND_ITEM))
+#define CC_IS_BACKGROUND_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_BACKGROUND_ITEM))
+#define CC_BACKGROUND_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_BACKGROUND_ITEM, CcBackgroundItemClass))
+
+typedef struct CcBackgroundItemPrivate CcBackgroundItemPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CcBackgroundItemPrivate *priv;
+} CcBackgroundItem;
+
+typedef struct
+{
+ GObjectClass parent_class;
+ void (* changed) (CcBackgroundItem *item);
+} CcBackgroundItemClass;
+
+GType cc_background_item_get_type (void);
+
+CcBackgroundItem * cc_background_item_new (const char *filename);
+gboolean cc_background_item_load (CcBackgroundItem *item);
+gboolean cc_background_item_changes_with_time (CcBackgroundItem *item);
+
+GdkPixbuf * cc_background_item_get_thumbnail (CcBackgroundItem *item,
+ GnomeDesktopThumbnailFactory *thumbs,
+ int width,
+ int height);
+GdkPixbuf * cc_background_item_get_frame_thumbnail (CcBackgroundItem *item,
+ GnomeDesktopThumbnailFactory *thumbs,
+ int width,
+ int height,
+ int frame);
+
+G_END_DECLS
+
+#endif /* __CC_BACKGROUND_ITEM_H */
diff --git a/capplets/appearance/cc-background-page.c b/capplets/appearance/cc-background-page.c
new file mode 100644
index 0000000..58bbd80
--- /dev/null
+++ b/capplets/appearance/cc-background-page.c
@@ -0,0 +1,1811 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <gconf/gconf-client.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnomeui/gnome-desktop-thumbnail.h>
+
+#include "cc-background-page.h"
+#include "cc-background-item.h"
+#include "cc-backgrounds-monitor.h"
+
+#define CC_BACKGROUND_PAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_BACKGROUND_PAGE, CcBackgroundPagePrivate))
+
+#define WID(s) GTK_WIDGET (gtk_builder_get_object (builder, s))
+#define BACKGROUNDS_DIR "/usr/share/backgrounds"
+
+#define WP_PATH_KEY "/desktop/gnome/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"
+
+struct CcBackgroundPagePrivate
+{
+ CcBackgroundsMonitor *monitor;
+ GtkTreeModel *model;
+ GtkWidget *icon_view;
+ GtkWidget *remove_button;
+ GtkWidget *color_menu;
+ GtkWidget *style_menu;
+ GtkWidget *primary_color_picker;
+ GtkWidget *secondary_color_picker;
+ GtkWidget *file_chooser;
+ GtkWidget *file_chooser_preview;
+
+ GnomeDesktopThumbnailFactory *thumb_factory;
+
+ int frame;
+ int thumb_width;
+ int thumb_height;
+
+ gulong screen_size_handler;
+ gulong screen_monitors_handler;
+};
+
+enum {
+ PROP_0,
+};
+
+static void cc_background_page_class_init (CcBackgroundPageClass *klass);
+static void cc_background_page_init (CcBackgroundPage *background_page);
+static void cc_background_page_finalize (GObject *object);
+
+G_DEFINE_TYPE (CcBackgroundPage, cc_background_page, CC_TYPE_PAGE)
+
+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}
+};
+
+enum {
+ COL_PIXBUF,
+ COL_ITEM,
+};
+
+enum {
+ SHADE_SOLID = 0,
+ SHADE_H_GRADIENT,
+ SHADE_V_GRADIENT,
+};
+
+enum {
+ SCALE_TILE = 0,
+ SCALE_ZOOM,
+ SCALE_CENTER,
+ SCALE_SCALE,
+ SCALE_STRETCH,
+};
+
+static GConfEnumStringPair options_lookup[] = {
+ { 0, "wallpaper" },
+ { 1, "zoom" },
+ { 2, "centered" },
+ { 3, "scaled" },
+ { 4, "stretched" },
+ { 0, NULL }
+};
+
+static GConfEnumStringPair shade_lookup[] = {
+ { 0, "solid" },
+ { 1, "horizontal-gradient" },
+ { 2, "vertical-gradient" },
+ { 0, NULL }
+};
+
+static const char *
+option_to_string (int type)
+{
+ return gconf_enum_to_string (options_lookup, type);
+}
+
+static const char *
+shading_to_string (int type)
+{
+ return gconf_enum_to_string (shade_lookup, type);
+}
+
+static int
+string_to_option (const char *option)
+{
+ int i = 3;
+ gconf_string_to_enum (options_lookup, option, &i);
+ return i;
+}
+
+static int
+string_to_shade (const char *shade_type)
+{
+ int i = 0;
+ gconf_string_to_enum (shade_lookup, shade_type, &i);
+ return i;
+}
+
+static void
+cc_background_page_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_background_page_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+on_style_menu_changed (GtkComboBox *combobox,
+ CcBackgroundPage *page)
+{
+ GConfClient *client;
+ int options;
+
+ client = gconf_client_get_default ();
+ options = gtk_combo_box_get_active (GTK_COMBO_BOX (page->priv->style_menu));
+ if (gconf_client_key_is_writable (client, WP_OPTIONS_KEY, NULL))
+ gconf_client_set_string (client,
+ WP_OPTIONS_KEY,
+ option_to_string (options),
+ NULL);
+ g_object_unref (client);
+}
+
+static void
+on_color_menu_changed (GtkWidget *combobox,
+ CcBackgroundPage *page)
+{
+ GConfClient *client;
+ int shade_type;
+
+ client = gconf_client_get_default ();
+ shade_type = gtk_combo_box_get_active (GTK_COMBO_BOX (page->priv->color_menu));
+ if (gconf_client_key_is_writable (client, WP_SHADING_KEY, NULL))
+ gconf_client_set_string (client,
+ WP_SHADING_KEY,
+ shading_to_string (shade_type),
+ NULL);
+ g_object_unref (client);
+}
+
+static void
+on_color_changed (GtkWidget *widget,
+ CcBackgroundPage *page)
+{
+ GConfClient *client;
+ GdkColor pcolor;
+ GdkColor scolor;
+ char *spcolor;
+ char *sscolor;
+
+ gtk_color_button_get_color (GTK_COLOR_BUTTON (page->priv->primary_color_picker),
+ &pcolor);
+ gtk_color_button_get_color (GTK_COLOR_BUTTON (page->priv->secondary_color_picker),
+ &scolor);
+
+ spcolor = gdk_color_to_string (&pcolor);
+ sscolor = gdk_color_to_string (&scolor);
+
+ client = gconf_client_get_default ();
+ gconf_client_set_string (client, WP_PCOLOR_KEY, spcolor, NULL);
+ gconf_client_set_string (client, WP_SCOLOR_KEY, sscolor, NULL);
+ g_object_unref (client);
+
+ g_free (spcolor);
+ g_free (sscolor);
+}
+
+static GdkPixbuf *buttons[3];
+
+static void
+create_button_images (CcBackgroundPage *page)
+{
+ GtkIconSet *icon_set;
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *pb;
+ GdkPixbuf *pb2;
+ int i, w, h;
+
+ icon_set = gtk_style_lookup_icon_set (page->priv->icon_view->style, "gtk-media-play");
+ pb = gtk_icon_set_render_icon (icon_set,
+ page->priv->icon_view->style,
+ GTK_TEXT_DIR_RTL,
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_MENU,
+ page->priv->icon_view,
+ NULL);
+ pb2 = gtk_icon_set_render_icon (icon_set,
+ page->priv->icon_view->style,
+ GTK_TEXT_DIR_LTR,
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_MENU,
+ page->priv->icon_view,
+ 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 CcBackgroundItem *
+get_selected_item (CcBackgroundPage *page,
+ GtkTreeIter *piter)
+{
+ CcBackgroundItem *item;
+ GList *selected;
+ GtkTreeIter iter;
+ gboolean res;
+
+ item = NULL;
+
+ selected = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (page->priv->icon_view));
+
+ if (selected == NULL) {
+ return NULL;
+ }
+
+ res = gtk_tree_model_get_iter (page->priv->model,
+ &iter,
+ selected->data);
+ g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected);
+
+ if (res) {
+ if (piter != NULL) {
+ *piter = iter;
+ }
+
+ gtk_tree_model_get (page->priv->model, &iter, COL_ITEM, &item, -1);
+ }
+
+ return item;
+}
+
+static void
+ui_update_sensitivities (CcBackgroundPage *page)
+{
+ GConfClient *client;
+ CcBackgroundItem *item;
+ char *filename;
+
+ item = get_selected_item (page, NULL);
+ filename = NULL;
+ if (item != NULL) {
+ g_object_get (item, "filename", &filename, NULL);
+ g_object_unref (item);
+ }
+
+ client = gconf_client_get_default ();
+ if (!gconf_client_key_is_writable (client, WP_OPTIONS_KEY, NULL)
+ || (filename != NULL && !strcmp (filename, "(none)")))
+ gtk_widget_set_sensitive (page->priv->style_menu, FALSE);
+ else
+ gtk_widget_set_sensitive (page->priv->style_menu, TRUE);
+
+ if (!gconf_client_key_is_writable (client, WP_SHADING_KEY, NULL))
+ gtk_widget_set_sensitive (page->priv->color_menu, FALSE);
+ else
+ gtk_widget_set_sensitive (page->priv->color_menu, TRUE);
+
+ if (!gconf_client_key_is_writable (client, WP_PCOLOR_KEY, NULL))
+ gtk_widget_set_sensitive (page->priv->primary_color_picker, FALSE);
+ else
+ gtk_widget_set_sensitive (page->priv->primary_color_picker, TRUE);
+
+ if (!gconf_client_key_is_writable (client, WP_SCOLOR_KEY, NULL))
+ gtk_widget_set_sensitive (page->priv->secondary_color_picker, FALSE);
+ else
+ gtk_widget_set_sensitive (page->priv->secondary_color_picker, TRUE);
+
+ if (filename == NULL || strcmp (filename, "(none)") == 0)
+ gtk_widget_set_sensitive (page->priv->remove_button, FALSE);
+ else
+ gtk_widget_set_sensitive (page->priv->remove_button, TRUE);
+
+ g_object_unref (client);
+ g_free (filename);
+}
+
+static gboolean
+find_uri_in_model (GtkTreeModel *model,
+ const char *uri,
+ GtkTreeIter *piter)
+{
+ GtkTreeIter iter;
+ gboolean valid;
+
+ if (uri == NULL) {
+ return FALSE;
+ }
+
+ for (valid = gtk_tree_model_get_iter_first (model, &iter);
+ valid;
+ valid = gtk_tree_model_iter_next (model, &iter)) {
+ CcBackgroundItem *item;
+ char *filename;
+
+ item = NULL;
+ gtk_tree_model_get (model, &iter, COL_ITEM, &item, -1);
+ if (item == NULL)
+ continue;
+
+ filename = NULL;
+ g_object_get (item, "filename", &filename, NULL);
+ g_object_unref (item);
+
+ if (filename != NULL) {
+ int cmp = strcmp (filename, uri);
+ g_free (filename);
+
+ if (cmp == 0) {
+ if (piter != NULL)
+ *piter = iter;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+on_item_changed (CcBackgroundItem *item,
+ CcBackgroundPage *page)
+{
+ GtkTreeIter iter;
+ char *uri;
+
+ uri = NULL;
+ g_object_get (item, "filename", &uri, NULL);
+ if (find_uri_in_model (page->priv->model, uri, &iter)) {
+ GdkPixbuf *pixbuf;
+
+ g_signal_handlers_block_by_func (item, G_CALLBACK (on_item_changed), page);
+
+ pixbuf = cc_background_item_get_thumbnail (item,
+ page->priv->thumb_factory,
+ page->priv->thumb_width,
+ page->priv->thumb_height);
+ if (pixbuf != NULL) {
+ gtk_list_store_set (GTK_LIST_STORE (page->priv->model),
+ &iter,
+ COL_PIXBUF, pixbuf,
+ -1);
+ g_object_unref (pixbuf);
+ }
+
+ g_signal_handlers_unblock_by_func (item, G_CALLBACK (on_item_changed), page);
+ }
+
+ g_free (uri);
+}
+
+static void
+load_item (CcBackgroundPage *page,
+ CcBackgroundItem *item)
+{
+ GtkTreeIter iter;
+ GdkPixbuf *pixbuf;
+ gboolean deleted;
+
+ g_signal_connect (item,
+ "changed",
+ G_CALLBACK (on_item_changed),
+ page);
+
+ g_object_get (item, "is-deleted", &deleted, NULL);
+ if (deleted == TRUE)
+ return;
+
+ gtk_list_store_append (GTK_LIST_STORE (page->priv->model), &iter);
+
+ pixbuf = cc_background_item_get_thumbnail (item,
+ page->priv->thumb_factory,
+ page->priv->thumb_width,
+ page->priv->thumb_height);
+
+ gtk_list_store_set (GTK_LIST_STORE (page->priv->model),
+ &iter,
+ COL_PIXBUF, pixbuf,
+ COL_ITEM, item,
+ -1);
+
+ if (pixbuf != NULL)
+ g_object_unref (pixbuf);
+
+}
+
+static void
+load_item_iter (CcBackgroundItem *item,
+ CcBackgroundPage *page)
+{
+ load_item (page, item);
+}
+
+static gboolean
+reload_item (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ CcBackgroundPage *page)
+{
+ CcBackgroundItem *item;
+ GdkPixbuf *pixbuf;
+
+ gtk_tree_model_get (model, iter, COL_ITEM, &item, -1);
+
+ pixbuf = cc_background_item_get_thumbnail (item,
+ page->priv->thumb_factory,
+ page->priv->thumb_width,
+ page->priv->thumb_height);
+ g_object_unref (item);
+
+ if (pixbuf) {
+ gtk_list_store_set (GTK_LIST_STORE (page->priv->model),
+ iter,
+ COL_PIXBUF, pixbuf,
+ -1);
+ g_object_unref (pixbuf);
+ }
+
+ return FALSE;
+}
+
+static gdouble
+get_monitor_aspect_ratio_for_widget (GtkWidget *widget)
+{
+ gdouble aspect;
+ int 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 (CcBackgroundPage *page)
+{
+ gdouble aspect;
+
+ aspect = get_monitor_aspect_ratio_for_widget (page->priv->icon_view);
+ if (aspect > 1) {
+ /* portrait */
+ page->priv->thumb_width = LIST_IMAGE_SIZE / aspect;
+ page->priv->thumb_height = LIST_IMAGE_SIZE;
+ } else {
+ page->priv->thumb_width = LIST_IMAGE_SIZE;
+ page->priv->thumb_height = LIST_IMAGE_SIZE * aspect;
+ }
+}
+
+static void
+reload_wallpapers (CcBackgroundPage *page)
+{
+ compute_thumbnail_sizes (page);
+ gtk_tree_model_foreach (page->priv->model,
+ (GtkTreeModelForeachFunc)reload_item,
+ page);
+}
+
+static void
+select_item (CcBackgroundPage *page,
+ CcBackgroundItem *item,
+ gboolean scroll)
+{
+ GtkTreeIter iter;
+ char *uri;
+
+ if (item == NULL)
+ return;
+
+ uri = NULL;
+ g_object_get (item, "filename", &uri, NULL);
+ g_debug ("Selecting item %s", uri);
+ if (find_uri_in_model (page->priv->model, uri, &iter)) {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (page->priv->model, &iter);
+ gtk_icon_view_select_path (GTK_ICON_VIEW (page->priv->icon_view), path);
+ if (scroll)
+ gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (page->priv->icon_view), path, FALSE, 0.5, 0.0);
+
+ gtk_tree_path_free (path);
+ }
+ g_free (uri);
+}
+
+static CcBackgroundItem *
+add_item_for_filename (CcBackgroundPage *page,
+ const char *filename)
+{
+ CcBackgroundItem *item;
+ gboolean added;
+
+ if (filename == NULL)
+ return NULL;
+
+ added = FALSE;
+
+ item = cc_background_item_new (filename);
+ if (cc_background_item_load (item)) {
+ added = cc_backgrounds_monitor_add_item (page->priv->monitor, item);
+ }
+ if (!added) {
+ g_object_unref (item);
+ item = NULL;
+ }
+
+ return item;
+}
+
+/* FIXME: move to base class? */
+static GtkWidget *
+get_toplevel_window (CcBackgroundPage *page)
+{
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (page));
+ if (!GTK_WIDGET_TOPLEVEL (toplevel)) {
+ return NULL;
+ }
+
+ return toplevel;
+}
+
+static void
+enable_busy_cursor (CcBackgroundPage *page,
+ gboolean enable)
+{
+ GdkCursor *cursor;
+ GtkWidget *toplevel;
+ GdkWindow *window;
+
+ toplevel = get_toplevel_window (page);
+ if (toplevel == NULL)
+ return;
+
+ window = gtk_widget_get_window (toplevel);
+
+ if (enable) {
+ cursor = gdk_cursor_new_for_display (gdk_display_get_default (),
+ GDK_WATCH);
+ } else {
+ cursor = NULL;
+ }
+
+ gdk_window_set_cursor (window, cursor);
+
+ if (cursor != NULL)
+ gdk_cursor_unref (cursor);
+}
+
+static void
+add_items_for_filenames (CcBackgroundPage *page,
+ GSList *filenames)
+{
+ CcBackgroundItem *item;
+
+ enable_busy_cursor (page, TRUE);
+
+ item = NULL;
+ while (filenames != NULL) {
+ gchar *uri = filenames->data;
+
+ item = add_item_for_filename (page, uri);
+ g_object_unref (item);
+ filenames = g_slist_remove (filenames, uri);
+ g_free (uri);
+ }
+
+ enable_busy_cursor (page, FALSE);
+
+ if (item != NULL) {
+ select_item (page, item, TRUE);
+ }
+}
+
+static void
+ui_update_option_menu (CcBackgroundPage *page,
+ int value)
+{
+ gtk_combo_box_set_active (GTK_COMBO_BOX (page->priv->style_menu),
+ value);
+}
+
+static void
+ui_update_shade_menu (CcBackgroundPage *page,
+ int value)
+{
+ gtk_combo_box_set_active (GTK_COMBO_BOX (page->priv->color_menu),
+ value);
+
+ if (value == SHADE_SOLID)
+ gtk_widget_hide (page->priv->secondary_color_picker);
+ else
+ gtk_widget_show (page->priv->secondary_color_picker);
+}
+
+static void
+on_item_added (CcBackgroundsMonitor *monitor,
+ CcBackgroundItem *item,
+ CcBackgroundPage *page)
+{
+ g_debug ("Item added");
+ load_item (page, item);
+}
+
+static void
+on_item_removed (CcBackgroundsMonitor *monitor,
+ CcBackgroundItem *item,
+ CcBackgroundPage *page)
+{
+ GtkTreeIter iter;
+ char *uri;
+
+ uri = NULL;
+ g_object_get (item, "filename", &uri, NULL);
+ g_debug ("Item removed: %s", uri);
+ if (find_uri_in_model (page->priv->model, uri, &iter)) {
+ gtk_list_store_remove (GTK_LIST_STORE (page->priv->model), &iter);
+ }
+ g_free (uri);
+}
+
+static void
+update_ui_from_gconf (CcBackgroundPage *page)
+{
+ GConfClient *client;
+ char *uri;
+ char *path;
+ CcBackgroundItem *item;
+ GtkTreeIter iter;
+ gboolean already_added;
+
+ client = gconf_client_get_default ();
+
+ uri = gconf_client_get_string (client,
+ WP_FILE_KEY,
+ NULL);
+
+ if (uri != NULL && *uri == '\0') {
+ g_free (uri);
+ uri = NULL;
+ }
+ if (uri == NULL)
+ uri = g_strdup ("(none)");
+
+ path = NULL;
+ if (g_utf8_validate (uri, -1, NULL)
+ && g_file_test (uri, G_FILE_TEST_EXISTS))
+ path = g_strdup (uri);
+ else
+ path = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
+ g_free (uri);
+
+ /* now update or add item */
+ already_added = FALSE;
+ item = NULL;
+ if (find_uri_in_model (page->priv->model, path, &iter)) {
+ gtk_tree_model_get (page->priv->model, &iter, COL_ITEM, &item, -1);
+ already_added = TRUE;
+ } else {
+ item = cc_background_item_new (path);
+ }
+ g_free (path);
+
+ if (item != NULL) {
+ char *placement;
+ char *shading;
+ char *primary_color;
+ char *secondary_color;
+
+ placement = gconf_client_get_string (client,
+ WP_OPTIONS_KEY,
+ NULL);
+ shading = gconf_client_get_string (client,
+ WP_SHADING_KEY,
+ NULL);
+ primary_color = gconf_client_get_string (client,
+ WP_PCOLOR_KEY,
+ NULL);
+ secondary_color = gconf_client_get_string (client,
+ WP_SCOLOR_KEY,
+ NULL);
+ if (placement == NULL)
+ placement = g_strdup ("none");
+
+ g_object_unref (client);
+
+
+ g_object_set (item,
+ "primary-color", primary_color,
+ "secondary-color", secondary_color,
+ "placement", placement,
+ "shading", shading,
+ NULL);
+
+ if (cc_background_item_load (item)) {
+ if (!already_added)
+ cc_backgrounds_monitor_add_item (page->priv->monitor, item);
+ }
+ select_item (page, item, TRUE);
+ g_object_unref (item);
+ }
+}
+
+static gboolean
+load_stuffs (CcBackgroundPage *page)
+{
+ GList *items;
+
+ compute_thumbnail_sizes (page);
+
+ g_debug ("Beginning to load");
+ cc_backgrounds_monitor_load (page->priv->monitor);
+ g_debug ("Finished loading");
+ items = cc_backgrounds_monitor_get_items (page->priv->monitor);
+ g_list_foreach (items, (GFunc)load_item_iter, page);
+ g_list_free (items);
+
+ g_signal_connect (page->priv->monitor,
+ "item-added",
+ G_CALLBACK (on_item_added),
+ page);
+ g_signal_connect (page->priv->monitor,
+ "item-removed",
+ G_CALLBACK (on_item_removed),
+ page);
+
+ update_ui_from_gconf (page);
+
+ return FALSE;
+}
+
+static void
+on_icon_view_realize (GtkWidget *widget,
+ CcBackgroundPage *page);
+
+static void
+on_icon_view_realize (GtkWidget *widget,
+ CcBackgroundPage *page)
+{
+ g_idle_add ((GSourceFunc)load_stuffs, page);
+ /* only run once */
+ g_signal_handlers_disconnect_by_func (widget, on_icon_view_realize, page);
+}
+
+static void
+next_frame (CcBackgroundPage *page,
+ GtkCellRenderer *cr,
+ int direction)
+{
+ CcBackgroundItem *item;
+ GtkTreeIter iter;
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *pb;
+ int frame;
+
+ pixbuf = NULL;
+
+ frame = page->priv->frame + direction;
+ item = get_selected_item (page, &iter);
+ if (item == NULL)
+ return;
+
+ if (frame >= 0)
+ pixbuf = cc_background_item_get_frame_thumbnail (item,
+ page->priv->thumb_factory,
+ page->priv->thumb_width,
+ page->priv->thumb_height,
+ frame);
+ if (pixbuf) {
+ gtk_list_store_set (GTK_LIST_STORE (page->priv->model),
+ &iter,
+ COL_PIXBUF, pixbuf,
+ -1);
+ g_object_unref (pixbuf);
+ page->priv->frame = frame;
+ }
+
+ pb = buttons[1];
+ if (direction < 0) {
+ if (frame == 0)
+ pb = buttons[0];
+ } else {
+ pixbuf = cc_background_item_get_frame_thumbnail (item,
+ page->priv->thumb_factory,
+ page->priv->thumb_width,
+ page->priv->thumb_height,
+ frame + 1);
+ if (pixbuf)
+ g_object_unref (pixbuf);
+ else
+ pb = buttons[2];
+ }
+ g_object_set (cr, "pixbuf", pb, NULL);
+ g_object_unref (item);
+}
+
+static gboolean
+on_icon_view_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ CcBackgroundPage *page)
+{
+ GtkCellRenderer *cell;
+
+ if (event->type != GDK_BUTTON_PRESS)
+ return FALSE;
+
+ if (gtk_icon_view_get_item_at_pos (GTK_ICON_VIEW (widget),
+ event->x,
+ event->y,
+ NULL,
+ &cell)) {
+ if (g_object_get_data (G_OBJECT (cell), "buttons")) {
+ int w;
+ int 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),
+ event->x + w,
+ event->y,
+ NULL, &cell2)
+ && cell == cell2) {
+ next_frame (page, cell, -1);
+ } else {
+ next_frame (page, cell, 1);
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+update_gconf_from_item (CcBackgroundPage *page,
+ CcBackgroundItem *item)
+{
+ GConfClient *client;
+ GConfChangeSet *cs;
+ char *pcolor;
+ char *scolor;
+ char *filename;
+ char *shade;
+ char *scale;
+
+ if (item == NULL) {
+ return;
+ }
+
+ cs = gconf_change_set_new ();
+
+ g_object_get (item,
+ "filename", &filename,
+ "shading", &shade,
+ "placement", &scale,
+ "primary-color", &pcolor,
+ "secondary-color", &scolor,
+ NULL);
+
+ if (strcmp (filename, "(none)") == 0) {
+ gconf_change_set_set_string (cs, WP_OPTIONS_KEY, "none");
+ gconf_change_set_set_string (cs, WP_FILE_KEY, "");
+ } else {
+ gchar *uri;
+
+ if (g_utf8_validate (filename, -1, NULL))
+ uri = g_strdup (filename);
+ else
+ uri = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+
+ if (uri == NULL) {
+ g_warning ("Failed to convert filename to UTF-8: %s", filename);
+ } else {
+ gconf_change_set_set_string (cs, WP_FILE_KEY, uri);
+ g_free (uri);
+ }
+
+ gconf_change_set_set_string (cs,
+ WP_OPTIONS_KEY,
+ scale);
+ }
+
+ gconf_change_set_set_string (cs,
+ WP_SHADING_KEY,
+ shade);
+ gconf_change_set_set_string (cs, WP_PCOLOR_KEY, pcolor);
+ gconf_change_set_set_string (cs, WP_SCOLOR_KEY, scolor);
+
+ g_free (filename);
+ g_free (shade);
+ g_free (scale);
+ g_free (pcolor);
+ g_free (scolor);
+
+ client = gconf_client_get_default ();
+ gconf_client_commit_change_set (client, cs, TRUE, NULL);
+ g_object_unref (client);
+
+ gconf_change_set_unref (cs);
+}
+
+static void
+on_icon_view_selection_changed (GtkIconView *view,
+ CcBackgroundPage *page)
+{
+ CcBackgroundItem *item;
+ GtkCellRenderer *cr;
+ GList *cells;
+ GList *l;
+
+ /* update the frame buttons */
+ page->priv->frame = -1;
+ cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (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);
+
+ item = get_selected_item (page, NULL);
+ g_debug ("Selection changed %p", item);
+ if (item != NULL) {
+ update_gconf_from_item (page, item);
+ g_object_unref (item);
+ }
+}
+
+static void
+buttons_cell_data_func (GtkCellLayout *layout,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ CcBackgroundPage *page)
+{
+ GtkTreePath *path;
+ CcBackgroundItem *item;
+ gboolean visible;
+
+ path = gtk_tree_model_get_path (model, iter);
+
+ visible = FALSE;
+ if (gtk_icon_view_path_is_selected (GTK_ICON_VIEW (layout), path)) {
+ item = get_selected_item (page, NULL);
+ if (item != NULL) {
+ visible = cc_background_item_changes_with_time (item);
+ g_object_unref (item);
+ }
+ }
+
+ g_object_set (G_OBJECT (cell),
+ "visible", visible,
+ NULL);
+
+ gtk_tree_path_free (path);
+}
+
+static void
+update_file_chooser_preview (GtkFileChooser *chooser,
+ CcBackgroundPage *page)
+{
+ char *uri;
+ GdkPixbuf *pixbuf;
+ const char *mime_type;
+ GFile *file;
+ GFileInfo *file_info;
+
+ gtk_file_chooser_set_preview_widget_active (chooser, TRUE);
+
+ uri = gtk_file_chooser_get_preview_uri (chooser);
+ if (uri == NULL) {
+ return;
+
+ }
+
+ 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);
+
+ mime_type = NULL;
+ if (file_info != NULL) {
+ mime_type = g_file_info_get_content_type (file_info);
+ g_object_unref (file_info);
+ }
+
+ pixbuf = NULL;
+ if (mime_type != NULL) {
+ pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (page->priv->thumb_factory,
+ uri,
+ mime_type);
+ }
+
+ if (pixbuf != NULL) {
+ gtk_image_set_from_pixbuf (GTK_IMAGE (page->priv->file_chooser_preview), pixbuf);
+ g_object_unref (pixbuf);
+ } else {
+ gtk_image_set_from_stock (GTK_IMAGE (page->priv->file_chooser_preview),
+ "gtk-dialog-question",
+ GTK_ICON_SIZE_DIALOG);
+ }
+}
+
+static void
+create_filechooser (CcBackgroundPage *page)
+{
+ const char *start_dir;
+ const char *pictures;
+ GtkFileFilter *filter;
+ GtkWidget *toplevel;
+ GtkWindow *window;
+
+ window = NULL;
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (page));
+ if (GTK_WIDGET_TOPLEVEL (toplevel)) {
+ window = GTK_WINDOW (toplevel);
+ }
+
+ page->priv->file_chooser = gtk_file_chooser_dialog_new (_("Add Wallpaper"),
+ 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 (page->priv->file_chooser), GTK_RESPONSE_OK);
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (page->priv->file_chooser), TRUE);
+ gtk_file_chooser_set_use_preview_label (GTK_FILE_CHOOSER (page->priv->file_chooser), FALSE);
+
+ start_dir = g_get_home_dir ();
+ if (g_file_test (BACKGROUNDS_DIR, G_FILE_TEST_IS_DIR)) {
+ gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (page->priv->file_chooser),
+ BACKGROUNDS_DIR,
+ NULL);
+ start_dir = BACKGROUNDS_DIR;
+ }
+
+ 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 (GTK_FILE_CHOOSER (page->priv->file_chooser),
+ pictures,
+ NULL);
+ start_dir = pictures;
+ }
+
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (page->priv->file_chooser),
+ 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 (GTK_FILE_CHOOSER (page->priv->file_chooser),
+ 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 (page->priv->file_chooser), filter);
+
+ page->priv->file_chooser_preview = gtk_image_new ();
+ gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (page->priv->file_chooser),
+ page->priv->file_chooser_preview);
+ gtk_widget_set_size_request (page->priv->file_chooser_preview, 128, -1);
+
+ gtk_widget_show (page->priv->file_chooser_preview);
+
+ g_signal_connect (page->priv->file_chooser,
+ "update-preview",
+ (GCallback) update_file_chooser_preview,
+ page);
+}
+
+static void
+on_add_button_clicked (GtkWidget *widget,
+ CcBackgroundPage *page)
+{
+ GSList *files;
+
+ if (page->priv->file_chooser == NULL)
+ create_filechooser (page);
+
+ switch (gtk_dialog_run (GTK_DIALOG (page->priv->file_chooser))) {
+ case GTK_RESPONSE_OK:
+ files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (page->priv->file_chooser));
+ add_items_for_filenames (page, files);
+ case GTK_RESPONSE_CANCEL:
+ default:
+ gtk_widget_hide (page->priv->file_chooser);
+ break;
+ }
+}
+
+static void
+on_remove_button_clicked (GtkWidget *widget,
+ CcBackgroundPage *page)
+{
+ CcBackgroundItem *item;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ item = get_selected_item (page, &iter);
+ if (item == NULL) {
+ return;
+ }
+
+ if (cc_backgrounds_monitor_remove_item (page->priv->monitor, item)) {
+ path = gtk_tree_model_get_path (page->priv->model, &iter);
+ } else {
+ path = gtk_tree_path_new_first ();
+ }
+
+ gtk_icon_view_select_path (GTK_ICON_VIEW (page->priv->icon_view), path);
+ gtk_icon_view_set_cursor (GTK_ICON_VIEW (page->priv->icon_view), path, NULL, FALSE);
+ gtk_tree_path_free (path);
+
+ g_object_unref (item);
+}
+
+static void
+wp_drag_received (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ CcBackgroundPage *page)
+{
+ if (info == TARGET_URI_LIST
+ || info == TARGET_BGIMAGE) {
+ GSList *realuris = NULL;
+ gchar **uris;
+
+ uris = g_uri_list_extract_uris ((char *) selection_data->data);
+ if (uris != NULL) {
+ char **uri;
+
+ enable_busy_cursor (page, TRUE);
+
+ 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);
+ }
+
+ add_items_for_filenames (page, realuris);
+
+ enable_busy_cursor (page, FALSE);
+
+ g_strfreev (uris);
+ }
+ }
+}
+
+static void
+wp_drag_get_data (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint type,
+ guint time,
+ CcBackgroundPage *page)
+{
+ CcBackgroundItem *item;
+ char *uris[2];
+ char *filename;
+
+ if (type != TARGET_URI_LIST) {
+ return;
+ }
+
+ item = get_selected_item (page, NULL);
+ if (item == NULL) {
+ return;
+ }
+ g_object_get (item,
+ "filename", &filename,
+ NULL);
+ uris[0] = g_filename_to_uri (filename, NULL, NULL);
+ uris[1] = NULL;
+
+ gtk_selection_data_set_uris (selection_data, uris);
+
+ g_free (uris[0]);
+ g_free (filename);
+ g_object_unref (item);
+}
+
+static gboolean
+on_query_tooltip (GtkWidget *widget,
+ int x,
+ int y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ CcBackgroundPage *page)
+{
+ GtkTreeIter iter;
+ CcBackgroundItem *item;
+
+ if (gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (page->priv->icon_view),
+ &x, &y,
+ keyboard_mode,
+ NULL,
+ NULL,
+ &iter)) {
+ gtk_tree_model_get (page->priv->model, &iter, COL_ITEM, &item, -1);
+ if (item != NULL) {
+ char *description;
+ g_object_get (item, "description", &description, NULL);
+ gtk_tooltip_set_markup (tooltip, description);
+ g_free (description);
+ g_object_unref (item);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint
+sort_model_iter (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ CcBackgroundPage *page)
+
+{
+ CcBackgroundItem *itema;
+ CcBackgroundItem *itemb;
+ char *filenamea;
+ char *filenameb;
+ char *descriptiona;
+ char *descriptionb;
+ int retval;
+
+ gtk_tree_model_get (model, a, COL_ITEM, &itema, -1);
+ gtk_tree_model_get (model, b, COL_ITEM, &itemb, -1);
+
+ g_object_get (itema,
+ "filename", &filenamea,
+ "description", &descriptiona,
+ NULL);
+ g_object_get (itemb,
+ "filename", &filenameb,
+ "description", &descriptionb,
+ NULL);
+ if (strcmp (filenamea, "(none)") == 0) {
+ retval = -1;
+ } else if (strcmp (filenameb, "(none)") == 0) {
+ retval = 1;
+ } else if (descriptiona != NULL && descriptionb != NULL) {
+ retval = g_utf8_collate (descriptiona, descriptionb);
+ } else {
+ retval = 0;
+ }
+ g_free (filenamea);
+ g_free (filenameb);
+ g_free (descriptiona);
+ g_free (descriptionb);
+ if (itema != NULL)
+ g_object_unref (itema);
+ if (itemb != NULL)
+ g_object_unref (itemb);
+
+ return retval;
+}
+
+static void
+screen_monitors_changed (GdkScreen *screen,
+ CcBackgroundPage *page)
+{
+ reload_wallpapers (page);
+}
+
+static void
+setup_page (CcBackgroundPage *page)
+{
+ GtkBuilder *builder;
+ GtkWidget *widget;
+ GError *error;
+ GtkCellRenderer *cr;
+
+ builder = gtk_builder_new ();
+
+ error = NULL;
+ gtk_builder_add_from_file (builder,
+ GNOMECC_UI_DIR
+ "/appearance.ui",
+ &error);
+ if (error != NULL) {
+ g_error (_("Could not load user interface file: %s"),
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+ page->priv->model = GTK_TREE_MODEL (gtk_list_store_new (2,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_OBJECT));
+
+ page->priv->icon_view = WID ("wp_view");
+ gtk_icon_view_set_model (GTK_ICON_VIEW (page->priv->icon_view),
+ GTK_TREE_MODEL (page->priv->model));
+
+ g_signal_connect_after (page->priv->icon_view,
+ "realize",
+ (GCallback) on_icon_view_realize,
+ page);
+
+ gtk_cell_layout_clear (GTK_CELL_LAYOUT (page->priv->icon_view));
+
+ cr = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (cr, "xpad", 5, "ypad", 5, NULL);
+
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (page->priv->icon_view), cr, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (page->priv->icon_view),
+ cr,
+ "pixbuf", 0,
+ NULL);
+
+ cr = gtk_cell_renderer_pixbuf_new ();
+ create_button_images (page);
+ 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 (page->priv->icon_view), cr, FALSE);
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (page->priv->icon_view),
+ cr,
+ (GtkCellLayoutDataFunc) buttons_cell_data_func,
+ page,
+ NULL);
+
+ gtk_widget_set_has_tooltip (page->priv->icon_view, TRUE);
+ g_signal_connect (page->priv->icon_view,
+ "selection-changed",
+ (GCallback) on_icon_view_selection_changed,
+ page);
+ g_signal_connect (page->priv->icon_view,
+ "button-press-event",
+ G_CALLBACK (on_icon_view_button_press),
+ page);
+ g_signal_connect (page->priv->icon_view,
+ "query-tooltip",
+ (GCallback) on_query_tooltip,
+ page);
+
+ page->priv->frame = -1;
+
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (page->priv->model),
+ 1,
+ (GtkTreeIterCompareFunc) sort_model_iter,
+ page,
+ NULL);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (page->priv->model),
+ 1,
+ GTK_SORT_ASCENDING);
+
+ gtk_drag_dest_set (page->priv->icon_view,
+ GTK_DEST_DEFAULT_ALL,
+ drop_types,
+ G_N_ELEMENTS (drop_types),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect (page->priv->icon_view,
+ "drag_data_received",
+ (GCallback) wp_drag_received,
+ page);
+
+ gtk_drag_source_set (page->priv->icon_view,
+ GDK_BUTTON1_MASK,
+ drag_types,
+ G_N_ELEMENTS (drag_types),
+ GDK_ACTION_COPY);
+ g_signal_connect (page->priv->icon_view,
+ "drag-data-get",
+ (GCallback) wp_drag_get_data,
+ page);
+
+ page->priv->style_menu = WID ("wp_style_menu");
+ g_signal_connect (page->priv->style_menu,
+ "changed",
+ (GCallback) on_style_menu_changed,
+ page);
+
+ page->priv->color_menu = WID ("wp_color_menu");
+ g_signal_connect (page->priv->color_menu,
+ "changed",
+ (GCallback) on_color_menu_changed,
+ page);
+
+ page->priv->secondary_color_picker = WID ("wp_scpicker");
+ g_signal_connect (page->priv->secondary_color_picker,
+ "color-set",
+ (GCallback) on_color_changed,
+ page);
+
+ page->priv->primary_color_picker = WID ("wp_pcpicker");
+ g_signal_connect (page->priv->primary_color_picker,
+ "color-set",
+ (GCallback) on_color_changed,
+ page);
+
+ widget = WID ("wp_add_button");
+ g_signal_connect (widget,
+ "clicked",
+ (GCallback) on_add_button_clicked,
+ page);
+
+ page->priv->remove_button = WID ("wp_rem_button");
+ g_signal_connect (page->priv->remove_button,
+ "clicked",
+ (GCallback) on_remove_button_clicked,
+ page);
+
+ /* FIXME: only register after initial load? */
+ page->priv->screen_monitors_handler = g_signal_connect (gtk_widget_get_screen (page->priv->icon_view),
+ "monitors-changed",
+ G_CALLBACK (screen_monitors_changed),
+ page);
+ page->priv->screen_size_handler = g_signal_connect (gtk_widget_get_screen (page->priv->icon_view),
+ "size-changed",
+ G_CALLBACK (screen_monitors_changed),
+ page);
+
+
+ ui_update_sensitivities (page);
+
+ /* create the file selector later to save time on startup */
+
+ widget = WID ("background_vbox");
+ gtk_widget_reparent (widget, GTK_WIDGET (page));
+ gtk_widget_show (widget);
+}
+
+static GObject *
+cc_background_page_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ CcBackgroundPage *background_page;
+
+ background_page = CC_BACKGROUND_PAGE (G_OBJECT_CLASS (cc_background_page_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ g_object_set (background_page,
+ "display-name", _("Background"),
+ "id", "background",
+ NULL);
+
+ setup_page (background_page);
+
+ return G_OBJECT (background_page);
+}
+
+static void
+cc_background_page_class_init (CcBackgroundPageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_background_page_get_property;
+ object_class->set_property = cc_background_page_set_property;
+ object_class->constructor = cc_background_page_constructor;
+ object_class->finalize = cc_background_page_finalize;
+
+ g_type_class_add_private (klass, sizeof (CcBackgroundPagePrivate));
+}
+
+static void
+change_current_uri (CcBackgroundPage *page,
+ const char *uri)
+{
+ CcBackgroundItem *item;
+ GtkTreeIter iter;
+
+ item = NULL;
+ g_debug ("Looking for %s", uri);
+ if (find_uri_in_model (page->priv->model, uri, &iter)) {
+ gtk_tree_model_get (page->priv->model, &iter, COL_ITEM, &item, -1);
+ } else {
+ item = add_item_for_filename (page, uri);
+ if (item != NULL)
+ g_object_ref (item);
+ }
+ if (item != NULL) {
+ select_item (page, item, TRUE);
+ g_object_unref (item);
+ }
+}
+
+static void
+on_gconf_file_changed (GConfClient *client,
+ guint id,
+ GConfEntry *entry,
+ CcBackgroundPage *page)
+{
+ const char *uri;
+ char *path;
+
+ uri = gconf_value_get_string (entry->value);
+
+ path = NULL;
+ if (uri == NULL || *uri == '\0') {
+ path = g_strdup ("(none)");
+ } else {
+ if (g_utf8_validate (uri, -1, NULL)
+ && g_file_test (uri, G_FILE_TEST_EXISTS))
+ path = g_strdup (uri);
+ else
+ path = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
+ }
+
+ change_current_uri (page, path);
+
+ g_free (path);
+}
+
+static void
+on_gconf_options_changed (GConfClient *client,
+ guint id,
+ GConfEntry *entry,
+ CcBackgroundPage *page)
+{
+ CcBackgroundItem *item;
+ const char *placement;
+
+ placement = gconf_value_get_string (entry->value);
+
+ /* "none" means we don't use a background image */
+ if (placement == NULL
+ || strcmp (placement, "none") == 0) {
+ change_current_uri (page, "(none)");
+ return;
+ }
+
+ ui_update_option_menu (page, string_to_option (placement));
+
+ item = get_selected_item (page, NULL);
+ if (item != NULL) {
+ g_object_set (item, "placement", placement, NULL);
+ cc_background_item_load (item);
+ g_object_unref (item);
+ }
+}
+
+static void
+on_gconf_shading_changed (GConfClient *client,
+ guint id,
+ GConfEntry *entry,
+ CcBackgroundPage *page)
+{
+ CcBackgroundItem *item;
+ const char *shading;
+
+ shading = gconf_value_get_string (entry->value);
+ ui_update_shade_menu (page, string_to_shade (shading));
+
+ item = get_selected_item (page, NULL);
+ if (item != NULL) {
+ g_object_set (item, "shading", shading, NULL);
+ cc_background_item_load (item);
+ g_object_unref (item);
+ }
+}
+
+static void
+on_gconf_color1_changed (GConfClient *client,
+ guint id,
+ GConfEntry *entry,
+ CcBackgroundPage *page)
+{
+ CcBackgroundItem *item;
+ GdkColor color;
+ const char *colorhex;
+
+ colorhex = gconf_value_get_string (entry->value);
+
+ gdk_color_parse (colorhex, &color);
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (page->priv->primary_color_picker),
+ &color);
+
+ item = get_selected_item (page, NULL);
+ if (item != NULL) {
+ g_object_set (item, "primary-color", colorhex, NULL);
+ cc_background_item_load (item);
+ g_object_unref (item);
+ }
+}
+
+static void
+on_gconf_color2_changed (GConfClient *client,
+ guint id,
+ GConfEntry *entry,
+ CcBackgroundPage *page)
+{
+ CcBackgroundItem *item;
+ GdkColor color;
+ const char *colorhex;
+
+ ui_update_sensitivities (page);
+
+ colorhex = gconf_value_get_string (entry->value);
+
+ gdk_color_parse (colorhex, &color);
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (page->priv->secondary_color_picker),
+ &color);
+
+ item = get_selected_item (page, NULL);
+ if (item != NULL) {
+ g_object_set (item, "secondary-color", colorhex, NULL);
+ cc_background_item_load (item);
+ g_object_unref (item);
+ }
+}
+
+static void
+cc_background_page_init (CcBackgroundPage *page)
+{
+ GConfClient *client;
+
+ page->priv = CC_BACKGROUND_PAGE_GET_PRIVATE (page);
+
+ page->priv->thumb_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);
+
+ page->priv->monitor = cc_backgrounds_monitor_new ();
+
+ client = gconf_client_get_default ();
+ gconf_client_add_dir (client,
+ WP_PATH_KEY,
+ GCONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+
+ gconf_client_notify_add (client,
+ WP_FILE_KEY,
+ (GConfClientNotifyFunc) on_gconf_file_changed,
+ page, NULL, NULL);
+ gconf_client_notify_add (client,
+ WP_OPTIONS_KEY,
+ (GConfClientNotifyFunc) on_gconf_options_changed,
+ page, NULL, NULL);
+ gconf_client_notify_add (client,
+ WP_SHADING_KEY,
+ (GConfClientNotifyFunc) on_gconf_shading_changed,
+ page, NULL, NULL);
+ gconf_client_notify_add (client,
+ WP_PCOLOR_KEY,
+ (GConfClientNotifyFunc) on_gconf_color1_changed,
+ page, NULL, NULL);
+ gconf_client_notify_add (client,
+ WP_SCOLOR_KEY,
+ (GConfClientNotifyFunc) on_gconf_color2_changed,
+ page, NULL, NULL);
+
+ g_object_unref (client);
+
+}
+
+static void
+cc_background_page_finalize (GObject *object)
+{
+ CcBackgroundPage *page;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CC_IS_BACKGROUND_PAGE (object));
+
+ page = CC_BACKGROUND_PAGE (object);
+
+ g_return_if_fail (page->priv != NULL);
+
+ if (page->priv->screen_monitors_handler > 0) {
+ g_signal_handler_disconnect (gtk_widget_get_screen (page->priv->icon_view),
+ page->priv->screen_monitors_handler);
+ page->priv->screen_monitors_handler = 0;
+ }
+ if (page->priv->screen_size_handler > 0) {
+ g_signal_handler_disconnect (gtk_widget_get_screen (page->priv->icon_view),
+ page->priv->screen_size_handler);
+ page->priv->screen_size_handler = 0;
+ }
+
+ if (page->priv->file_chooser != NULL) {
+ g_object_ref_sink (page->priv->file_chooser);
+ g_object_unref (page->priv->file_chooser);
+ }
+
+ if (page->priv->monitor != NULL) {
+ cc_backgrounds_monitor_save (page->priv->monitor);
+ g_object_unref (page->priv->monitor);
+ }
+
+ G_OBJECT_CLASS (cc_background_page_parent_class)->finalize (object);
+}
+
+CcPage *
+cc_background_page_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (CC_TYPE_BACKGROUND_PAGE, NULL);
+
+ return CC_PAGE (object);
+}
diff --git a/capplets/appearance/cc-background-page.h b/capplets/appearance/cc-background-page.h
new file mode 100644
index 0000000..58b729b
--- /dev/null
+++ b/capplets/appearance/cc-background-page.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __CC_BACKGROUND_PAGE_H
+#define __CC_BACKGROUND_PAGE_H
+
+#include <gtk/gtk.h>
+#include "cc-page.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_BACKGROUND_PAGE (cc_background_page_get_type ())
+#define CC_BACKGROUND_PAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_BACKGROUND_PAGE, CcBackgroundPage))
+#define CC_BACKGROUND_PAGE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_BACKGROUND_PAGE, CcBackgroundPageClass))
+#define CC_IS_BACKGROUND_PAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_BACKGROUND_PAGE))
+#define CC_IS_BACKGROUND_PAGE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_BACKGROUND_PAGE))
+#define CC_BACKGROUND_PAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_BACKGROUND_PAGE, CcBackgroundPageClass))
+
+typedef struct CcBackgroundPagePrivate CcBackgroundPagePrivate;
+
+typedef struct
+{
+ CcPage parent;
+ CcBackgroundPagePrivate *priv;
+} CcBackgroundPage;
+
+typedef struct
+{
+ CcPageClass parent_class;
+} CcBackgroundPageClass;
+
+GType cc_background_page_get_type (void);
+
+CcPage * cc_background_page_new (void);
+
+G_END_DECLS
+
+#endif /* __CC_BACKGROUND_PAGE_H */
diff --git a/capplets/appearance/cc-backgrounds-monitor.c b/capplets/appearance/cc-backgrounds-monitor.c
new file mode 100644
index 0000000..a18bfa5
--- /dev/null
+++ b/capplets/appearance/cc-backgrounds-monitor.c
@@ -0,0 +1,714 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+#include "cc-backgrounds-monitor.h"
+#include "cc-background-item.h"
+
+#define CC_BACKGROUNDS_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_BACKGROUNDS_MONITOR, CcBackgroundsMonitorPrivate))
+
+struct CcBackgroundsMonitorPrivate
+{
+ GHashTable *item_hash;
+};
+
+enum {
+ PROP_0,
+};
+
+enum {
+ ITEM_ADDED,
+ ITEM_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void cc_backgrounds_monitor_class_init (CcBackgroundsMonitorClass *klass);
+static void cc_backgrounds_monitor_init (CcBackgroundsMonitor *backgrounds_monitor);
+static void cc_backgrounds_monitor_finalize (GObject *object);
+
+G_DEFINE_TYPE (CcBackgroundsMonitor, cc_backgrounds_monitor, G_TYPE_OBJECT)
+
+#include <gio/gio.h>
+#include <string.h>
+#include <libxml/parser.h>
+
+static gboolean
+xml_get_bool (const xmlNode *parent,
+ const char *prop_name)
+{
+ xmlChar * prop;
+ gboolean ret_val = FALSE;
+
+ g_assert (parent != NULL);
+ g_assert (prop_name != NULL);
+
+ prop = xmlGetProp ((xmlNode *) parent, (xmlChar*)prop_name);
+ if (prop == NULL) {
+ goto done;
+ }
+
+ if (!g_ascii_strcasecmp ((char *)prop, "true")
+ || !g_ascii_strcasecmp ((char *)prop, "1")) {
+ ret_val = TRUE;
+ } else {
+ ret_val = FALSE;
+ }
+ g_free (prop);
+ done:
+ return ret_val;
+}
+
+static void
+xml_set_bool (const xmlNode *parent,
+ const xmlChar *prop_name,
+ gboolean value)
+{
+ g_assert (parent != NULL);
+ g_assert (prop_name != NULL);
+
+ if (value) {
+ xmlSetProp ((xmlNode *) parent, prop_name, (xmlChar *)"true");
+ } else {
+ xmlSetProp ((xmlNode *) parent, prop_name, (xmlChar *)"false");
+ }
+}
+
+static void
+load_legacy (CcBackgroundsMonitor *monitor)
+{
+ FILE *fp;
+ char *foo;
+ char *filename;
+
+ filename = g_build_filename (g_get_home_dir (),
+ ".gnome2",
+ "wallpapers.list",
+ NULL);
+
+ if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ if ((fp = fopen (filename, "r")) != NULL) {
+ foo = (char *) g_malloc (sizeof (char) * 4096);
+ while (fgets (foo, 4096, fp)) {
+ CcBackgroundItem *item;
+
+ if (foo[strlen (foo) - 1] == '\n') {
+ foo[strlen (foo) - 1] = '\0';
+ }
+
+ item = g_hash_table_lookup (monitor->priv->item_hash, foo);
+ if (item != NULL) {
+ continue;
+ }
+
+ if (!g_file_test (foo, G_FILE_TEST_EXISTS)) {
+ continue;
+ }
+
+ item = cc_background_item_new (foo);
+ if (cc_background_item_load (item)) {
+ cc_backgrounds_monitor_add_item (monitor, item);
+ } else {
+ g_object_unref (item);
+ }
+ }
+ fclose (fp);
+ g_free (foo);
+ }
+ }
+
+ g_free (filename);
+}
+
+static void
+load_xml (CcBackgroundsMonitor *monitor,
+ const char *xml_filename)
+{
+ xmlDoc *wplist;
+ xmlNode *root;
+ xmlNode *list;
+ xmlNode *wpa;
+ xmlChar *nodelang;
+ const char * const *syslangs;
+ int i;
+
+ //g_debug ("loading from: %s", xml_filename);
+
+ wplist = xmlParseFile (xml_filename);
+
+ if (wplist == NULL)
+ return;
+
+ syslangs = g_get_language_names ();
+
+ root = xmlDocGetRootElement (wplist);
+
+ for (list = root->children; list != NULL; list = list->next) {
+ char *filename;
+ char *name;
+ char *primary_color;
+ char *secondary_color;
+ char *options;
+ char *shade_type;
+ gboolean deleted;
+
+ if (strcmp ((char *)list->name, "wallpaper") != 0) {
+ continue;
+ }
+
+ filename = NULL;
+ name = NULL;
+ options = NULL;
+ shade_type = NULL;
+ primary_color = NULL;
+ secondary_color = NULL;
+
+ deleted = xml_get_bool (list, "deleted");
+
+ for (wpa = list->children; wpa != NULL; wpa = wpa->next) {
+ if (wpa->type == XML_COMMENT_NODE) {
+ continue;
+ }
+
+ if (strcmp ((char *)wpa->name, "filename") == 0) {
+ if (wpa->last != NULL
+ && wpa->last->content != NULL) {
+ char *content;
+
+ content = g_strstrip ((char *)wpa->last->content);
+
+ if (strcmp (content, "(none)") == 0)
+ filename = g_strdup (content);
+ else if (g_utf8_validate (content, -1, NULL) &&
+ g_file_test (content, G_FILE_TEST_EXISTS))
+ filename = g_strdup (content);
+ else
+ filename = g_filename_from_utf8 (content, -1, NULL, NULL, NULL);
+ } else {
+ break;
+ }
+ } else if (strcmp ((char *)wpa->name, "name") == 0) {
+ if (wpa->last != NULL && wpa->last->content != NULL) {
+ nodelang = xmlNodeGetLang (wpa->last);
+
+ if (name == NULL && nodelang == NULL) {
+ name = g_strdup (g_strstrip ((char *)wpa->last->content));
+ } else {
+ for (i = 0; syslangs[i] != NULL; i++) {
+ if (strcmp (syslangs[i], (char *)nodelang) == 0) {
+ g_free (name);
+ name = g_strdup (g_strstrip ((char *)wpa->last->content));
+ break;
+ }
+ }
+ }
+
+ xmlFree (nodelang);
+ } else {
+ break;
+ }
+ } else if (strcmp ((char *)wpa->name, "options") == 0) {
+ if (wpa->last != NULL) {
+ options = g_strdup (g_strstrip ((char *)wpa->last->content));
+ }
+ } else if (strcmp ((char *)wpa->name, "shade_type") == 0) {
+ if (wpa->last != NULL) {
+ shade_type = g_strdup (g_strstrip ((char *)wpa->last->content));
+ }
+ } else if (strcmp ((char *)wpa->name, "pcolor") == 0) {
+ if (wpa->last != NULL) {
+ primary_color = g_strdup (g_strstrip ((char *)wpa->last->content));
+ }
+ } else if (strcmp ((char *)wpa->name, "scolor") == 0) {
+ if (wpa->last != NULL) {
+ secondary_color = g_strdup (g_strstrip ((char *)wpa->last->content));
+ }
+ } else if (strcmp ((char *)wpa->name, "text") == 0) {
+ /* 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 (filename != NULL
+ && g_hash_table_lookup (monitor->priv->item_hash, filename) == NULL) {
+ CcBackgroundItem *item;
+
+ item = cc_background_item_new (filename);
+ g_object_set (item,
+ "name", name,
+ "primary-color", primary_color,
+ "secondary-color", secondary_color,
+ "placement", options,
+ "shading", shade_type,
+ NULL);
+
+ if (cc_background_item_load (item)) {
+ if (cc_backgrounds_monitor_add_item (monitor, item)) {
+ g_debug ("Added item %s", name);
+ }
+ }
+ g_object_unref (item);
+ }
+
+ g_free (filename);
+ g_free (name);
+ g_free (primary_color);
+ g_free (secondary_color);
+ g_free (options);
+ g_free (shade_type);
+ }
+ xmlFreeDoc (wplist);
+}
+
+static void
+file_changed (GFileMonitor *file_monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ CcBackgroundsMonitor *monitor)
+{
+ char *filename;
+
+ switch (event_type) {
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ case G_FILE_MONITOR_EVENT_CREATED:
+ filename = g_file_get_path (file);
+ load_xml (monitor, filename);
+ g_free (filename);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+add_monitor (CcBackgroundsMonitor *monitor,
+ GFile *directory)
+{
+ GFileMonitor *file_monitor;
+ GError *error = NULL;
+
+ file_monitor = g_file_monitor_directory (directory,
+ G_FILE_MONITOR_NONE,
+ NULL,
+ &error);
+ if (error != NULL) {
+ char *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 (file_monitor,
+ "changed",
+ G_CALLBACK (file_changed),
+ monitor);
+}
+
+static void
+load_from_dir (CcBackgroundsMonitor *monitor,
+ const char *path)
+{
+ GFile *directory;
+ GFileEnumerator *enumerator;
+ GError *error;
+ GFileInfo *info;
+
+ if (!g_file_test (path, G_FILE_TEST_IS_DIR)) {
+ return;
+ }
+
+ g_debug ("Loading from directory %s", path);
+ directory = g_file_new_for_path (path);
+
+ error = NULL;
+ 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 char *filename;
+ char *fullpath;
+
+ filename = g_file_info_get_name (info);
+ fullpath = g_build_filename (path, filename, NULL);
+ g_object_unref (info);
+
+ load_xml (monitor, fullpath);
+ g_free (fullpath);
+ }
+ g_file_enumerator_close (enumerator, NULL, NULL);
+
+ add_monitor (monitor, directory);
+
+ g_object_unref (directory);
+}
+
+static void
+ensure_none (CcBackgroundsMonitor *monitor)
+{
+ CcBackgroundItem *item;
+
+ item = g_hash_table_lookup (monitor->priv->item_hash, "(none)");
+ if (item == NULL) {
+ item = cc_background_item_new ("(none)");
+ if (cc_background_item_load (item)) {
+ cc_backgrounds_monitor_add_item (monitor, item);
+ } else {
+ g_object_unref (item);
+ }
+ } else {
+ g_object_set (item, "is-deleted", FALSE, NULL);
+ }
+}
+
+void
+cc_backgrounds_monitor_load (CcBackgroundsMonitor *monitor)
+{
+ const char * const *system_data_dirs;
+ char *filename;
+ int i;
+
+ g_return_if_fail (monitor != NULL);
+
+ filename = g_build_filename (g_get_home_dir (),
+ ".gnome2",
+ "backgrounds.xml",
+ NULL);
+
+ if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ load_xml (monitor, filename);
+ } else {
+ g_free (filename);
+ filename = g_build_filename (g_get_home_dir (),
+ ".gnome2",
+ "wp-list.xml",
+ NULL);
+ if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ load_xml (monitor, filename);
+ }
+ }
+ g_free (filename);
+
+ filename = g_build_filename (g_get_user_data_dir (),
+ "gnome-background-properties",
+ NULL);
+ load_from_dir (monitor, filename);
+ g_free (filename);
+
+ system_data_dirs = g_get_system_data_dirs ();
+ for (i = 0; system_data_dirs[i]; i++) {
+ filename = g_build_filename (system_data_dirs[i],
+ "gnome-background-properties",
+ NULL);
+ load_from_dir (monitor, filename);
+ g_free (filename);
+ }
+
+ load_from_dir (monitor, WALLPAPER_DATADIR);
+
+ load_legacy (monitor);
+
+ /* We always want to have a (none) entry */
+ ensure_none (monitor);
+}
+
+static void
+list_flatten (const char *key,
+ CcBackgroundItem *item,
+ GList **list)
+{
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (item != NULL);
+
+ *list = g_list_prepend (*list, item);
+}
+
+gboolean
+cc_backgrounds_monitor_add_item (CcBackgroundsMonitor *monitor,
+ CcBackgroundItem *item)
+{
+ char *uri;
+ gboolean deleted;
+ gboolean ret;
+
+ ret = FALSE;
+
+ g_return_val_if_fail (monitor != NULL, FALSE);
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ uri = NULL;
+ g_object_get (item,
+ "filename", &uri,
+ "is-deleted", &deleted,
+ NULL);
+ if (g_hash_table_lookup (monitor->priv->item_hash, uri) == NULL) {
+ g_debug ("Inserting %s", uri);
+ g_hash_table_insert (monitor->priv->item_hash,
+ g_strdup (uri),
+ g_object_ref (item));
+ ret = TRUE;
+ } else if (!deleted) {
+ g_debug ("Undeleting %s", uri);
+ g_object_set (item, "is-deleted", FALSE, NULL);
+ ret = TRUE;
+ }
+
+ if (ret)
+ g_signal_emit (monitor, signals [ITEM_ADDED], 0, item);
+
+ g_free (uri);
+
+ return ret;
+}
+
+gboolean
+cc_backgrounds_monitor_remove_item (CcBackgroundsMonitor *monitor,
+ CcBackgroundItem *item)
+{
+ char *uri;
+ gboolean deleted;
+ gboolean ret;
+
+ ret = FALSE;
+
+ g_return_val_if_fail (monitor != NULL, FALSE);
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ g_object_get (item,
+ "filename", &uri,
+ "is-deleted", &deleted,
+ NULL);
+ if (g_hash_table_lookup (monitor->priv->item_hash, uri) != NULL
+ && !deleted) {
+ g_object_set (item, "is-deleted", TRUE, NULL);
+ g_signal_emit (monitor, signals [ITEM_REMOVED], 0, item);
+ ret = TRUE;
+ }
+ g_free (uri);
+ return ret;
+}
+
+GList *
+cc_backgrounds_monitor_get_items (CcBackgroundsMonitor *monitor)
+{
+ GList *list;
+
+ g_return_val_if_fail (monitor != NULL, NULL);
+
+ list = NULL;
+ g_hash_table_foreach (monitor->priv->item_hash,
+ (GHFunc) list_flatten,
+ &list);
+ list = g_list_reverse (list);
+ return list;
+}
+
+void
+cc_backgrounds_monitor_save (CcBackgroundsMonitor *monitor)
+{
+ xmlDoc *wplist;
+ xmlNode *root;
+ xmlNode *wallpaper;
+ xmlNode *node;
+ GList *list = NULL;
+ char *wpfile;
+
+ g_return_if_fail (monitor != NULL);
+
+ g_hash_table_foreach (monitor->priv->item_hash,
+ (GHFunc) list_flatten,
+ &list);
+ list = g_list_reverse (list);
+
+ wpfile = g_build_filename (g_get_home_dir (),
+ "/.gnome2",
+ "backgrounds.xml",
+ NULL);
+
+ xmlKeepBlanksDefault (0);
+
+ wplist = xmlNewDoc ((xmlChar *)"1.0");
+ xmlCreateIntSubset (wplist, (xmlChar *)"wallpapers", NULL, (xmlChar *)"gnome-wp-list.dtd");
+ root = xmlNewNode (NULL, (xmlChar *)"wallpapers");
+ xmlDocSetRootElement (wplist, root);
+
+ while (list != NULL) {
+ CcBackgroundItem *item;
+ char *scale;
+ char *shade;
+ char *filename;
+ char *utf8_filename;
+ char *name;
+ char *pcolor;
+ char *scolor;
+ gboolean deleted;
+
+ item = list->data;
+ g_object_get (item,
+ "filename", &filename,
+ "name", &name,
+ "primary-color", &pcolor,
+ "secondary-color", &scolor,
+ "shading", &shade,
+ "placement", &scale,
+ "is-deleted", &deleted,
+ NULL);
+
+ if (strcmp (filename, "(none)") == 0
+ || (g_utf8_validate (filename, -1, NULL)
+ && g_file_test (filename, G_FILE_TEST_EXISTS))) {
+ utf8_filename = g_strdup (filename);
+ } else {
+ utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+ }
+
+ wallpaper = xmlNewChild (root, NULL, (xmlChar *)"wallpaper", NULL);
+ xml_set_bool (wallpaper, (xmlChar *)"deleted", deleted);
+ node = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"name", (xmlChar *)name);
+ node = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"filename", (xmlChar *)filename);
+ node = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"options", (xmlChar *)scale);
+ node = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"shade_type", (xmlChar *)shade);
+ node = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"pcolor", (xmlChar *)pcolor);
+ node = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"scolor", (xmlChar *)scolor);
+
+ g_free (pcolor);
+ g_free (scolor);
+ g_free (filename);
+ g_free (utf8_filename);
+ g_free (name);
+ g_free (shade);
+ g_free (scale);
+
+ list = g_list_delete_link (list, list);
+ }
+
+ xmlSaveFormatFile (wpfile, wplist, 1);
+ xmlFreeDoc (wplist);
+ g_free (wpfile);
+}
+
+static GObject *
+cc_backgrounds_monitor_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ CcBackgroundsMonitor *backgrounds_monitor;
+
+ backgrounds_monitor = CC_BACKGROUNDS_MONITOR (G_OBJECT_CLASS (cc_backgrounds_monitor_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ return G_OBJECT (backgrounds_monitor);
+}
+
+static void
+cc_backgrounds_monitor_class_init (CcBackgroundsMonitorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructor = cc_backgrounds_monitor_constructor;
+ object_class->finalize = cc_backgrounds_monitor_finalize;
+
+ signals [ITEM_ADDED]
+ = g_signal_new ("item-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, CC_TYPE_BACKGROUND_ITEM);
+ signals [ITEM_REMOVED]
+ = g_signal_new ("item-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, CC_TYPE_BACKGROUND_ITEM);
+
+ g_type_class_add_private (klass, sizeof (CcBackgroundsMonitorPrivate));
+}
+
+static void
+cc_backgrounds_monitor_init (CcBackgroundsMonitor *monitor)
+{
+ monitor->priv = CC_BACKGROUNDS_MONITOR_GET_PRIVATE (monitor);
+
+ monitor->priv->item_hash = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+}
+
+static void
+cc_backgrounds_monitor_finalize (GObject *object)
+{
+ CcBackgroundsMonitor *monitor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CC_IS_BACKGROUNDS_MONITOR (object));
+
+ monitor = CC_BACKGROUNDS_MONITOR (object);
+
+ g_return_if_fail (monitor->priv != NULL);
+
+ g_hash_table_destroy (monitor->priv->item_hash);
+
+ G_OBJECT_CLASS (cc_backgrounds_monitor_parent_class)->finalize (object);
+}
+
+CcBackgroundsMonitor *
+cc_backgrounds_monitor_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (CC_TYPE_BACKGROUNDS_MONITOR, NULL);
+
+ return CC_BACKGROUNDS_MONITOR (object);
+}
diff --git a/capplets/appearance/cc-backgrounds-monitor.h b/capplets/appearance/cc-backgrounds-monitor.h
new file mode 100644
index 0000000..8e46e2c
--- /dev/null
+++ b/capplets/appearance/cc-backgrounds-monitor.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __CC_BACKGROUNDS_MONITOR_H
+#define __CC_BACKGROUNDS_MONITOR_H
+
+#include <glib-object.h>
+#include "cc-background-item.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_BACKGROUNDS_MONITOR (cc_backgrounds_monitor_get_type ())
+#define CC_BACKGROUNDS_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_BACKGROUNDS_MONITOR, CcBackgroundsMonitor))
+#define CC_BACKGROUNDS_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_BACKGROUNDS_MONITOR, CcBackgroundsMonitorClass))
+#define CC_IS_BACKGROUNDS_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_BACKGROUNDS_MONITOR))
+#define CC_IS_BACKGROUNDS_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_BACKGROUNDS_MONITOR))
+#define CC_BACKGROUNDS_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_BACKGROUNDS_MONITOR, CcBackgroundsMonitorClass))
+
+typedef struct CcBackgroundsMonitorPrivate CcBackgroundsMonitorPrivate;
+
+typedef struct
+{
+ GObject parent;
+ CcBackgroundsMonitorPrivate *priv;
+} CcBackgroundsMonitor;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* item_added) (CcBackgroundsMonitor *monitor,
+ CcBackgroundItem *item);
+ void (* item_removed) (CcBackgroundsMonitor *monitor,
+ CcBackgroundItem *item);
+} CcBackgroundsMonitorClass;
+
+GType cc_backgrounds_monitor_get_type (void);
+
+CcBackgroundsMonitor * cc_backgrounds_monitor_new (void);
+
+void cc_backgrounds_monitor_load (CcBackgroundsMonitor *monitor);
+void cc_backgrounds_monitor_save (CcBackgroundsMonitor *monitor);
+
+GList * cc_backgrounds_monitor_get_items (CcBackgroundsMonitor *monitor);
+gboolean cc_backgrounds_monitor_add_item (CcBackgroundsMonitor *monitor,
+ CcBackgroundItem *item);
+gboolean cc_backgrounds_monitor_remove_item (CcBackgroundsMonitor *monitor,
+ CcBackgroundItem *item);
+
+G_END_DECLS
+
+#endif /* __CC_BACKGROUNDS_MONITOR_H */
diff --git a/capplets/appearance/gnome-wp-item.c b/capplets/appearance/gnome-wp-item.c
index b4af8fc..74f5b79 100644
--- a/capplets/appearance/gnome-wp-item.c
+++ b/capplets/appearance/gnome-wp-item.c
@@ -123,7 +123,6 @@ void gnome_wp_item_update (GnomeWPItem *item) {
}
GnomeWPItem * gnome_wp_item_new (const gchar * filename,
- GHashTable * wallpapers,
GnomeDesktopThumbnailFactory * thumbnails) {
GnomeWPItem *item = g_new0 (GnomeWPItem, 1);
@@ -143,8 +142,6 @@ GnomeWPItem * gnome_wp_item_new (const gchar * filename,
gnome_wp_item_update (item);
gnome_wp_item_ensure_gnome_bg (item);
gnome_wp_item_update_description (item);
-
- g_hash_table_insert (wallpapers, item->filename, item);
} else {
gnome_wp_item_free (item);
item = NULL;
diff --git a/capplets/appearance/gnome-wp-item.h b/capplets/appearance/gnome-wp-item.h
index 03dc417..6654e3d 100644
--- a/capplets/appearance/gnome-wp-item.h
+++ b/capplets/appearance/gnome-wp-item.h
@@ -66,7 +66,6 @@ struct _GnomeWPItem {
};
GnomeWPItem * gnome_wp_item_new (const gchar *filename,
- GHashTable *wallpapers,
GnomeDesktopThumbnailFactory *thumbnails);
void gnome_wp_item_free (GnomeWPItem *item);
diff --git a/capplets/appearance/gnome-wp-xml.c b/capplets/appearance/gnome-wp-xml.c
index 5ecea04..8598bff 100644
--- a/capplets/appearance/gnome-wp-xml.c
+++ b/capplets/appearance/gnome-wp-xml.c
@@ -83,7 +83,7 @@ static void gnome_wp_load_legacy (AppearanceData *data) {
continue;
}
- item = gnome_wp_item_new (foo, data->wp_hash, data->thumb_factory);
+ item = gnome_wp_item_new (foo, data->thumb_factory);
if (item != NULL && item->fileinfo == NULL) {
gnome_wp_item_free (item);
}
diff --git a/capplets/keyboard/Makefile.am b/capplets/keyboard/Makefile.am
index 0535402..e608163 100644
--- a/capplets/keyboard/Makefile.am
+++ b/capplets/keyboard/Makefile.am
@@ -1,10 +1,20 @@
# This is used in GNOMECC_CAPPLETS_CFLAGS
cappletname = keyboard
+module_flags = -export_dynamic -avoid-version -module -no-undefined -export-symbols-regex '^g_io_module_(load|unload)'
+
+INCLUDES = \
+ $(GNOMECC_CAPPLETS_CFLAGS) \
+ $(LIBGNOMEKBDUI_CFLAGS) \
+ -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \
+ -DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \
+ -DGNOMECC_UI_DIR="\"$(uidir)\""
+
+noinst_LTLIBRARIES = libkeyboard-common.la
+
bin_PROGRAMS = gnome-keyboard-properties
-gnome_keyboard_properties_SOURCES = \
- gnome-keyboard-properties.c \
+libkeyboard_common_la_SOURCES = \
gnome-keyboard-properties-a11y.c \
gnome-keyboard-properties-a11y.h \
gnome-keyboard-properties-xkb.c \
@@ -15,7 +25,36 @@ gnome_keyboard_properties_SOURCES = \
gnome-keyboard-properties-xkbpv.c \
gnome-keyboard-properties-xkb.h
-gnome_keyboard_properties_LDADD = $(GNOMECC_CAPPLETS_LIBS) $(LIBGNOMEKBDUI_LIBS)
+libkeyboard_common_la_LDFLAGS = \
+ -no-undefined -export_dynamic -avoid-version
+
+ccmodulesdir = $(libdir)/control-center-1/extensions
+ccmodules_LTLIBRARIES = libkeyboard.la
+
+libkeyboard_la_SOURCES = \
+ keyboard-module.c \
+ cc-keyboard-panel.h \
+ cc-keyboard-panel.c
+
+libkeyboard_la_LDFLAGS = \
+ $(module_flags)
+
+libkeyboard_la_LIBADD = \
+ libkeyboard-common.la \
+ $(GNOMECC_CAPPLETS_LIBS) \
+ $(LIBGNOMEKBDUI_LIBS)
+
+libkeyboard_la_CFLAGS = \
+ -I$(top_builddir)/shell \
+ -I$(top_srcdir)/shell
+
+gnome_keyboard_properties_SOURCES = \
+ gnome-keyboard-properties.c
+
+gnome_keyboard_properties_LDADD = \
+ libkeyboard-common.la \
+ $(GNOMECC_CAPPLETS_LIBS) \
+ $(LIBGNOMEKBDUI_LIBS)
@INTLTOOL_DESKTOP_RULE@
@@ -30,12 +69,6 @@ desktopdir = $(datadir)/applications
Desktop_in_files = keyboard.desktop.in
desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop)
-INCLUDES = \
- $(GNOMECC_CAPPLETS_CFLAGS) \
- $(LIBGNOMEKBDUI_CFLAGS) \
- -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \
- -DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \
- -DGNOMECC_UI_DIR="\"$(uidir)\""
CLEANFILES = $(GNOMECC_CAPPLETS_CLEANFILES) $(Desktop_in_files) $(desktop_DATA)
EXTRA_DIST = $(ui_DATA)
diff --git a/capplets/keyboard/cc-keyboard-panel.c b/capplets/keyboard/cc-keyboard-panel.c
new file mode 100644
index 0000000..bd45ee6
--- /dev/null
+++ b/capplets/keyboard/cc-keyboard-panel.c
@@ -0,0 +1,293 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+#include "cc-keyboard-panel.h"
+
+#include "gconf-property-editor.h"
+#include "capplet-stock-icons.h"
+
+#include "gnome-keyboard-properties-a11y.h"
+#include "gnome-keyboard-properties-xkb.h"
+
+#define CC_KEYBOARD_PANEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_KEYBOARD_PANEL, CcKeyboardPanelPrivate))
+
+#define WID(s) GTK_WIDGET (gtk_builder_get_object (builder, s))
+
+struct CcKeyboardPanelPrivate
+{
+ gpointer dummy;
+};
+
+enum {
+ PROP_0,
+};
+
+static void cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass);
+static void cc_keyboard_panel_init (CcKeyboardPanel *keyboard_panel);
+static void cc_keyboard_panel_finalize (GObject *object);
+
+G_DEFINE_DYNAMIC_TYPE (CcKeyboardPanel, cc_keyboard_panel, CC_TYPE_PANEL)
+
+static void
+cc_keyboard_panel_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_keyboard_panel_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static GConfValue *
+blink_from_widget (GConfPropertyEditor *peditor,
+ const GConfValue *value)
+{
+ GConfValue *new_value;
+
+ new_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (new_value,
+ 2600 - gconf_value_get_int (value));
+
+ return new_value;
+}
+
+static GConfValue *
+blink_to_widget (GConfPropertyEditor * peditor, const GConfValue * value)
+{
+ GConfValue *new_value;
+ gint current_rate;
+
+ current_rate = gconf_value_get_int (value);
+ new_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (new_value,
+ CLAMP (2600 - current_rate, 100, 2500));
+
+ return new_value;
+}
+
+static void
+setup_panel (CcKeyboardPanel *panel)
+{
+ GtkBuilder *builder;
+ GtkSizeGroup *size_group;
+ GtkWidget *image;
+ GtkWidget *widget;
+ GObject *peditor;
+ char *monitor;
+ GConfChangeSet *changeset;
+
+ changeset = NULL;
+
+ builder = gtk_builder_new ();
+ gtk_builder_add_from_file (builder,
+ GNOMECC_UI_DIR
+ "/gnome-keyboard-properties-dialog.ui",
+ NULL);
+
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ gtk_size_group_add_widget (size_group, WID ("repeat_slow_label"));
+ gtk_size_group_add_widget (size_group, WID ("delay_short_label"));
+ gtk_size_group_add_widget (size_group, WID ("blink_slow_label"));
+ g_object_unref (G_OBJECT (size_group));
+
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ gtk_size_group_add_widget (size_group, WID ("repeat_fast_label"));
+ gtk_size_group_add_widget (size_group, WID ("delay_long_label"));
+ gtk_size_group_add_widget (size_group, WID ("blink_fast_label"));
+ g_object_unref (G_OBJECT (size_group));
+
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ gtk_size_group_add_widget (size_group, WID ("repeat_delay_scale"));
+ gtk_size_group_add_widget (size_group, WID ("repeat_speed_scale"));
+ gtk_size_group_add_widget (size_group, WID ("cursor_blink_time_scale"));
+ g_object_unref (G_OBJECT (size_group));
+
+ image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
+ gtk_button_set_image (GTK_BUTTON (WID ("xkb_layouts_add")), image);
+
+ image = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON);
+ gtk_button_set_image (GTK_BUTTON (WID ("xkb_reset_to_defaults")), image);
+
+ peditor = gconf_peditor_new_boolean (changeset, "/desktop/gnome/peripherals/keyboard/repeat",
+ WID ("repeat_toggle"), NULL);
+ gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor),
+ WID ("repeat_table"));
+
+ gconf_peditor_new_numeric_range (changeset, "/desktop/gnome/peripherals/keyboard/delay",
+ WID ("repeat_delay_scale"), NULL);
+
+ gconf_peditor_new_numeric_range (changeset, "/desktop/gnome/peripherals/keyboard/rate",
+ WID ("repeat_speed_scale"), NULL);
+
+ peditor = gconf_peditor_new_boolean (changeset, "/desktop/gnome/interface/cursor_blink",
+ WID ("cursor_toggle"), NULL);
+ gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor),
+ WID ("cursor_hbox"));
+ gconf_peditor_new_numeric_range (changeset,
+ "/desktop/gnome/interface/cursor_blink_time",
+ WID ("cursor_blink_time_scale"),
+ "conv-to-widget-cb",
+ blink_to_widget,
+ "conv-from-widget-cb",
+ blink_from_widget, NULL);
+
+ /* Ergonomics */
+ monitor = g_find_program_in_path ("gnome-typing-monitor");
+ if (monitor != NULL) {
+ g_free (monitor);
+
+ peditor = gconf_peditor_new_boolean (changeset, "/desktop/gnome/typing_break/enabled",
+ WID ("break_enabled_toggle"), NULL);
+ gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor),
+ WID ("break_details_table"));
+ gconf_peditor_new_numeric_range (changeset,
+ "/desktop/gnome/typing_break/type_time",
+ WID ("break_enabled_spin"), NULL);
+ gconf_peditor_new_numeric_range (changeset,
+ "/desktop/gnome/typing_break/break_time",
+ WID ("break_interval_spin"),
+ NULL);
+ gconf_peditor_new_boolean (changeset,
+ "/desktop/gnome/typing_break/allow_postpone",
+ WID ("break_postponement_toggle"),
+ NULL);
+
+ } else {
+ /* don't show the typing break tab if the daemon is not available */
+ GtkNotebook *nb;
+ gint tb_page;
+
+ nb = GTK_NOTEBOOK (WID ("keyboard_notebook"));
+ tb_page = gtk_notebook_page_num (nb, WID ("break_enabled_toggle"));
+ gtk_notebook_remove_page (nb, tb_page);
+ }
+
+ setup_xkb_tabs (builder, changeset);
+ setup_a11y_tabs (builder, changeset);
+
+ widget = WID ("main-vbox");
+ gtk_widget_reparent (widget, GTK_WIDGET (panel));
+ gtk_widget_show (widget);
+}
+
+static GObject *
+cc_keyboard_panel_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ CcKeyboardPanel *keyboard_panel;
+
+ keyboard_panel = CC_KEYBOARD_PANEL (G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+ g_object_set (keyboard_panel,
+ "display-name", _("Keyboard"),
+ "id", "keyboard.desktop",
+ NULL);
+
+ setup_panel (keyboard_panel);
+
+ return G_OBJECT (keyboard_panel);
+}
+
+static void
+cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_keyboard_panel_get_property;
+ object_class->set_property = cc_keyboard_panel_set_property;
+ object_class->constructor = cc_keyboard_panel_constructor;
+ object_class->finalize = cc_keyboard_panel_finalize;
+
+ g_type_class_add_private (klass, sizeof (CcKeyboardPanelPrivate));
+}
+
+static void
+cc_keyboard_panel_class_finalize (CcKeyboardPanelClass *klass)
+{
+}
+
+static void
+cc_keyboard_panel_init (CcKeyboardPanel *panel)
+{
+ GConfClient *client;
+
+ panel->priv = CC_KEYBOARD_PANEL_GET_PRIVATE (panel);
+
+ client = gconf_client_get_default ();
+ gconf_client_add_dir (client,
+ "/desktop/gnome/peripherals/keyboard",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ gconf_client_add_dir (client, "/desktop/gnome/interface",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ g_object_unref (client);
+}
+
+static void
+cc_keyboard_panel_finalize (GObject *object)
+{
+ CcKeyboardPanel *keyboard_panel;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CC_IS_KEYBOARD_PANEL (object));
+
+ keyboard_panel = CC_KEYBOARD_PANEL (object);
+
+ g_return_if_fail (keyboard_panel->priv != NULL);
+
+ G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->finalize (object);
+}
+
+void
+cc_keyboard_panel_register (GIOModule *module)
+{
+ cc_keyboard_panel_register_type (G_TYPE_MODULE (module));
+ g_io_extension_point_implement (CC_PANEL_EXTENSION_POINT_NAME,
+ CC_TYPE_KEYBOARD_PANEL,
+ "keyboard",
+ 10);
+}
diff --git a/capplets/keyboard/cc-keyboard-panel.h b/capplets/keyboard/cc-keyboard-panel.h
new file mode 100644
index 0000000..78ef63e
--- /dev/null
+++ b/capplets/keyboard/cc-keyboard-panel.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __CC_SIMPLE_PANEL_H
+#define __CC_SIMPLE_PANEL_H
+
+#include <gtk/gtk.h>
+#include "cc-panel.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_KEYBOARD_PANEL (cc_keyboard_panel_get_type ())
+#define CC_KEYBOARD_PANEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_KEYBOARD_PANEL, CcKeyboardPanel))
+#define CC_KEYBOARD_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_KEYBOARD_PANEL, CcKeyboardPanelClass))
+#define CC_IS_KEYBOARD_PANEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_KEYBOARD_PANEL))
+#define CC_IS_KEYBOARD_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_KEYBOARD_PANEL))
+#define CC_KEYBOARD_PANEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_KEYBOARD_PANEL, CcKeyboardPanelClass))
+
+typedef struct CcKeyboardPanelPrivate CcKeyboardPanelPrivate;
+
+typedef struct
+{
+ CcPanel parent;
+ CcKeyboardPanelPrivate *priv;
+} CcKeyboardPanel;
+
+typedef struct
+{
+ CcPanelClass parent_class;
+} CcKeyboardPanelClass;
+
+GType cc_keyboard_panel_get_type (void);
+void cc_keyboard_panel_register (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* __CC_KEYBOARD_PANEL_H */
diff --git a/capplets/keyboard/gnome-keyboard-properties-dialog.ui b/capplets/keyboard/gnome-keyboard-properties-dialog.ui
index 36b9df5..5459787 100644
--- a/capplets/keyboard/gnome-keyboard-properties-dialog.ui
+++ b/capplets/keyboard/gnome-keyboard-properties-dialog.ui
@@ -77,12 +77,12 @@
<property name="type_hint">dialog</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
- <object class="GtkVBox" id="dialog-vbox1">
+ <object class="GtkVBox" id="dialog-vbox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
- <object class="GtkVBox" id="vbox1">
+ <object class="GtkVBox" id="main-vbox">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
diff --git a/capplets/keyboard/keyboard-module.c b/capplets/keyboard/keyboard-module.c
new file mode 100644
index 0000000..86798a0
--- /dev/null
+++ b/capplets/keyboard/keyboard-module.c
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gio/gio.h>
+
+#include "cc-keyboard-panel.h"
+
+void
+g_io_module_load (GIOModule *module)
+{
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+ cc_keyboard_panel_register (module);
+}
+
+void
+g_io_module_unload (GIOModule *module)
+{
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9caccb4..d22b9c8 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -20,6 +20,9 @@ capplets/appearance/appearance-font.c
capplets/appearance/appearance-main.c
capplets/appearance/appearance-style.c
capplets/appearance/appearance-themes.c
+capplets/appearance/cc-appearance-panel.c
+capplets/appearance/cc-background-item.c
+capplets/appearance/cc-background-page.c
[type: gettext/glade]capplets/appearance/data/appearance.ui
capplets/appearance/data/gnome-appearance-properties.desktop.in.in
capplets/appearance/data/gnome-theme-installer.desktop.in.in
@@ -55,6 +58,7 @@ capplets/keyboard/gnome-keyboard-properties.c
[type: gettext/glade]capplets/keyboard/gnome-keyboard-properties-layout-chooser.ui
[type: gettext/glade]capplets/keyboard/gnome-keyboard-properties-model-chooser.ui
[type: gettext/glade]capplets/keyboard/gnome-keyboard-properties-options-dialog.ui
+capplets/keyboard/cc-keyboard-panel.c
capplets/keyboard/gnome-keyboard-properties-xkb.c
capplets/keyboard/gnome-keyboard-properties-xkbltadd.c
capplets/keyboard/gnome-keyboard-properties-xkblt.c
@@ -78,6 +82,7 @@ shell/control-center.c
shell/control-center.schemas.in
shell/gnomecc.desktop.in.in
shell/gnomecc.directory.in
+shell/shell.ui
typing-break/drw-break-window.c
typing-break/drwright.c
typing-break/main.c
diff --git a/shell/Makefile.am b/shell/Makefile.am
index b7c4919..da62c63 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -2,7 +2,7 @@ INCLUDES = \
-I$(top_srcdir) \
$(GNOMECC_SHELL_CFLAGS)
-bin_PROGRAMS = gnome-control-center
+bin_PROGRAMS = gnome-control-center
menudir = $(sysconfdir)/xdg/menus
menu_DATA = gnomecc.menu
@@ -11,13 +11,20 @@ uidir = $(pkgdatadir)/ui
ui_DATA = shell.ui
gnome_control_center_SOURCES = \
+ cc-page.h \
+ cc-page.c \
+ cc-panel.h \
+ cc-panel.c \
control-center.c
gnome_control_center_LDADD = \
$(GNOMECC_SHELL_LIBS)
+gnome_control_center_LDFLAGS = -export-dynamic
+
AM_CPPFLAGS = \
-DGNOMELOCALEDIR="\"$(datadir)/locale\"" \
+ -DEXTENSION_DIR="\"$(libdir)/control-center-1/extensions\"" \
-DUIDIR="\"$(uidir)\"" \
-DMENUDIR="\"$(menudir)\""
diff --git a/shell/cc-page.c b/shell/cc-page.c
new file mode 100644
index 0000000..9b95aed
--- /dev/null
+++ b/shell/cc-page.c
@@ -0,0 +1,178 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+#include "cc-page.h"
+
+#define CC_PAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_PAGE, CcPagePrivate))
+
+struct CcPagePrivate
+{
+ char *id;
+ char *display_name;
+};
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_DISPLAY_NAME,
+};
+
+static void cc_page_class_init (CcPageClass *klass);
+static void cc_page_init (CcPage *page);
+static void cc_page_finalize (GObject *object);
+
+G_DEFINE_ABSTRACT_TYPE (CcPage, cc_page, GTK_TYPE_ALIGNMENT)
+
+static void
+_cc_page_set_id (CcPage *page,
+ const char *id)
+{
+ g_free (page->priv->id);
+ page->priv->id = g_strdup (id);
+}
+
+static void
+_cc_page_set_display_name (CcPage *page,
+ const char *name)
+{
+ g_free (page->priv->display_name);
+ page->priv->display_name = g_strdup (name);
+}
+
+static void
+cc_page_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcPage *self;
+
+ self = CC_PAGE (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ _cc_page_set_id (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_NAME:
+ _cc_page_set_display_name (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_page_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcPage *self;
+
+ self = CC_PAGE (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_value_set_string (value, self->priv->id);
+ break;
+ case PROP_DISPLAY_NAME:
+ g_value_set_string (value, self->priv->display_name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+cc_page_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ CcPage *page;
+
+ page = CC_PAGE (G_OBJECT_CLASS (cc_page_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ return G_OBJECT (page);
+}
+
+static void
+cc_page_class_init (CcPageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_page_get_property;
+ object_class->set_property = cc_page_set_property;
+ object_class->constructor = cc_page_constructor;
+ object_class->finalize = cc_page_finalize;
+
+ g_type_class_add_private (klass, sizeof (CcPagePrivate));
+
+ g_object_class_install_property (object_class,
+ PROP_ID,
+ g_param_spec_string ("id",
+ "id",
+ "id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_NAME,
+ g_param_spec_string ("display-name",
+ "display name",
+ "display name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+cc_page_init (CcPage *page)
+{
+
+ page->priv = CC_PAGE_GET_PRIVATE (page);
+}
+
+static void
+cc_page_finalize (GObject *object)
+{
+ CcPage *page;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CC_IS_PAGE (object));
+
+ page = CC_PAGE (object);
+
+ g_return_if_fail (page->priv != NULL);
+
+ g_free (page->priv->id);
+ g_free (page->priv->display_name);
+
+ G_OBJECT_CLASS (cc_page_parent_class)->finalize (object);
+}
diff --git a/shell/cc-page.h b/shell/cc-page.h
new file mode 100644
index 0000000..8ab20e0
--- /dev/null
+++ b/shell/cc-page.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __CC_PAGE_H
+#define __CC_PAGE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_PAGE (cc_page_get_type ())
+#define CC_PAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_PAGE, CcPage))
+#define CC_PAGE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_PAGE, CcPageClass))
+#define CC_IS_PAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_PAGE))
+#define CC_IS_PAGE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_PAGE))
+#define CC_PAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_PAGE, CcPageClass))
+
+typedef struct CcPagePrivate CcPagePrivate;
+
+typedef struct
+{
+ GtkAlignment parent;
+ CcPagePrivate *priv;
+} CcPage;
+
+typedef struct
+{
+ GtkAlignmentClass parent_class;
+} CcPageClass;
+
+GType cc_page_get_type (void);
+
+G_END_DECLS
+
+#endif /* __CC_PAGE_H */
diff --git a/shell/cc-panel.c b/shell/cc-panel.c
new file mode 100644
index 0000000..6d53f6d
--- /dev/null
+++ b/shell/cc-panel.c
@@ -0,0 +1,182 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+#include "cc-panel.h"
+
+#define CC_PANEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_PANEL, CcPanelPrivate))
+
+struct CcPanelPrivate
+{
+ char *id;
+ char *display_name;
+ char *category;
+ char *current_location;
+};
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_DISPLAY_NAME,
+ PROP_CATEGORY,
+ PROP_CURRENT_LOCATION,
+};
+
+static void cc_panel_class_init (CcPanelClass *klass);
+static void cc_panel_init (CcPanel *panel);
+static void cc_panel_finalize (GObject *object);
+
+G_DEFINE_ABSTRACT_TYPE (CcPanel, cc_panel, GTK_TYPE_ALIGNMENT)
+
+static void
+_cc_panel_set_id (CcPanel *panel,
+ const char *id)
+{
+ g_free (panel->priv->id);
+ panel->priv->id = g_strdup (id);
+}
+
+static void
+_cc_panel_set_display_name (CcPanel *panel,
+ const char *name)
+{
+ g_free (panel->priv->display_name);
+ panel->priv->display_name = g_strdup (name);
+}
+
+static void
+cc_panel_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcPanel *self;
+
+ self = CC_PANEL (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ _cc_panel_set_id (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_NAME:
+ _cc_panel_set_display_name (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_panel_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcPanel *self;
+
+ self = CC_PANEL (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_value_set_string (value, self->priv->id);
+ break;
+ case PROP_DISPLAY_NAME:
+ g_value_set_string (value, self->priv->display_name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+cc_panel_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ CcPanel *panel;
+
+ panel = CC_PANEL (G_OBJECT_CLASS (cc_panel_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ return G_OBJECT (panel);
+}
+
+static void
+cc_panel_class_init (CcPanelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_panel_get_property;
+ object_class->set_property = cc_panel_set_property;
+ object_class->constructor = cc_panel_constructor;
+ object_class->finalize = cc_panel_finalize;
+
+ g_type_class_add_private (klass, sizeof (CcPanelPrivate));
+
+ g_object_class_install_property (object_class,
+ PROP_ID,
+ g_param_spec_string ("id",
+ "id",
+ "id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_NAME,
+ g_param_spec_string ("display-name",
+ "display name",
+ "display name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+cc_panel_init (CcPanel *panel)
+{
+
+ panel->priv = CC_PANEL_GET_PRIVATE (panel);
+}
+
+static void
+cc_panel_finalize (GObject *object)
+{
+ CcPanel *panel;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CC_IS_PANEL (object));
+
+ panel = CC_PANEL (object);
+
+ g_return_if_fail (panel->priv != NULL);
+
+ g_free (panel->priv->id);
+ g_free (panel->priv->display_name);
+
+ G_OBJECT_CLASS (cc_panel_parent_class)->finalize (object);
+}
diff --git a/shell/cc-panel.h b/shell/cc-panel.h
new file mode 100644
index 0000000..162e366
--- /dev/null
+++ b/shell/cc-panel.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __CC_PANEL_H
+#define __CC_PANEL_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_PANEL (cc_panel_get_type ())
+#define CC_PANEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_PANEL, CcPanel))
+#define CC_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_PANEL, CcPanelClass))
+#define CC_IS_PANEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_PANEL))
+#define CC_IS_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_PANEL))
+#define CC_PANEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_PANEL, CcPanelClass))
+
+#define CC_PANEL_EXTENSION_POINT_NAME "control-center-panel-1"
+
+typedef struct CcPanelPrivate CcPanelPrivate;
+
+typedef struct
+{
+ GtkAlignment parent;
+ CcPanelPrivate *priv;
+} CcPanel;
+
+typedef struct
+{
+ GtkAlignmentClass parent_class;
+} CcPanelClass;
+
+GType cc_panel_get_type (void);
+
+G_END_DECLS
+
+#endif /* __CC_PANEL_H */
diff --git a/shell/control-center.c b/shell/control-center.c
index 7fc9a81..478a6bd 100644
--- a/shell/control-center.c
+++ b/shell/control-center.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2009, 2010 Intel, Inc.
+ * Copyright (c) 2010 Red Hat, Inc.
*
* The Control Center is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
@@ -18,14 +19,16 @@
* Author: Thomas Wood <thos gnome org>
*/
+#include <gio/gio.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <string.h>
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
#include <gnome-menus/gmenu-tree.h>
-#define W(b,x) GTK_WIDGET (gtk_builder_get_object (b, x))
+#include "cc-panel.h"
+#define W(b,x) GTK_WIDGET (gtk_builder_get_object (b, x))
typedef struct
{
@@ -45,6 +48,61 @@ typedef struct
void item_activated_cb (GtkIconView *icon_view, GtkTreePath *path, ShellData *data);
+static GHashTable *panels = NULL;
+
+static void
+load_panel_plugins (void)
+{
+ static volatile GType panel_type = G_TYPE_INVALID;
+ static GIOExtensionPoint *ep = NULL;
+ GList *modules;
+ GList *panel_implementations;
+ GList *l;
+
+ /* make sure base type is registered */
+ if (panel_type == G_TYPE_INVALID)
+ {
+ panel_type = g_type_from_name ("CcPanel");
+ }
+
+ panels = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+ if (ep == NULL)
+ {
+ g_debug ("Registering extension point");
+ ep = g_io_extension_point_register (CC_PANEL_EXTENSION_POINT_NAME);
+ }
+
+ /* load all modules */
+ g_debug ("Loading all modules in %s", EXTENSION_DIR);
+ modules = g_io_modules_load_all_in_directory (EXTENSION_DIR);
+
+ g_debug ("Loaded %d modules", g_list_length (modules));
+
+ /* find all extensions */
+ panel_implementations = g_io_extension_point_get_extensions (ep);
+ for (l = panel_implementations; l != NULL; l = l->next)
+ {
+ GIOExtension *extension;
+ CcPanel *panel;
+ char *id;
+
+ extension = l->data;
+
+ g_debug ("Found extension: %s %d", g_io_extension_get_name (extension), g_io_extension_get_priority (extension));
+ panel = g_object_new (g_io_extension_get_type (extension), NULL);
+ g_object_get (panel, "id", &id, NULL);
+ g_hash_table_insert (panels, g_strdup (id), g_object_ref (panel));
+ g_debug ("id: '%s'", id);
+ g_free (id);
+ }
+
+ /* unload all modules; the module our instantiated authority is in won't be unloaded because
+ * we've instantiated a reference to a type in this module
+ */
+ g_list_foreach (modules, (GFunc) g_type_module_unuse, NULL);
+ g_list_free (modules);
+}
gboolean
button_release_cb (GtkWidget *view,
@@ -176,11 +234,14 @@ fill_model (ShellData *data)
foo = gmenu_tree_directory_get_contents (l->data);
dir_name = gmenu_tree_directory_get_name (l->data);
- store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING,
+ store = gtk_list_store_new (4,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
GDK_TYPE_PIXBUF);
iconview = gtk_icon_view_new_with_model (GTK_TREE_MODEL (store));
- gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (iconview), 2);
+ gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (iconview), 3);
gtk_icon_view_set_text_column (GTK_ICON_VIEW (iconview), 0);
gtk_icon_view_set_item_width (GTK_ICON_VIEW (iconview), 120);
g_signal_connect (iconview, "item-activated",
@@ -214,6 +275,7 @@ fill_model (ShellData *data)
GError *err = NULL;
const gchar *icon = gmenu_tree_entry_get_icon (f->data);
const gchar *name = gmenu_tree_entry_get_name (f->data);
+ const gchar *id = gmenu_tree_entry_get_desktop_file_id (f->data);
const gchar *exec = gmenu_tree_entry_get_exec (f->data);
GdkPixbuf *pixbuf = NULL;
@@ -231,7 +293,8 @@ fill_model (ShellData *data)
gtk_list_store_insert_with_values (store, NULL, 0,
0, name,
1, exec,
- 2, pixbuf,
+ 2, id,
+ 3, pixbuf,
-1);
gtk_list_store_insert_with_values (data->store, NULL, 0,
@@ -246,37 +309,6 @@ fill_model (ShellData *data)
}
-static gboolean
-switch_after_delay (ShellData *data)
-{
- gtk_notebook_set_current_page (GTK_NOTEBOOK (data->notebook), 2);
-
- gtk_widget_show (W (data->builder, "home-button"));
-
- gtk_window_set_title (GTK_WINDOW (data->window), data->current_title);
-
- return FALSE;
-}
-
-void
-plug_added_cb (GtkSocket *socket,
- ShellData *data)
-{
- GtkWidget *notebook;
- GSList *l;
-
- notebook = W (data->builder, "notebook");
-
- /* FIXME: this shouldn't be necassary if the capplet doesn't add to the socket
- * until it is fully ready */
- g_timeout_add (100, (GSourceFunc) switch_after_delay, data);
-
- /* make sure no items are selected when the user switches back to the icon
- * views */
- for (l = data->icon_views; l; l = l->next)
- gtk_icon_view_unselect_all (GTK_ICON_VIEW (l->data));
-}
-
void
item_activated_cb (GtkIconView *icon_view,
GtkTreePath *path,
@@ -284,48 +316,61 @@ item_activated_cb (GtkIconView *icon_view,
{
GtkTreeModel *model;
GtkTreeIter iter = {0,};
- gchar *name, *exec, *command;
- GtkWidget *socket, *notebook;
- guint socket_id = 0;
+ gchar *name, *exec, *id, *markup;
+ GtkWidget *notebook;
static gint index = -1;
-
- /* create new socket */
- socket = gtk_socket_new ();
-
- g_signal_connect (socket, "plug-added", G_CALLBACK (plug_added_cb), data);
+ CcPanel *panel;
notebook = data->notebook;
if (index >= 0)
gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), index);
- index = gtk_notebook_append_page (GTK_NOTEBOOK (notebook), socket, NULL);
-
- gtk_widget_show (socket);
-
- socket_id = gtk_socket_get_id (GTK_SOCKET (socket));
/* get exec */
model = gtk_icon_view_get_model (icon_view);
gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_model_get (model, &iter, 0, &name, 1, &exec, -1);
+ gtk_tree_model_get (model, &iter, 0, &name, 1, &exec, 2, &id, -1);
+
+ g_debug ("activated id: '%s'", id);
g_free (data->current_title);
data->current_title = name;
- /* start app */
- command = g_strdup_printf ("%s --socket=%u", exec, socket_id);
- g_spawn_command_line_async (command, NULL);
- g_free (command);
+ /* first look for a panel module */
+ panel = g_hash_table_lookup (panels, id);
+ if (panel != NULL)
+ {
+ index = gtk_notebook_append_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (panel), NULL);
+ gtk_widget_show_all (GTK_WIDGET (panel));
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), index);
+ gtk_widget_show (W (data->builder, "home-button"));
+ gtk_window_set_title (GTK_WINDOW (data->window), data->current_title);
+ }
+ else
+ {
+ /* start app directly */
+ g_debug ("Panel module not found for %s", id);
+ g_spawn_command_line_async (exec, NULL);
+ }
+ g_free (id);
g_free (exec);
}
-void
+static void
home_button_clicked_cb (GtkButton *button,
ShellData *data)
{
+ int page;
+ GtkWidget *widget;
+
+ page = gtk_notebook_get_current_page (GTK_NOTEBOOK (data->notebook));
gtk_notebook_set_current_page (GTK_NOTEBOOK (data->notebook), 0);
+ widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (data->notebook), page);
+ gtk_widget_hide (widget);
+ gtk_notebook_remove_page (GTK_NOTEBOOK (data->notebook), page);
+
gtk_window_set_title (GTK_WINDOW (data->window), "System Settings");
gtk_widget_hide (GTK_WIDGET (button));
@@ -415,6 +460,8 @@ main (int argc, char **argv)
g_signal_connect (widget, "key-press-event",
G_CALLBACK (search_entry_key_press_event_cb), data);
+ load_panel_plugins ();
+
gtk_widget_show_all (data->window);
gtk_main ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]