[gthumb] contact_sheet: allow to copy the settings of another theme



commit 773d50d695e228c270103fad82e88a38d7518963
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sat Jan 1 15:06:18 2011 +0100

    contact_sheet: allow to copy the settings of another theme

 extensions/contact_sheet/dlg-contact-sheet.c       |   38 ++-
 .../contact_sheet/gth-contact-sheet-theme-dialog.c |   73 +++-
 .../contact_sheet/gth-contact-sheet-theme-dialog.h |    3 +-
 extensions/contact_sheet/gth-contact-sheet-theme.c |   20 +
 extensions/contact_sheet/gth-contact-sheet-theme.h |    3 +
 gthumb/Makefile.am                                 |    2 +
 gthumb/gth-menu-button.c                           |  576 ++++++++++++++++++++
 gthumb/gth-menu-button.h                           |   77 +++
 8 files changed, 787 insertions(+), 5 deletions(-)
---
diff --git a/extensions/contact_sheet/dlg-contact-sheet.c b/extensions/contact_sheet/dlg-contact-sheet.c
index 3125d71..ee9622f 100644
--- a/extensions/contact_sheet/dlg-contact-sheet.c
+++ b/extensions/contact_sheet/dlg-contact-sheet.c
@@ -546,25 +546,54 @@ theme_dialog_response_cb (GtkDialog *dialog,
 }
 
 
+static GList *
+get_all_themes (DialogData *data)
+{
+	GList        *list = NULL;
+	GtkTreeModel *model;
+	GtkTreeIter   iter;
+
+	model = GTK_TREE_MODEL (GET_WIDGET ("theme_liststore"));
+	if (gtk_tree_model_get_iter_first (model, &iter))
+		do {
+			GthContactSheetTheme *theme;
+
+			gtk_tree_model_get (model, &iter,
+					    THEME_COLUMN_THEME, &theme,
+					    -1);
+			if (theme != NULL)
+				list = g_list_prepend (list, gth_contact_sheet_theme_ref (theme));
+		}
+		while (gtk_tree_model_iter_next (model, &iter));
+
+	return g_list_reverse (list);
+}
+
+
 static void
 edit_theme_button_clicked_cb (GtkButton *button,
 			      gpointer   user_data)
 {
 	DialogData           *data = user_data;
 	GthContactSheetTheme *theme;
+	GList                *all_themes;
 	GtkWidget            *theme_dialog;
 
 	theme = get_selected_theme (data);
 	if ((theme == NULL) || ! theme->editable)
 		return;
 
-	theme_dialog = gth_contact_sheet_theme_dialog_new (theme);
+	all_themes = get_all_themes (data);
+	theme_dialog = gth_contact_sheet_theme_dialog_new (theme, all_themes);
 	g_signal_connect (theme_dialog,
 			  "response",
 			  G_CALLBACK (theme_dialog_response_cb),
 			  data);
 	gtk_window_set_transient_for (GTK_WINDOW (theme_dialog), GTK_WINDOW (data->dialog));
+	gtk_window_set_modal (GTK_WINDOW (theme_dialog), TRUE);
 	gtk_widget_show (theme_dialog);
+
+	gth_contact_sheet_theme_list_free (all_themes);
 }
 
 
@@ -573,15 +602,20 @@ add_theme_button_clicked_cb (GtkButton *button,
 			     gpointer   user_data)
 {
 	DialogData *data = user_data;
+	GList      *all_themes;
 	GtkWidget  *theme_dialog;
 
-	theme_dialog = gth_contact_sheet_theme_dialog_new (NULL);
+	all_themes = get_all_themes (data);
+	theme_dialog = gth_contact_sheet_theme_dialog_new (NULL, all_themes);
 	g_signal_connect (theme_dialog,
 			  "response",
 			  G_CALLBACK (theme_dialog_response_cb),
 			  data);
 	gtk_window_set_transient_for (GTK_WINDOW (theme_dialog), GTK_WINDOW (data->dialog));
+	gtk_window_set_modal (GTK_WINDOW (theme_dialog), TRUE);
 	gtk_widget_show (theme_dialog);
+
+	gth_contact_sheet_theme_list_free (all_themes);
 }
 
 
