diff options
Diffstat (limited to 'gedit/gedit-message-area.c')
-rwxr-xr-x | gedit/gedit-message-area.c | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/gedit/gedit-message-area.c b/gedit/gedit-message-area.c new file mode 100755 index 00000000..09240515 --- /dev/null +++ b/gedit/gedit-message-area.c @@ -0,0 +1,626 @@ +/* + * gedit-message-area.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * + * 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. + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +/* TODO: Style properties */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include "gedit-message-area.h" + +#define GEDIT_MESSAGE_AREA_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ + GEDIT_TYPE_MESSAGE_AREA, \ + GeditMessageAreaPrivate)) + +struct _GeditMessageAreaPrivate +{ + GtkWidget *main_hbox; + + GtkWidget *contents; + GtkWidget *action_area; + + gboolean changing_style; +}; + +typedef struct _ResponseData ResponseData; + +struct _ResponseData +{ + gint response_id; +}; + +enum { + RESPONSE, + CLOSE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE(GeditMessageArea, gedit_message_area, GTK_TYPE_HBOX) + + +static void +gedit_message_area_finalize (GObject *object) +{ + /* + GeditMessageArea *message_area = GEDIT_MESSAGE_AREA (object); + */ + + G_OBJECT_CLASS (gedit_message_area_parent_class)->finalize (object); +} + +static ResponseData * +get_response_data (GtkWidget *widget, + gboolean create) +{ + ResponseData *ad = g_object_get_data (G_OBJECT (widget), + "gedit-message-area-response-data"); + + if (ad == NULL && create) + { + ad = g_new (ResponseData, 1); + + g_object_set_data_full (G_OBJECT (widget), + "gedit-message-area-response-data", + ad, + g_free); + } + + return ad; +} + +static GtkWidget * +find_button (GeditMessageArea *message_area, + gint response_id) +{ + GList *children, *tmp_list; + GtkWidget *child = NULL; + + children = gtk_container_get_children ( + GTK_CONTAINER (message_area->priv->action_area)); + + for (tmp_list = children; tmp_list; tmp_list = tmp_list->next) + { + ResponseData *rd = get_response_data (tmp_list->data, FALSE); + + if (rd && rd->response_id == response_id) + { + child = tmp_list->data; + break; + } + } + + g_list_free (children); + + return child; +} + +static void +gedit_message_area_close (GeditMessageArea *message_area) +{ + if (!find_button (message_area, GTK_RESPONSE_CANCEL)) + return; + + /* emit response signal */ + gedit_message_area_response (GEDIT_MESSAGE_AREA (message_area), + GTK_RESPONSE_CANCEL); +} + +static gboolean +paint_message_area (GtkWidget *widget, + GdkEventExpose *event, + gpointer user_data) +{ + gtk_paint_flat_box (widget->style, + widget->window, + GTK_STATE_NORMAL, + GTK_SHADOW_OUT, + NULL, + widget, + "tooltip", + widget->allocation.x + 1, + widget->allocation.y + 1, + widget->allocation.width - 2, + widget->allocation.height - 2); + + return FALSE; +} + +static void +gedit_message_area_class_init (GeditMessageAreaClass *klass) +{ + GObjectClass *object_class; + GtkBindingSet *binding_set; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = gedit_message_area_finalize; + + klass->close = gedit_message_area_close; + + g_type_class_add_private (object_class, sizeof(GeditMessageAreaPrivate)); + + signals[RESPONSE] = g_signal_new ("response", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GeditMessageAreaClass, response), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + signals[CLOSE] = g_signal_new ("close", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GeditMessageAreaClass, close), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + binding_set = gtk_binding_set_by_class (klass); + + gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, "close", 0); +} + +static void +style_set (GtkWidget *widget, + GtkStyle *prev_style, + GeditMessageArea *message_area) +{ + GtkWidget *window; + GtkStyle *style; + + if (message_area->priv->changing_style) + return; + + /* This is a hack needed to use the tooltip background color */ + window = gtk_window_new (GTK_WINDOW_POPUP); + gtk_widget_set_name (window, "gtk-tooltip"); + gtk_widget_ensure_style (window); + style = gtk_widget_get_style (window); + + message_area->priv->changing_style = TRUE; + gtk_widget_set_style (GTK_WIDGET (message_area), style); + message_area->priv->changing_style = FALSE; + + gtk_widget_destroy (window); + + gtk_widget_queue_draw (GTK_WIDGET (message_area)); +} + +static void +gedit_message_area_init (GeditMessageArea *message_area) +{ + message_area->priv = GEDIT_MESSAGE_AREA_GET_PRIVATE (message_area); + + message_area->priv->main_hbox = gtk_hbox_new (FALSE, 16); /* FIXME: use style properties */ + gtk_widget_show (message_area->priv->main_hbox); + gtk_container_set_border_width (GTK_CONTAINER (message_area->priv->main_hbox), + 8); /* FIXME: use style properties */ + + message_area->priv->action_area = gtk_vbox_new (TRUE, 10); /* FIXME: use style properties */ + gtk_widget_show (message_area->priv->action_area); + gtk_box_pack_end (GTK_BOX (message_area->priv->main_hbox), + message_area->priv->action_area, + FALSE, + TRUE, + 0); + + gtk_box_pack_start (GTK_BOX (message_area), + message_area->priv->main_hbox, + TRUE, + TRUE, + 0); + + gtk_widget_set_app_paintable (GTK_WIDGET (message_area), TRUE); + + g_signal_connect (message_area, + "expose-event", + G_CALLBACK (paint_message_area), + NULL); + + /* Note that we connect to style-set on one of the internal + * widgets, not on the message area itself, since gtk does + * not deliver any further style-set signals for a widget on + * which the style has been forced with gtk_widget_set_style() */ + g_signal_connect (message_area->priv->main_hbox, + "style-set", + G_CALLBACK (style_set), + message_area); +} + +static gint +get_response_for_widget (GeditMessageArea *message_area, + GtkWidget *widget) +{ + ResponseData *rd; + + rd = get_response_data (widget, FALSE); + if (!rd) + return GTK_RESPONSE_NONE; + else + return rd->response_id; +} + +static void +action_widget_activated (GtkWidget *widget, GeditMessageArea *message_area) +{ + gint response_id; + + response_id = get_response_for_widget (message_area, widget); + + gedit_message_area_response (message_area, response_id); +} + +void +gedit_message_area_add_action_widget (GeditMessageArea *message_area, + GtkWidget *child, + gint response_id) +{ + ResponseData *ad; + guint signal_id; + + g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + ad = get_response_data (child, TRUE); + + ad->response_id = response_id; + + if (GTK_IS_BUTTON (child)) + signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON); + else + signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal; + + if (signal_id) + { + GClosure *closure; + + closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated), + G_OBJECT (message_area)); + + g_signal_connect_closure_by_id (child, + signal_id, + 0, + closure, + FALSE); + } + else + g_warning ("Only 'activatable' widgets can be packed into the action area of a GeditMessageArea"); + + if (response_id != GTK_RESPONSE_HELP) + gtk_box_pack_start (GTK_BOX (message_area->priv->action_area), + child, + FALSE, + FALSE, + 0); + else + gtk_box_pack_end (GTK_BOX (message_area->priv->action_area), + child, + FALSE, + FALSE, + 0); +} + +/** + * gedit_message_area_set_contents: + * @message_area: a #GeditMessageArea + * @contents: widget you want to add to the contents area + * + * Adds the @contents widget to the contents area of #GeditMessageArea. + */ +void +gedit_message_area_set_contents (GeditMessageArea *message_area, + GtkWidget *contents) +{ + g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + g_return_if_fail (GTK_IS_WIDGET (contents)); + + message_area->priv->contents = contents; + gtk_box_pack_start (GTK_BOX (message_area->priv->main_hbox), + message_area->priv->contents, + TRUE, + TRUE, + 0); +} + +/** + * gedit_message_area_add_button: + * @message_area: a #GeditMessageArea + * @button_text: text of button, or stock ID + * @response_id: response ID for the button + * + * Adds a button with the given text (or a stock button, if button_text is a stock ID) + * and sets things up so that clicking the button will emit the "response" signal + * with the given response_id. The button is appended to the end of the message area's + * action area. The button widget is returned, but usually you don't need it. + * + * Returns: the button widget that was added + */ +GtkWidget* +gedit_message_area_add_button (GeditMessageArea *message_area, + const gchar *button_text, + gint response_id) +{ + GtkWidget *button; + + g_return_val_if_fail (GEDIT_IS_MESSAGE_AREA (message_area), NULL); + g_return_val_if_fail (button_text != NULL, NULL); + + button = gtk_button_new_from_stock (button_text); + + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + + gtk_widget_show (button); + + gedit_message_area_add_action_widget (message_area, + button, + response_id); + + return button; +} + +static void +add_buttons_valist (GeditMessageArea *message_area, + const gchar *first_button_text, + va_list args) +{ + const gchar* text; + gint response_id; + + g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + + if (first_button_text == NULL) + return; + + text = first_button_text; + response_id = va_arg (args, gint); + + while (text != NULL) + { + gedit_message_area_add_button (message_area, + text, + response_id); + + text = va_arg (args, gchar*); + if (text == NULL) + break; + + response_id = va_arg (args, int); + } +} + +/** + * gedit_message_area_add_buttons: + * @message_area: a #GeditMessageArea + * @first_button_text: button text or stock ID + * @...: response ID for first button, then more text-response_id pairs + * + * Adds more buttons, same as calling gedit_message_area_add_button() repeatedly. + * The variable argument list should be NULL-terminated as with + * gedit_message_area_new_with_buttons(). Each button must have both text and response ID. + */ +void +gedit_message_area_add_buttons (GeditMessageArea *message_area, + const gchar *first_button_text, + ...) +{ + va_list args; + + va_start (args, first_button_text); + + add_buttons_valist (message_area, + first_button_text, + args); + + va_end (args); +} + +/** + * gedit_message_area_new: + * + * Creates a new #GeditMessageArea object. + * + * Returns: a new #GeditMessageArea object + */ +GtkWidget * +gedit_message_area_new (void) +{ + return g_object_new (GEDIT_TYPE_MESSAGE_AREA, NULL); +} + +/** + * gedit_message_area_new_with_buttons: + * @first_button_text: stock ID or text to go in first button, or NULL + * @...: response ID for first button, then additional buttons, ending with NULL + * + * Creates a new #GeditMessageArea with buttons. Button text/response ID pairs + * should be listed, with a NULL pointer ending the list. Button text can be either + * a stock ID such as GTK_STOCK_OK, or some arbitrary text. A response ID can be any + * positive number, or one of the values in the GtkResponseType enumeration. If + * the user clicks one of these dialog buttons, GeditMessageArea will emit the "response" + * signal with the corresponding response ID. + * + * Returns: a new #GeditMessageArea + */ +GtkWidget * +gedit_message_area_new_with_buttons (const gchar *first_button_text, + ...) +{ + GeditMessageArea *message_area; + va_list args; + + message_area = GEDIT_MESSAGE_AREA (gedit_message_area_new ()); + + va_start (args, first_button_text); + + add_buttons_valist (message_area, + first_button_text, + args); + + va_end (args); + + return GTK_WIDGET (message_area); +} + +/** + * gedit_message_area_set_response_sensitive: + * @message_area: a #GeditMessageArea + * @response_id: a response ID + * @setting: TRUE for sensitive + * + * Calls gtk_widget_set_sensitive (widget, setting) for each widget in the dialog's + * action area with the given response_id. A convenient way to sensitize/desensitize + * dialog buttons. + */ +void +gedit_message_area_set_response_sensitive (GeditMessageArea *message_area, + gint response_id, + gboolean setting) +{ + GList *children; + GList *tmp_list; + + g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + + children = gtk_container_get_children (GTK_CONTAINER (message_area->priv->action_area)); + + tmp_list = children; + while (tmp_list != NULL) + { + GtkWidget *widget = tmp_list->data; + ResponseData *rd = get_response_data (widget, FALSE); + + if (rd && rd->response_id == response_id) + gtk_widget_set_sensitive (widget, setting); + + tmp_list = g_list_next (tmp_list); + } + + g_list_free (children); +} + +/** + * gedit_message_area_set_default_response: + * @message_area: a #GeditMessageArea + * @response_id: a response ID + * + * Sets the last widget in the message area's action area with the given response_id + * as the default widget for the dialog. Pressing "Enter" normally activates the + * default widget. + */ +void +gedit_message_area_set_default_response (GeditMessageArea *message_area, + gint response_id) +{ + GList *children; + GList *tmp_list; + + g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + + children = gtk_container_get_children (GTK_CONTAINER (message_area->priv->action_area)); + + tmp_list = children; + while (tmp_list != NULL) + { + GtkWidget *widget = tmp_list->data; + ResponseData *rd = get_response_data (widget, FALSE); + + if (rd && rd->response_id == response_id) + gtk_widget_grab_default (widget); + + tmp_list = g_list_next (tmp_list); + } + + g_list_free (children); +} + +/** + * gedit_message_area_set_default_response: + * @message_area: a #GeditMessageArea + * @response_id: a response ID + * + * Emits the 'response' signal with the given @response_id. + */ +void +gedit_message_area_response (GeditMessageArea *message_area, + gint response_id) +{ + g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + + g_signal_emit (message_area, + signals[RESPONSE], + 0, + response_id); +} + +/** + * gedit_message_area_add_stock_button_with_text: + * @message_area: a #GeditMessageArea + * @text: the text to visualize in the button + * @stock_id: the stock ID of the button + * @response_id: a response ID + * + * Same as gedit_message_area_add_button() but with a specific text. + */ +GtkWidget * +gedit_message_area_add_stock_button_with_text (GeditMessageArea *message_area, + const gchar *text, + const gchar *stock_id, + gint response_id) +{ + GtkWidget *button; + + g_return_val_if_fail (GEDIT_IS_MESSAGE_AREA (message_area), NULL); + g_return_val_if_fail (text != NULL, NULL); + g_return_val_if_fail (stock_id != NULL, NULL); + + button = gtk_button_new_with_mnemonic (text); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_stock (stock_id, + GTK_ICON_SIZE_BUTTON)); + + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + + gtk_widget_show (button); + + gedit_message_area_add_action_widget (message_area, + button, + response_id); + + return button; +} + |