627 lines
16 KiB
C
627 lines
16 KiB
C
/*
|
|
* pluma-message-area.c
|
|
* This file is part of pluma
|
|
*
|
|
* 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 pluma Team, 2005. See the AUTHORS file for a
|
|
* list of people on the pluma 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 "pluma-message-area.h"
|
|
|
|
#define PLUMA_MESSAGE_AREA_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \
|
|
PLUMA_TYPE_MESSAGE_AREA, \
|
|
PlumaMessageAreaPrivate))
|
|
|
|
struct _PlumaMessageAreaPrivate
|
|
{
|
|
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(PlumaMessageArea, pluma_message_area, GTK_TYPE_HBOX)
|
|
|
|
|
|
static void
|
|
pluma_message_area_finalize (GObject *object)
|
|
{
|
|
/*
|
|
PlumaMessageArea *message_area = PLUMA_MESSAGE_AREA (object);
|
|
*/
|
|
|
|
G_OBJECT_CLASS (pluma_message_area_parent_class)->finalize (object);
|
|
}
|
|
|
|
static ResponseData *
|
|
get_response_data (GtkWidget *widget,
|
|
gboolean create)
|
|
{
|
|
ResponseData *ad = g_object_get_data (G_OBJECT (widget),
|
|
"pluma-message-area-response-data");
|
|
|
|
if (ad == NULL && create)
|
|
{
|
|
ad = g_new (ResponseData, 1);
|
|
|
|
g_object_set_data_full (G_OBJECT (widget),
|
|
"pluma-message-area-response-data",
|
|
ad,
|
|
g_free);
|
|
}
|
|
|
|
return ad;
|
|
}
|
|
|
|
static GtkWidget *
|
|
find_button (PlumaMessageArea *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
|
|
pluma_message_area_close (PlumaMessageArea *message_area)
|
|
{
|
|
if (!find_button (message_area, GTK_RESPONSE_CANCEL))
|
|
return;
|
|
|
|
/* emit response signal */
|
|
pluma_message_area_response (PLUMA_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
|
|
pluma_message_area_class_init (PlumaMessageAreaClass *klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
GtkBindingSet *binding_set;
|
|
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
object_class->finalize = pluma_message_area_finalize;
|
|
|
|
klass->close = pluma_message_area_close;
|
|
|
|
g_type_class_add_private (object_class, sizeof(PlumaMessageAreaPrivate));
|
|
|
|
signals[RESPONSE] = g_signal_new ("response",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (PlumaMessageAreaClass, 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 (PlumaMessageAreaClass, 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,
|
|
PlumaMessageArea *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
|
|
pluma_message_area_init (PlumaMessageArea *message_area)
|
|
{
|
|
message_area->priv = PLUMA_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 (PlumaMessageArea *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, PlumaMessageArea *message_area)
|
|
{
|
|
gint response_id;
|
|
|
|
response_id = get_response_for_widget (message_area, widget);
|
|
|
|
pluma_message_area_response (message_area, response_id);
|
|
}
|
|
|
|
void
|
|
pluma_message_area_add_action_widget (PlumaMessageArea *message_area,
|
|
GtkWidget *child,
|
|
gint response_id)
|
|
{
|
|
ResponseData *ad;
|
|
guint signal_id;
|
|
|
|
g_return_if_fail (PLUMA_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 PlumaMessageArea");
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* pluma_message_area_set_contents:
|
|
* @message_area: a #PlumaMessageArea
|
|
* @contents: widget you want to add to the contents area
|
|
*
|
|
* Adds the @contents widget to the contents area of #PlumaMessageArea.
|
|
*/
|
|
void
|
|
pluma_message_area_set_contents (PlumaMessageArea *message_area,
|
|
GtkWidget *contents)
|
|
{
|
|
g_return_if_fail (PLUMA_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);
|
|
}
|
|
|
|
/**
|
|
* pluma_message_area_add_button:
|
|
* @message_area: a #PlumaMessageArea
|
|
* @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*
|
|
pluma_message_area_add_button (PlumaMessageArea *message_area,
|
|
const gchar *button_text,
|
|
gint response_id)
|
|
{
|
|
GtkWidget *button;
|
|
|
|
g_return_val_if_fail (PLUMA_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);
|
|
|
|
pluma_message_area_add_action_widget (message_area,
|
|
button,
|
|
response_id);
|
|
|
|
return button;
|
|
}
|
|
|
|
static void
|
|
add_buttons_valist (PlumaMessageArea *message_area,
|
|
const gchar *first_button_text,
|
|
va_list args)
|
|
{
|
|
const gchar* text;
|
|
gint response_id;
|
|
|
|
g_return_if_fail (PLUMA_IS_MESSAGE_AREA (message_area));
|
|
|
|
if (first_button_text == NULL)
|
|
return;
|
|
|
|
text = first_button_text;
|
|
response_id = va_arg (args, gint);
|
|
|
|
while (text != NULL)
|
|
{
|
|
pluma_message_area_add_button (message_area,
|
|
text,
|
|
response_id);
|
|
|
|
text = va_arg (args, gchar*);
|
|
if (text == NULL)
|
|
break;
|
|
|
|
response_id = va_arg (args, int);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* pluma_message_area_add_buttons:
|
|
* @message_area: a #PlumaMessageArea
|
|
* @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 pluma_message_area_add_button() repeatedly.
|
|
* The variable argument list should be NULL-terminated as with
|
|
* pluma_message_area_new_with_buttons(). Each button must have both text and response ID.
|
|
*/
|
|
void
|
|
pluma_message_area_add_buttons (PlumaMessageArea *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);
|
|
}
|
|
|
|
/**
|
|
* pluma_message_area_new:
|
|
*
|
|
* Creates a new #PlumaMessageArea object.
|
|
*
|
|
* Returns: a new #PlumaMessageArea object
|
|
*/
|
|
GtkWidget *
|
|
pluma_message_area_new (void)
|
|
{
|
|
return g_object_new (PLUMA_TYPE_MESSAGE_AREA, NULL);
|
|
}
|
|
|
|
/**
|
|
* pluma_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 #PlumaMessageArea 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, PlumaMessageArea will emit the "response"
|
|
* signal with the corresponding response ID.
|
|
*
|
|
* Returns: a new #PlumaMessageArea
|
|
*/
|
|
GtkWidget *
|
|
pluma_message_area_new_with_buttons (const gchar *first_button_text,
|
|
...)
|
|
{
|
|
PlumaMessageArea *message_area;
|
|
va_list args;
|
|
|
|
message_area = PLUMA_MESSAGE_AREA (pluma_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);
|
|
}
|
|
|
|
/**
|
|
* pluma_message_area_set_response_sensitive:
|
|
* @message_area: a #PlumaMessageArea
|
|
* @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
|
|
pluma_message_area_set_response_sensitive (PlumaMessageArea *message_area,
|
|
gint response_id,
|
|
gboolean setting)
|
|
{
|
|
GList *children;
|
|
GList *tmp_list;
|
|
|
|
g_return_if_fail (PLUMA_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);
|
|
}
|
|
|
|
/**
|
|
* pluma_message_area_set_default_response:
|
|
* @message_area: a #PlumaMessageArea
|
|
* @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
|
|
pluma_message_area_set_default_response (PlumaMessageArea *message_area,
|
|
gint response_id)
|
|
{
|
|
GList *children;
|
|
GList *tmp_list;
|
|
|
|
g_return_if_fail (PLUMA_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);
|
|
}
|
|
|
|
/**
|
|
* pluma_message_area_set_default_response:
|
|
* @message_area: a #PlumaMessageArea
|
|
* @response_id: a response ID
|
|
*
|
|
* Emits the 'response' signal with the given @response_id.
|
|
*/
|
|
void
|
|
pluma_message_area_response (PlumaMessageArea *message_area,
|
|
gint response_id)
|
|
{
|
|
g_return_if_fail (PLUMA_IS_MESSAGE_AREA (message_area));
|
|
|
|
g_signal_emit (message_area,
|
|
signals[RESPONSE],
|
|
0,
|
|
response_id);
|
|
}
|
|
|
|
/**
|
|
* pluma_message_area_add_stock_button_with_text:
|
|
* @message_area: a #PlumaMessageArea
|
|
* @text: the text to visualize in the button
|
|
* @stock_id: the stock ID of the button
|
|
* @response_id: a response ID
|
|
*
|
|
* Same as pluma_message_area_add_button() but with a specific text.
|
|
*/
|
|
GtkWidget *
|
|
pluma_message_area_add_stock_button_with_text (PlumaMessageArea *message_area,
|
|
const gchar *text,
|
|
const gchar *stock_id,
|
|
gint response_id)
|
|
{
|
|
GtkWidget *button;
|
|
|
|
g_return_val_if_fail (PLUMA_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);
|
|
|
|
pluma_message_area_add_action_widget (message_area,
|
|
button,
|
|
response_id);
|
|
|
|
return button;
|
|
}
|
|
|