summaryrefslogtreecommitdiff
path: root/mate-panel/libpanel-util/panel-icon-chooser.c
diff options
context:
space:
mode:
Diffstat (limited to 'mate-panel/libpanel-util/panel-icon-chooser.c')
-rw-r--r--mate-panel/libpanel-util/panel-icon-chooser.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/mate-panel/libpanel-util/panel-icon-chooser.c b/mate-panel/libpanel-util/panel-icon-chooser.c
new file mode 100644
index 00000000..07d14641
--- /dev/null
+++ b/mate-panel/libpanel-util/panel-icon-chooser.c
@@ -0,0 +1,555 @@
+/*
+ * panel-icon-chooser.c: An icon chooser widget
+ *
+ * Copyright (C) 2010 Novell, 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.
+ *
+ * Authors:
+ * Vincent Untz <[email protected]>
+ */
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "panel-gtk.h"
+#include "panel-xdg.h"
+
+#include "panel-icon-chooser.h"
+
+#define PANEL_ICON_CHOOSER_ICON_SIZE GTK_ICON_SIZE_DIALOG
+
+struct _PanelIconChooserPrivate
+{
+ char *fallback_icon_name;
+ char *icon;
+
+ char *icon_theme_dir;
+
+ GtkWidget *image;
+
+ GtkWidget *filechooser;
+};
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_FALLBACK_ICON,
+ PROP_ICON
+};
+
+static guint panel_icon_chooser_signals[LAST_SIGNAL] = { 0 };
+
+#define PANEL_ICON_CHOOSER_GET_PRIVATE(o) (PANEL_ICON_CHOOSER (o)->priv)
+
+G_DEFINE_TYPE (PanelIconChooser, panel_icon_chooser, GTK_TYPE_BUTTON)
+
+static void _panel_icon_chooser_clicked (GtkButton *button);
+static void _panel_icon_chooser_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+static void _panel_icon_chooser_screen_changed (GtkWidget *widget,
+ GdkScreen *prev_screen);
+
+/* gobject stuff */
+
+static GObject *
+panel_icon_chooser_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GObject *obj;
+ PanelIconChooser *chooser;
+
+ obj = G_OBJECT_CLASS (panel_icon_chooser_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties);
+
+ chooser = PANEL_ICON_CHOOSER (obj);
+ gtk_container_add (GTK_CONTAINER (chooser), chooser->priv->image);
+ gtk_widget_show (chooser->priv->image);
+
+ return obj;
+}
+
+static void
+panel_icon_chooser_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PanelIconChooser *chooser;
+
+ g_return_if_fail (PANEL_IS_ICON_CHOOSER (object));
+
+ chooser = PANEL_ICON_CHOOSER (object);
+
+ switch (prop_id) {
+ case PROP_FALLBACK_ICON:
+ g_value_set_string (value, panel_icon_chooser_get_fallback_icon_name (chooser));
+ break;
+ case PROP_ICON:
+ g_value_set_string (value, panel_icon_chooser_get_icon (chooser));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+panel_icon_chooser_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PanelIconChooser *chooser;
+
+ g_return_if_fail (PANEL_IS_ICON_CHOOSER (object));
+
+ chooser = PANEL_ICON_CHOOSER (object);
+
+ switch (prop_id) {
+ case PROP_FALLBACK_ICON:
+ panel_icon_chooser_set_fallback_icon_name (chooser,
+ g_value_get_string (value));
+ break;
+ case PROP_ICON:
+ panel_icon_chooser_set_icon (chooser,
+ g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+panel_icon_chooser_destroy (GtkObject *object)
+{
+ PanelIconChooser *chooser;
+
+ chooser = PANEL_ICON_CHOOSER (object);
+
+ if (chooser->priv->filechooser) {
+ gtk_widget_destroy (chooser->priv->filechooser);
+ chooser->priv->filechooser = NULL;
+ }
+
+ /* remember, destroy can be run multiple times! */
+
+ if (chooser->priv->fallback_icon_name != NULL)
+ g_free (chooser->priv->fallback_icon_name);
+ chooser->priv->fallback_icon_name = NULL;
+
+ if (chooser->priv->icon != NULL)
+ g_free (chooser->priv->icon);
+ chooser->priv->icon = NULL;
+
+ if (chooser->priv->icon_theme_dir != NULL)
+ g_free (chooser->priv->icon_theme_dir);
+ chooser->priv->icon_theme_dir = NULL;
+
+ GTK_OBJECT_CLASS (panel_icon_chooser_parent_class)->destroy (object);
+}
+
+static void
+panel_icon_chooser_class_init (PanelIconChooserClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (class);
+ GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (class);
+ GtkButtonClass *gtkbutton_class = GTK_BUTTON_CLASS (class);
+
+ gobject_class->constructor = panel_icon_chooser_constructor;
+ gobject_class->get_property = panel_icon_chooser_get_property;
+ gobject_class->set_property = panel_icon_chooser_set_property;
+
+ gtkobject_class->destroy = panel_icon_chooser_destroy;
+
+ gtkwidget_class->style_set = _panel_icon_chooser_style_set;
+ gtkwidget_class->screen_changed = _panel_icon_chooser_screen_changed;
+
+ gtkbutton_class->clicked = _panel_icon_chooser_clicked;
+
+ g_type_class_add_private (class,
+ sizeof (PanelIconChooserPrivate));
+
+ panel_icon_chooser_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PanelIconChooserClass,
+ changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_FALLBACK_ICON,
+ g_param_spec_string ("fallback-icon-name",
+ "Fallback Icon Name",
+ "Icon name of the icon displayed (but not returned) if the current icon does not exit",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_ICON,
+ g_param_spec_string ("icon",
+ "Icon",
+ "Icon name or path",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static void
+panel_icon_chooser_init (PanelIconChooser *chooser)
+{
+ PanelIconChooserPrivate *priv;
+
+ priv = G_TYPE_INSTANCE_GET_PRIVATE (chooser,
+ PANEL_TYPE_ICON_CHOOSER,
+ PanelIconChooserPrivate);
+
+ chooser->priv = priv;
+
+ priv->fallback_icon_name = g_strdup ("gtk-missing-image");
+ priv->icon = NULL;
+ priv->icon_theme_dir = NULL;
+
+ priv->image = gtk_image_new_from_icon_name (priv->fallback_icon_name,
+ PANEL_ICON_CHOOSER_ICON_SIZE);
+
+ priv->filechooser = NULL;
+}
+
+/* internal code */
+
+static void
+_panel_icon_chooser_update (PanelIconChooser *chooser)
+{
+ if (!chooser->priv->icon) {
+ gtk_image_set_from_icon_name (GTK_IMAGE (chooser->priv->image),
+ chooser->priv->fallback_icon_name,
+ PANEL_ICON_CHOOSER_ICON_SIZE);
+
+ } else if (g_path_is_absolute (chooser->priv->icon)) {
+ gboolean fallback;
+
+ fallback = TRUE;
+
+ if (g_file_test (chooser->priv->icon, G_FILE_TEST_EXISTS)) {
+ /* we pass via a pixbuf to force the size we want */
+ GdkPixbuf *pixbuf;
+ int width, height;
+
+ gtk_icon_size_lookup (PANEL_ICON_CHOOSER_ICON_SIZE,
+ &width, &height);
+ pixbuf = gdk_pixbuf_new_from_file_at_size (chooser->priv->icon,
+ width, height,
+ NULL);
+
+ if (pixbuf) {
+ gtk_image_set_from_pixbuf (GTK_IMAGE (chooser->priv->image),
+ pixbuf);
+ g_object_unref (pixbuf);
+ fallback = FALSE;
+ }
+ }
+
+ if (fallback) {
+ gtk_image_set_from_icon_name (GTK_IMAGE (chooser->priv->image),
+ chooser->priv->fallback_icon_name,
+ PANEL_ICON_CHOOSER_ICON_SIZE);
+ }
+
+ } else {
+ /* Note: using GThemedIcon doesn't work well, see bug #606752.
+ * When we'll remove the alternative code, we won't need the
+ * style_set/screen_changed handlers anymore.
+ */
+#if 0
+ GIcon *icon;
+ char *names[2];
+
+ names[0] = panel_xdg_icon_remove_extension (chooser->priv->icon);
+ names[1] = chooser->priv->fallback_icon_name;
+ icon = g_themed_icon_new_from_names (names, 2);
+
+ gtk_image_set_from_gicon (GTK_IMAGE (chooser->priv->image),
+ icon,
+ PANEL_ICON_CHOOSER_ICON_SIZE);
+
+ g_free (names[0]);
+#endif
+ GtkIconTheme *icon_theme;
+ const char *icon;
+ char *no_ext;
+
+ no_ext = panel_xdg_icon_remove_extension (chooser->priv->icon);
+
+ icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (chooser)));
+ if (gtk_icon_theme_has_icon (icon_theme, no_ext))
+ icon = no_ext;
+ else
+ icon = chooser->priv->fallback_icon_name;
+
+ gtk_image_set_from_icon_name (GTK_IMAGE (chooser->priv->image),
+ icon,
+ PANEL_ICON_CHOOSER_ICON_SIZE);
+
+ g_free (no_ext);
+ }
+}
+
+static char *
+_panel_icon_chooser_find_icon_from_path (PanelIconChooser *chooser,
+ const char *path)
+{
+ GdkScreen *screen;
+ char *icon;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (chooser));
+
+ icon = panel_xdg_icon_name_from_icon_path (path, screen);
+ if (!icon)
+ icon = g_strdup (path);
+
+ return icon;
+}
+
+static void
+_panel_icon_chooser_file_chooser_response (GtkFileChooser *filechooser,
+ gint response_id,
+ PanelIconChooser *chooser)
+{
+ if (response_id == GTK_RESPONSE_ACCEPT) {
+ char *path;
+ char *icon;
+
+ path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser));
+ icon = _panel_icon_chooser_find_icon_from_path (chooser, path);
+ g_free (path);
+
+ panel_icon_chooser_set_icon (chooser, icon);
+ g_free (icon);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (filechooser));
+}
+
+static void
+_panel_icon_chooser_clicked (GtkButton *button)
+{
+ PanelIconChooser *chooser = PANEL_ICON_CHOOSER (button);
+ GtkWidget *filechooser;
+ GtkWidget *toplevel;
+ GtkWindow *parent;
+ char *path;
+ gboolean filechooser_path_set;
+
+ if (chooser->priv->filechooser) {
+ gtk_window_present (GTK_WINDOW (chooser->priv->filechooser));
+ return;
+ }
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ if (gtk_widget_is_toplevel (toplevel))
+ parent = GTK_WINDOW (toplevel);
+ else
+ parent = NULL;
+
+ filechooser = gtk_file_chooser_dialog_new (_("Choose an icon"),
+ parent,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN,
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+ panel_gtk_file_chooser_add_image_preview (GTK_FILE_CHOOSER (filechooser));
+
+ path = g_build_filename (DATADIR, "icons", NULL);
+ gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (filechooser),
+ path, NULL);
+ g_free (path);
+
+ path = g_build_filename (DATADIR, "pixmaps", NULL);
+ gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (filechooser),
+ path, NULL);
+ g_free (path);
+
+ filechooser_path_set = FALSE;
+
+ if (chooser->priv->icon) {
+ char *path = NULL;
+ if (g_path_is_absolute (chooser->priv->icon)) {
+ path = g_strdup (chooser->priv->icon);
+ } else {
+ GtkIconTheme *icon_theme;
+ GtkIconInfo *info;
+ char *no_ext;
+ int size;
+
+ icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (chooser)));
+ no_ext = panel_xdg_icon_remove_extension (chooser->priv->icon);
+ gtk_icon_size_lookup (PANEL_ICON_CHOOSER_ICON_SIZE,
+ &size, NULL);
+
+ info = gtk_icon_theme_lookup_icon (icon_theme, no_ext,
+ size, 0);
+ g_free (no_ext);
+
+ if (info) {
+ path = g_strdup (gtk_icon_info_get_filename (info));
+ gtk_icon_info_free (info);
+ }
+ }
+
+ if (path) {
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filechooser),
+ path);
+ g_free (path);
+ filechooser_path_set = TRUE;
+ }
+ }
+
+ if (!filechooser_path_set) {
+ char *path;
+ // FIXME? Use current icon theme? But there might not be a lot
+ // of icons there...
+ path = g_build_filename (DATADIR, "icons", NULL);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filechooser),
+ path);
+ }
+
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (filechooser), TRUE);
+
+ g_signal_connect (filechooser, "response",
+ G_CALLBACK (_panel_icon_chooser_file_chooser_response),
+ chooser);
+
+ chooser->priv->filechooser = filechooser;
+
+ g_signal_connect (G_OBJECT (filechooser), "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &chooser->priv->filechooser);
+
+ gtk_widget_show (filechooser);
+}
+
+static void
+_panel_icon_chooser_style_set (GtkWidget *widget,
+ GtkStyle *prev_style)
+{
+ PanelIconChooser *chooser;
+
+ chooser = PANEL_ICON_CHOOSER (widget);
+
+ GTK_WIDGET_CLASS (panel_icon_chooser_parent_class)->style_set (widget, prev_style);
+
+ _panel_icon_chooser_update (chooser);
+}
+
+static void
+_panel_icon_chooser_screen_changed (GtkWidget *widget,
+ GdkScreen *prev_screen)
+{
+ PanelIconChooser *chooser;
+
+ chooser = PANEL_ICON_CHOOSER (widget);
+
+ if (GTK_WIDGET_CLASS (panel_icon_chooser_parent_class)->screen_changed)
+ GTK_WIDGET_CLASS (panel_icon_chooser_parent_class)->screen_changed (widget, prev_screen);
+
+ _panel_icon_chooser_update (chooser);
+}
+
+/* public methods */
+
+GtkWidget *
+panel_icon_chooser_new (const char *icon)
+{
+ GtkWidget *chooser;
+
+ chooser = g_object_new (PANEL_TYPE_ICON_CHOOSER,
+ "icon", icon,
+ NULL);
+
+ return chooser;
+}
+
+const char *
+panel_icon_chooser_get_fallback_icon_name (PanelIconChooser *chooser)
+{
+ g_return_val_if_fail (PANEL_IS_ICON_CHOOSER (chooser), NULL);
+
+ return chooser->priv->fallback_icon_name;
+}
+
+void
+panel_icon_chooser_set_fallback_icon_name (PanelIconChooser *chooser,
+ const char *fallback_icon_name)
+{
+ g_return_if_fail (PANEL_IS_ICON_CHOOSER (chooser));
+
+ if (g_strcmp0 (chooser->priv->fallback_icon_name, fallback_icon_name) == 0)
+ return;
+
+ if (chooser->priv->fallback_icon_name)
+ g_free (chooser->priv->fallback_icon_name);
+ chooser->priv->fallback_icon_name = g_strdup (fallback_icon_name);
+
+ _panel_icon_chooser_update (chooser);
+
+ g_object_notify (G_OBJECT (chooser), "fallback-icon-name");
+}
+
+const char *
+panel_icon_chooser_get_icon (PanelIconChooser *chooser)
+{
+ g_return_val_if_fail (PANEL_IS_ICON_CHOOSER (chooser), NULL);
+
+ return chooser->priv->icon;
+}
+
+void
+panel_icon_chooser_set_icon (PanelIconChooser *chooser,
+ const char *icon)
+{
+ g_return_if_fail (PANEL_IS_ICON_CHOOSER (chooser));
+
+ if (g_strcmp0 (chooser->priv->icon, icon) == 0)
+ return;
+
+ if (chooser->priv->icon)
+ g_free (chooser->priv->icon);
+ chooser->priv->icon = g_strdup (icon);
+
+ _panel_icon_chooser_update (chooser);
+
+ g_object_notify (G_OBJECT (chooser), "icon");
+
+ g_signal_emit (G_OBJECT (chooser),
+ panel_icon_chooser_signals[CHANGED], 0, icon);
+}