diff --git a/extensions/contact_sheet/gth-contact-sheet-theme-dialog.c b/extensions/contact_sheet/gth-contact-sheet-theme-dialog.c
index 6c3c8f5..d91af96 100644
--- a/extensions/contact_sheet/gth-contact-sheet-theme-dialog.c
+++ b/extensions/contact_sheet/gth-contact-sheet-theme-dialog.c
@@ -31,7 +31,10 @@ static gpointer parent_class = NULL;
 
 struct _GthContactSheetThemeDialogPrivate {
 	GtkBuilder           *builder;
+	GtkWidget            *copy_from_button;
+	GtkWidget            *copy_from_menu;
 	GthContactSheetTheme *theme;
+	GList                *all_themes;
 };
 
 
@@ -44,6 +47,7 @@ gth_contact_sheet_theme_dialog_finalize (GObject *object)
 
 	_g_object_unref (self->priv->builder);
 	gth_contact_sheet_theme_unref (self->priv->theme);
+	gth_contact_sheet_theme_list_free (self->priv->all_themes);
 
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -188,6 +192,7 @@ gth_contact_sheet_theme_dialog_init (GthContactSheetThemeDialog *self)
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_CONTACT_SHEET_THEME_DIALOG, GthContactSheetThemeDialogPrivate);
 	self->priv->builder = _gtk_builder_new_from_file ("contact-sheet-theme-properties.ui", "contact_sheet");
 	self->priv->theme = NULL;
+	self->priv->all_themes = NULL;
 
 	gtk_window_set_title (GTK_WINDOW (self), _("Theme Properties"));
 	gtk_window_set_resizable (GTK_WINDOW (self), TRUE);
@@ -202,11 +207,25 @@ gth_contact_sheet_theme_dialog_init (GthContactSheetThemeDialog *self)
 	gtk_container_set_border_width (GTK_CONTAINER (content), 5);
 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), content, TRUE, TRUE, 0);
 
+	/* "Copy from" button */
+
+	self->priv->copy_from_button = gth_menu_button_new ();
+	gth_menu_button_set_label (GTH_MENU_BUTTON (self->priv->copy_from_button), _("Copy _From"));
+	gth_menu_button_set_use_underline (GTH_MENU_BUTTON (self->priv->copy_from_button), TRUE);
+	gtk_widget_show (self->priv->copy_from_button);
+
+	self->priv->copy_from_menu = gtk_menu_new ();
+	gth_menu_button_set_menu (GTH_MENU_BUTTON (self->priv->copy_from_button), self->priv->copy_from_menu);
+
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (self))), self->priv->copy_from_button, FALSE, FALSE, 0);
+
+	/* other buttons */
+
 	gtk_dialog_add_button (GTK_DIALOG (self),
 			       GTK_STOCK_CANCEL,
 			       GTK_RESPONSE_CANCEL);
 	gtk_dialog_add_button (GTK_DIALOG (self),
-			       GTK_STOCK_OK,
+			       GTK_STOCK_SAVE,
 			       GTK_RESPONSE_OK);
 	gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
 
@@ -405,20 +424,70 @@ update_controls_from_theme (GthContactSheetThemeDialog *self,
 
 
 static void
+copy_from_menu_item_activate_cb (GtkMenuItem *menu_item,
+         			 gpointer     user_data)
+{
+	GthContactSheetThemeDialog *self = user_data;
+	char                       *display_name;
+	GFile                      *file;
+	GthContactSheetTheme       *theme;
+
+	if ((self->priv->theme != NULL) && (self->priv->theme->file != NULL))
+		file = g_file_dup (self->priv->theme->file);
+	else
+		file = NULL;
+	display_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("name_entry"))));
+
+	theme = g_object_get_data (G_OBJECT (menu_item), "theme");
+	if (theme != NULL)
+		update_controls_from_theme (self, theme);
+	gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("name_entry")), display_name);
+
+	_g_object_unref (self->priv->theme->file);
+	self->priv->theme->file = _g_object_ref (file);
+
+	g_free (display_name);
+	_g_object_unref (file);
+}
+
+
+static void
 gth_contact_sheet_theme_dialog_construct (GthContactSheetThemeDialog *self,
 					  GthContactSheetTheme       *theme)
 {
+	GList *scan;
+
+	for (scan = self->priv->all_themes; scan; scan = scan->next) {
+		GthContactSheetTheme *other_theme = scan->data;
+		GtkWidget            *menu_item;
+
+		if ((theme != NULL) && g_file_equal (theme->file, other_theme->file))
+			continue;
+
+		menu_item = gtk_menu_item_new_with_label (other_theme->display_name);
+		g_object_set_data (G_OBJECT (menu_item), "theme", other_theme);
+		gtk_widget_show (menu_item);
+		g_signal_connect (menu_item,
+				  "activate",
+				  G_CALLBACK (copy_from_menu_item_activate_cb),
+				  self);
+
+		gtk_menu_shell_append (GTK_MENU_SHELL (self->priv->copy_from_menu), menu_item);
+	}
+
 	update_controls_from_theme (self, theme);
 	gtk_widget_queue_draw (GET_WIDGET ("preview_area"));
 }
 
 
 GtkWidget *
-gth_contact_sheet_theme_dialog_new (GthContactSheetTheme *theme)
+gth_contact_sheet_theme_dialog_new (GthContactSheetTheme *theme,
+				    GList                *all_themes)
 {
 	GthContactSheetThemeDialog *self;
 
 	self = g_object_new (GTH_TYPE_CONTACT_SHEET_THEME_DIALOG, NULL);
+	self->priv->all_themes = gth_contact_sheet_theme_list_copy (all_themes);
 	gth_contact_sheet_theme_dialog_construct (self, theme);
 
 	return (GtkWidget *) self;
diff --git a/extensions/contact_sheet/gth-contact-sheet-theme-dialog.h b/extensions/contact_sheet/gth-contact-sheet-theme-dialog.h
index eb4ccfa..41da202 100644
--- a/extensions/contact_sheet/gth-contact-sheet-theme-dialog.h
+++ b/extensions/contact_sheet/gth-contact-sheet-theme-dialog.h
@@ -49,7 +49,8 @@ struct _GthContactSheetThemeDialogClass {
 };
 
 GType                   gth_contact_sheet_theme_dialog_get_type    (void);
-GtkWidget *             gth_contact_sheet_theme_dialog_new         (GthContactSheetTheme       *theme);
+GtkWidget *             gth_contact_sheet_theme_dialog_new         (GthContactSheetTheme       *theme,
+								    GList                      *all_themes);
 GthContactSheetTheme *  gth_contact_sheet_theme_dialog_get_theme   (GthContactSheetThemeDialog *self);
 
 G_END_DECLS
diff --git a/extensions/contact_sheet/gth-contact-sheet-theme.c b/extensions/contact_sheet/gth-contact-sheet-theme.c
index cfac6c3..b09a5eb 100644
--- a/extensions/contact_sheet/gth-contact-sheet-theme.c
+++ b/extensions/contact_sheet/gth-contact-sheet-theme.c
@@ -653,3 +653,23 @@ gth_contact_sheet_theme_create_preview (GthContactSheetTheme *theme,
 
 	return pixbuf;
 }
+
+
+GList *
+gth_contact_sheet_theme_list_copy (GList *list)
+{
+	GList *new_list;
+
+	new_list = g_list_copy (list);
+	g_list_foreach (new_list, (GFunc) gth_contact_sheet_theme_ref, NULL);
+
+	return new_list;
+}
+
+
+void
+gth_contact_sheet_theme_list_free (GList *list)
+{
+	g_list_foreach (list, (GFunc) gth_contact_sheet_theme_unref, NULL);
+	g_list_free (list);
+}
diff --git a/extensions/contact_sheet/gth-contact-sheet-theme.h b/extensions/contact_sheet/gth-contact-sheet-theme.h
index d2f4f9a..3631710 100644
--- a/extensions/contact_sheet/gth-contact-sheet-theme.h
+++ b/extensions/contact_sheet/gth-contact-sheet-theme.h
@@ -101,4 +101,7 @@ void                   gth_contact_sheet_theme_paint_preview     (GthContactShee
 GdkPixbuf *            gth_contact_sheet_theme_create_preview    (GthContactSheetTheme  *theme,
 								  int                    preview_size);
 
+GList *                gth_contact_sheet_theme_list_copy         (GList *list);
+void                   gth_contact_sheet_theme_list_free         (GList *list);
+
 #endif /* GTH_CONTACT_SHEET_THEME_H */
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index 695c9f6..280f0e4 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -78,6 +78,7 @@ PUBLIC_HEADER_FILES = 					\
 	gth-info-bar.h					\
 	gth-location-chooser.h				\
 	gth-main.h					\
+	gth-menu-button.h				\
 	gth-metadata.h					\
 	gth-metadata-chooser.h				\
 	gth-metadata-provider.h				\
@@ -199,6 +200,7 @@ gthumb_SOURCES = 					\
 	gth-main-default-sort-types.c			\
 	gth-main-default-tests.c			\
 	gth-main-default-types.c			\
+	gth-menu-button.c				\
 	gth-metadata.c					\
 	gth-metadata-chooser.c				\
 	gth-metadata-provider.c				\
diff --git a/gthumb/gth-menu-button.c b/gthumb/gth-menu-button.c
new file mode 100644
index 0000000..2106ec2
--- /dev/null
+++ b/gthumb/gth-menu-button.c
@@ -0,0 +1,576 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009, 2010 Free Software Foundation, 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include "gth-menu-button.h"
+
+#define MENU_ID "gth-menu-button-menu-id"
+
+struct _GthMenuButtonPrivate {
+	guint      active : 1;
+	GtkMenu   *menu;
+	GtkWidget *icon_widget;
+	GtkWidget *label_widget;
+};
+
+enum {
+	SHOW_MENU,
+	LAST_SIGNAL
+};
+
+enum {
+	PROP_0,
+	PROP_LABEL,
+	PROP_USE_UNDERLINE,
+	PROP_STOCK_ID,
+	PROP_ICON_NAME,
+	PROP_MENU
+};
+
+static gpointer parent_class = NULL;
+static int signals[LAST_SIGNAL];
+
+
+static void
+gth_menu_button_state_changed (GtkWidget    *widget,
+			       GtkStateType  previous_state)
+{
+	GthMenuButton *self = GTH_MENU_BUTTON (widget);
+
+	if (! gtk_widget_is_sensitive (widget) && (self->priv->menu != NULL))
+		gtk_menu_shell_deactivate (GTK_MENU_SHELL (self->priv->menu));
+}
+
+
+static void
+gth_menu_button_set_property (GObject      *object,
+			      guint         prop_id,
+			      const GValue *value,
+			      GParamSpec   *pspec)
+{
+	GthMenuButton *self = GTH_MENU_BUTTON (object);
+
+	switch (prop_id) {
+	case PROP_LABEL:
+		gth_menu_button_set_label (self, g_value_get_string (value));
+		break;
+	case PROP_USE_UNDERLINE:
+		gth_menu_button_set_use_underline (self, g_value_get_boolean (value));
+		break;
+	case PROP_STOCK_ID:
+		gth_menu_button_set_stock_id (self, g_value_get_string (value));
+		break;
+	case PROP_ICON_NAME:
+		gth_menu_button_set_icon_name (self, g_value_get_string (value));
+		break;
+	case PROP_MENU:
+		gth_menu_button_set_menu (self, g_value_get_object (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+
+static void
+gth_menu_button_get_property (GObject    *object,
+			      guint       prop_id,
+			      GValue     *value,
+			      GParamSpec *pspec)
+{
+	GthMenuButton *self = GTH_MENU_BUTTON (object);
+
+	switch (prop_id) {
+	case PROP_LABEL:
+		g_value_set_string (value, gth_menu_button_get_label (self));
+		break;
+	case PROP_USE_UNDERLINE:
+		g_value_set_boolean (value, gth_menu_button_get_use_underline (self));
+		break;
+	case PROP_STOCK_ID:
+		g_value_set_string (value, gth_menu_button_get_stock_id (self));
+		break;
+	case PROP_ICON_NAME:
+		g_value_set_string (value, gth_menu_button_get_icon_name (self));
+		break;
+	case PROP_MENU:
+		g_value_set_object (value, self->priv->menu);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+
+/* Callback for the "deactivate" signal on the pop-up menu.
+ * This is used so that we unset the state of the toggle button
+ * when the pop-up menu disappears.
+ */
+static int
+menu_deactivate_cb (GtkMenuShell  *menu_shell,
+		    GthMenuButton *self)
+{
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self), FALSE);
+	return TRUE;
+}
+
+
+static void
+menu_position_func (GtkMenu       *menu,
+                    int           *x,
+                    int           *y,
+                    gboolean      *push_in,
+                    GthMenuButton *self)
+{
+	GtkWidget        *widget = GTK_WIDGET (self);
+	GtkRequisition    menu_req;
+	GtkTextDirection  direction;
+	GdkRectangle      monitor;
+	int               monitor_num;
+	GdkScreen        *screen;
+	GtkAllocation     allocation;
+
+	gtk_widget_size_request (GTK_WIDGET (self->priv->menu), &menu_req);
+
+	direction = gtk_widget_get_direction (widget);
+
+	screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+	monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
+	if (monitor_num < 0)
+		monitor_num = 0;
+	gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+	gtk_widget_get_allocation (widget, &allocation);
+
+	gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
+	*x += allocation.x;
+	*y += allocation.y;
+
+	if (direction == GTK_TEXT_DIR_LTR)
+		*x += MAX (allocation.width - menu_req.width, 0);
+	else if (menu_req.width > allocation.width)
+		*x -= menu_req.width - allocation.width;
+
+	if ((*y + allocation.height + menu_req.height) <= monitor.y + monitor.height)
+		*y += allocation.height;
+	else if ((*y - menu_req.height) >= monitor.y)
+		*y -= menu_req.height;
+	else if (monitor.y + monitor.height - (*y + allocation.height) > *y)
+		*y += allocation.height;
+	else
+		*y -= menu_req.height;
+
+	*push_in = FALSE;
+}
+
+
+static void
+popup_menu_under_button (GthMenuButton  *self,
+                         GdkEventButton *event)
+{
+	g_signal_emit (self, signals[SHOW_MENU], 0);
+
+	if (self->priv->menu == NULL)
+		return;
+
+	if (gtk_menu_get_attach_widget (self->priv->menu) != NULL)
+		gtk_menu_detach (self->priv->menu);
+	gtk_menu_popup (self->priv->menu, NULL, NULL,
+			(GtkMenuPositionFunc) menu_position_func,
+			self,
+			event ? event->button : 0,
+			event ? event->time : gtk_get_current_event_time ());
+}
+
+
+static gboolean
+toggle_button_toggled_cb (GtkToggleButton *togglebutton,
+			  gpointer         user_data)
+{
+	GthMenuButton *self = user_data;
+	gboolean       toggle_active = gtk_toggle_button_get_active (togglebutton);
+
+	if (self->priv->menu == NULL)
+		return FALSE;
+
+	if (self->priv->active != toggle_active) {
+		self->priv->active = toggle_active;
+		g_object_notify (G_OBJECT (self), "active");
+
+		if (self->priv->active && ! gtk_widget_get_visible (GTK_WIDGET (self->priv->menu))) {
+			/* we get here only when the menu is activated by a key
+			 * press, so that we can select the first menu item */
+			popup_menu_under_button (self, NULL);
+			gtk_menu_shell_select_first (GTK_MENU_SHELL (self->priv->menu), FALSE);
+		}
+	}
+
+	return FALSE;
+}
+
+
+static gboolean
+toggle_button_press_event_cb (GtkWidget      *widget,
+                              GdkEventButton *event,
+                              gpointer        user_data)
+{
+	GthMenuButton *self = user_data;
+
+	if ((event->button == 1) && (self->priv->menu != NULL))  {
+		popup_menu_under_button (self, event);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+
+		return TRUE;
+	}
+	else
+		return FALSE;
+}
+
+
+static void
+gth_menu_button_class_init (GthMenuButtonClass *klass)
+{
+	GObjectClass   *object_class;
+	GtkWidgetClass *widget_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GthMenuButtonPrivate));
+
+	object_class = (GObjectClass *) klass;
+	object_class->set_property = gth_menu_button_set_property;
+	object_class->get_property = gth_menu_button_get_property;
+
+	widget_class = (GtkWidgetClass *) klass;
+	widget_class->state_changed = gth_menu_button_state_changed;
+
+	/* signals */
+
+	/**
+	 * GthMenuButton::show-menu:
+	 * @button: the object on which the signal is emitted
+	 *
+	 * The ::show-menu signal is emitted before the menu is shown.
+	 *
+	 * It can be used to populate the menu on demand, using
+	 * gth_menu_button_get_menu().
+	 *
+	 * Note that even if you populate the menu dynamically in this way,
+	 * you must set an empty menu on the #GthMenuButton beforehand,
+	 * since the arrow is made insensitive if the menu is not set.
+	 */
+	signals[SHOW_MENU] =
+		g_signal_new ("show-menu",
+			      G_OBJECT_CLASS_TYPE (klass),
+			      G_SIGNAL_RUN_FIRST,
+			      G_STRUCT_OFFSET (GthMenuButtonClass, show_menu),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);
+
+	/* properties */
+
+	g_object_class_install_property (object_class,
+					 PROP_LABEL,
+					 g_param_spec_string ("label",
+							      "Label",
+							      "Text to show in the item.",
+							      NULL,
+							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+	g_object_class_install_property (object_class,
+					 PROP_USE_UNDERLINE,
+					 g_param_spec_boolean ("use-underline",
+							       "Use underline",
+							       "If set, an underline in the label property indicates that the next character should be used for the mnemonic accelerator key in the overflow menu",
+							       FALSE,
+							       G_PARAM_READABLE | G_PARAM_WRITABLE));
+	g_object_class_install_property (object_class,
+					 PROP_STOCK_ID,
+					 g_param_spec_string ("stock-id",
+							      "Stock Id",
+							      "The stock icon displayed on the item",
+							      NULL,
+							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+	g_object_class_install_property (object_class,
+					 PROP_ICON_NAME,
+					 g_param_spec_string ("icon-name",
+							      "Icon name",
+							      "The name of the themed icon displayed on the item",
+							      NULL,
+							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+	g_object_class_install_property (object_class,
+					 PROP_MENU,
+					 g_param_spec_object ("menu",
+							      "Menu",
+							      "The dropdown menu",
+							      GTK_TYPE_MENU,
+							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+}
+
+
+static void
+gth_menu_button_init (GthMenuButton *self)
+{
+	guint      image_spacing;
+	GtkWidget *arrow;
+	GtkWidget *arrow_align;
+	GtkWidget *main_box;
+	GtkWidget *box;
+
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_MENU_BUTTON, GthMenuButtonPrivate);
+	self->priv->menu = NULL;
+	self->priv->active = FALSE;
+
+	gtk_widget_style_get (GTK_WIDGET (self),
+			      "image-spacing", &image_spacing,
+			      NULL);
+
+	/* icon and label */
+
+	self->priv->icon_widget = gtk_image_new ();
+	gtk_widget_show (self->priv->icon_widget);
+
+	self->priv->label_widget = gtk_label_new (NULL);
+	gtk_widget_show (self->priv->label_widget);
+
+	/* arrow */
+
+	arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+	gtk_widget_show (arrow);
+
+	arrow_align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (arrow_align), 0, 0, image_spacing, 0);
+	gtk_widget_show (arrow_align);
+	gtk_container_add (GTK_CONTAINER (arrow_align), arrow);
+
+	/* box */
+
+	main_box = gtk_hbox_new (FALSE, image_spacing);
+	gtk_widget_show (main_box);
+	gtk_container_add (GTK_CONTAINER (self), main_box);
+
+	box = gtk_hbox_new (FALSE, image_spacing);
+	gtk_widget_show (box);
+	gtk_box_pack_start (GTK_BOX (box), self->priv->icon_widget, FALSE, TRUE, 0);
+	gtk_box_pack_end (GTK_BOX (box), self->priv->label_widget, TRUE, TRUE, 0);
+
+	gtk_box_pack_start (GTK_BOX (main_box), box, TRUE, TRUE, 0);
+	gtk_box_pack_end (GTK_BOX (main_box), arrow_align, FALSE, FALSE, 0);
+
+	/* signals */
+
+	g_signal_connect (self,
+			  "toggled",
+			  G_CALLBACK (toggle_button_toggled_cb),
+			  self);
+	g_signal_connect (self,
+			  "button-press-event",
+		          G_CALLBACK (toggle_button_press_event_cb),
+		          self);
+}
+
+
+GType
+gth_menu_button_get_type (void)
+{
+        static GType type = 0;
+
+        if (! type) {
+                GTypeInfo type_info = {
+			sizeof (GthMenuButtonClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_menu_button_class_init,
+			NULL,
+			NULL,
+			sizeof (GthMenuButton),
+			0,
+			(GInstanceInitFunc) gth_menu_button_init
+		};
+		type = g_type_register_static (GTK_TYPE_TOGGLE_BUTTON,
+					       "GthMenuButton",
+					       &type_info,
+					       0);
+	}
+
+        return type;
+}
+
+
+GtkWidget *
+gth_menu_button_new (void)
+{
+	return (GtkWidget *) g_object_new (GTH_TYPE_MENU_BUTTON, NULL);
+}
+
+
+GtkWidget *
+gth_menu_button_new_from_stock (const char *stock_id)
+{
+	g_return_val_if_fail (stock_id != NULL, NULL);
+
+	return (GtkWidget *) g_object_new (GTH_TYPE_MENU_BUTTON,
+					   "stock-id", stock_id,
+					   NULL);
+}
+
+
+void
+gth_menu_button_set_label (GthMenuButton *self,
+			   const char    *label)
+{
+	g_return_if_fail (GTH_IS_MENU_BUTTON (self));
+
+	gtk_label_set_label (GTK_LABEL (self->priv->label_widget), label);
+	g_object_notify (G_OBJECT (self), "label");
+}
+
+
+G_CONST_RETURN char *
+gth_menu_button_get_label (GthMenuButton *self)
+{
+	g_return_val_if_fail (GTH_IS_MENU_BUTTON (self), NULL);
+
+	return gtk_label_get_label (GTK_LABEL (self->priv->label_widget));
+}
+
+
+void
+gth_menu_button_set_use_underline (GthMenuButton *self,
+				   gboolean       use_underline)
+{
+	g_return_if_fail (GTH_IS_MENU_BUTTON (self));
+
+	gtk_label_set_use_underline (GTK_LABEL (self->priv->label_widget), use_underline);
+	g_object_notify (G_OBJECT (self), "use-underline");
+}
+
+
+gboolean
+gth_menu_button_get_use_underline (GthMenuButton *self)
+{
+	g_return_val_if_fail (GTH_IS_MENU_BUTTON (self), FALSE);
+
+	return gtk_label_get_use_underline (GTK_LABEL (self->priv->label_widget));
+}
+
+
+void
+gth_menu_button_set_stock_id (GthMenuButton *self,
+			      const char    *stock_id)
+{
+	GtkStockItem  stock_item;
+	char         *label_text;
+
+	g_return_if_fail (GTH_IS_MENU_BUTTON (self));
+
+	gtk_image_set_from_stock (GTK_IMAGE (self->priv->icon_widget), stock_id, GTK_ICON_SIZE_BUTTON);
+
+	if ((stock_id != NULL) && gtk_stock_lookup (stock_id, &stock_item))
+		label_text = stock_item.label;
+	else
+		label_text = "";
+	gtk_label_set_text (GTK_LABEL (self->priv->label_widget), label_text);
+	gth_menu_button_set_use_underline (self, TRUE);
+
+	g_object_notify (G_OBJECT (self), "stock-id");
+}
+
+
+G_CONST_RETURN char *
+gth_menu_button_get_stock_id (GthMenuButton *self)
+{
+	char *stock_id;
+
+	g_return_val_if_fail (GTH_IS_MENU_BUTTON (self), NULL);
+
+	gtk_image_get_stock (GTK_IMAGE (self->priv->icon_widget),
+			     &stock_id,
+			     NULL);
+
+	return stock_id;
+}
+
+
+void
+gth_menu_button_set_icon_name (GthMenuButton *self,
+			      const char     *icon_name)
+{
+	g_return_if_fail (GTH_IS_MENU_BUTTON (self));
+
+	gtk_image_set_from_icon_name (GTK_IMAGE (self->priv->icon_widget), icon_name, GTK_ICON_SIZE_BUTTON);
+	g_object_notify (G_OBJECT (self), "icon-name");
+}
+
+
+G_CONST_RETURN char *
+gth_menu_button_get_icon_name (GthMenuButton *self)
+{
+	const char *icon_name;
+
+	g_return_val_if_fail (GTH_IS_MENU_BUTTON (self), NULL);
+
+	gtk_image_get_icon_name (GTK_IMAGE (self->priv->icon_widget),
+				 &icon_name,
+				 NULL);
+
+	return icon_name;
+}
+
+
+void
+gth_menu_button_set_menu (GthMenuButton *self,
+		          GtkWidget     *menu)
+{
+	g_return_if_fail (GTH_IS_MENU_BUTTON (self));
+	g_return_if_fail (GTK_IS_MENU (menu) || menu == NULL);
+
+	if (self->priv->menu != GTK_MENU (menu)) {
+		if ((self->priv->menu != NULL) && gtk_widget_get_visible (GTK_WIDGET (self->priv->menu)))
+			gtk_menu_shell_deactivate (GTK_MENU_SHELL (self->priv->menu));
+
+		self->priv->menu = GTK_MENU (menu);
+
+		if (self->priv->menu != NULL) {
+			g_object_add_weak_pointer (G_OBJECT (self->priv->menu), (gpointer *) &self->priv->menu);
+
+			gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
+			g_signal_connect (self->priv->menu,
+					  "deactivate",
+					  G_CALLBACK (menu_deactivate_cb),
+					  self);
+		}
+		else
+			gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+	}
+
+	g_object_notify (G_OBJECT (self), "menu");
+}
+
+
+GtkWidget *
+gth_menu_button_get_menu (GthMenuButton *self)
+{
+	g_return_val_if_fail (GTH_IS_MENU_BUTTON (self), NULL);
+
+	return GTK_WIDGET (self->priv->menu);
+}
diff --git a/gthumb/gth-menu-button.h b/gthumb/gth-menu-button.h
new file mode 100644
index 0000000..132bc0f
--- /dev/null
+++ b/gthumb/gth-menu-button.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2010 Free Software Foundation, 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_MENU_BUTTON_H
+#define GTH_MENU_BUTTON_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_MENU_BUTTON            (gth_menu_button_get_type ())
+#define GTH_MENU_BUTTON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_MENU_BUTTON, GthMenuButton))
+#define GTH_MENU_BUTTON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_MENU_BUTTON, GthMenuButtonClass))
+#define GTH_IS_MENU_BUTTON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_MENU_BUTTON))
+#define GTH_IS_MENU_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_MENU_BUTTON))
+#define GTH_MENU_BUTTON_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_MENU_BUTTON, GthMenuButtonClass))
+
+typedef struct _GthMenuButton        GthMenuButton;
+typedef struct _GthMenuButtonClass   GthMenuButtonClass;
+typedef struct _GthMenuButtonPrivate GthMenuButtonPrivate;
+
+struct _GthMenuButton
+{
+	GtkToggleButton parent;
+	GthMenuButtonPrivate *priv;
+};
+
+struct _GthMenuButtonClass
+{
+	GtkToggleButtonClass parent_class;
+
+	/*< signals >*/
+
+	void (*show_menu) (GthMenuButton *button);
+};
+
+GType                   gth_menu_button_get_type          (void) G_GNUC_CONST;
+GtkWidget *             gth_menu_button_new               (void);
+GtkWidget *             gth_menu_button_new_from_stock    (const char    *stock_id);
+void                    gth_menu_button_set_label         (GthMenuButton *button,
+							   const char    *label);
+G_CONST_RETURN char *   gth_menu_button_get_label         (GthMenuButton *button);
+void                    gth_menu_button_set_use_underline (GthMenuButton *button,
+							   gboolean       use_underline);
+gboolean                gth_menu_button_get_use_underline (GthMenuButton *button);
+void                    gth_menu_button_set_stock_id      (GthMenuButton *button,
+							   const char    *stock_id);
+G_CONST_RETURN char *   gth_menu_button_get_stock_id      (GthMenuButton *button);
+void                    gth_menu_button_set_icon_name     (GthMenuButton *button,
+							   const char    *icon_name);
+G_CONST_RETURN char *   gth_menu_button_get_icon_name     (GthMenuButton *button);
+void                    gth_menu_button_set_menu          (GthMenuButton *button,
+							   GtkWidget     *menu);
+GtkWidget *             gth_menu_button_get_menu          (GthMenuButton *button);
+
+G_END_DECLS
+
+#endif /* GTH_MENU_BUTTON_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]