glade/src/glade-utils.c
Paolo Borelli 40e01dab2c center the dialog. Patch by Michał Byrecki <byrek@elektronika.org>.
2004-06-16  Paolo Borelli  <pborelli@katamail.com>

        * src/gedit-utils.c (glade_util_file_chooser_new): center the
        dialog. Patch by Michał Byrecki <byrek@elektronika.org>.
2004-06-30 05:40:22 +00:00

787 lines
20 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2001 Ximian, 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:
* Chema Celorio <chema@celorio.com>
*/
#include <string.h>
#include <gtk/gtktooltips.h>
#include <gdk/gdkkeysyms.h>
#include <gmodule.h>
#include "glade.h"
#include "glade-project.h"
#include "glade-command.h"
#include "glade-debug.h"
#include "glade-placeholder.h"
#include "glade-widget.h"
#include "glade-widget-class.h"
#include "glade-property.h"
#include "glade-property-class.h"
#define GLADE_UTIL_HAS_NODES "glade_util_has_nodes"
#define GLADE_UTIL_SELECTION_NODE_SIZE 7
void
glade_util_widget_set_tooltip (GtkWidget *widget, const gchar *str)
{
GtkTooltips *tooltip;
tooltip = gtk_tooltips_new ();
gtk_tooltips_set_tip (tooltip, widget, str, NULL);
return;
}
GType
glade_util_get_type_from_name (const gchar *name)
{
static GModule *allsymbols;
guint (*get_type) ();
GType type;
if (!allsymbols)
allsymbols = g_module_open (NULL, 0);
if (!g_module_symbol (allsymbols, name,
(gpointer) &get_type)) {
g_warning (_("We could not find the symbol \"%s\""),
name);
return FALSE;
}
g_assert (get_type);
type = get_type ();
if (type == 0) {
g_warning(_("Could not get the type from \"%s"),
name);
return FALSE;
}
/* Disabled for GtkAdjustment, but i'd like to check for this somehow. Chema */
#if 0
if (!g_type_is_a (type, gtk_widget_get_type ())) {
g_warning (_("The loaded type is not a GtkWidget, while trying to load \"%s\""),
class->name);
return FALSE;
}
#endif
return type;
}
void
glade_util_ui_warn (GtkWidget *parent, const gchar *warning)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_OK,
warning);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
typedef struct {
GtkStatusbar *statusbar;
guint context_id;
guint message_id;
} FlashInfo;
static const guint32 flash_length = 3000;
static gboolean
remove_message_timeout (FlashInfo * fi)
{
gtk_statusbar_remove (fi->statusbar, fi->context_id, fi->message_id);
g_free (fi);
/* remove the timeout */
return FALSE;
}
/**
* glade_utils_flash_message:
* @statusbar: The statusbar
* @context_id: The message context_id
* @format: The message to flash on the statusbar
*
* Flash a temporary message on the statusbar.
*/
void
glade_util_flash_message (GtkWidget *statusbar, guint context_id, gchar *format, ...)
{
va_list args;
FlashInfo *fi;
gchar *message;
g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
g_return_if_fail (format != NULL);
va_start (args, format);
message = g_strdup_vprintf (format, args);
va_end (args);
fi = g_new (FlashInfo, 1);
fi->statusbar = GTK_STATUSBAR (statusbar);
fi->context_id = context_id;
fi->message_id = gtk_statusbar_push (fi->statusbar, fi->context_id, message);
gtk_timeout_add (flash_length, (GtkFunction) remove_message_timeout, fi);
g_free (message);
}
static gint
glade_util_compare_uline_labels (const gchar *labela, const gchar *labelb)
{
for (;;) {
gunichar c1, c2;
if (*labela == '\0')
return (*labelb == '\0') ? 0 : -1;
if (*labelb == '\0')
return 1;
c1 = g_utf8_get_char (labela);
if (c1 == '_') {
labela = g_utf8_next_char (labela);
c1 = g_utf8_get_char (labela);
}
c2 = g_utf8_get_char (labelb);
if (c2 == '_') {
labelb = g_utf8_next_char (labelb);
c2 = g_utf8_get_char (labelb);
}
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
labela = g_utf8_next_char (labela);
labelb = g_utf8_next_char (labelb);
}
/* Shouldn't be reached. */
return 0;
}
/* This is a GCompareFunc for comparing the labels of 2 stock items, ignoring
any '_' characters. It isn't particularly efficient. */
gint
glade_util_compare_stock_labels (gconstpointer a, gconstpointer b)
{
const gchar *stock_ida = a, *stock_idb = b;
GtkStockItem itema, itemb;
gboolean founda, foundb;
gint retval;
founda = gtk_stock_lookup (stock_ida, &itema);
foundb = gtk_stock_lookup (stock_idb, &itemb);
if (founda)
{
if (!foundb)
retval = -1;
else
/* FIXME: Not ideal for UTF-8. */
retval = glade_util_compare_uline_labels (itema.label, itemb.label);
}
else
{
if (!foundb)
retval = 0;
else
retval = 1;
}
return retval;
}
gchar *
glade_util_gtk_combo_func (gpointer data)
{
GtkListItem * listitem = data;
/* I needed to pinch this as well - Damon. */
static const gchar *gtk_combo_string_key = "gtk-combo-string-value";
GtkWidget *label;
gchar *ltext = NULL;
ltext = (gchar *) gtk_object_get_data (GTK_OBJECT (listitem),
gtk_combo_string_key);
if (!ltext) {
label = GTK_BIN (listitem)->child;
if (!label || !GTK_IS_LABEL (label))
return NULL;
ltext = (gchar*) gtk_label_get_text (GTK_LABEL (label));
}
return ltext;
}
/* These are pinched from gtkcombo.c */
gpointer /* GtkListItem * */
glade_util_gtk_combo_find (GtkCombo * combo)
{
gchar *text;
gchar *ltext;
GList *clist;
int (*string_compare) (const char *, const char *);
if (combo->case_sensitive)
string_compare = strcmp;
else
string_compare = g_strcasecmp;
text = (gchar*) gtk_entry_get_text (GTK_ENTRY (combo->entry));
clist = GTK_LIST (combo->list)->children;
while (clist && clist->data) {
ltext = glade_util_gtk_combo_func (GTK_LIST_ITEM (clist->data));
if (!ltext)
continue;
if (!(*string_compare) (ltext, text))
return (GtkListItem *) clist->data;
clist = clist->next;
}
return NULL;
}
/**
* glade_util_hide_window:
* @window:
*
* If you use this function to handle the delete_event of a window, when it
* will be shown again it will appear in the position where it was before
* beeing hidden.
**/
void
glade_util_hide_window (GtkWindow *window)
{
gint x, y;
g_return_if_fail (GTK_IS_WINDOW (window));
/* remember position of window for when it is used again */
gtk_window_get_position (window, &x, &y);
gtk_widget_hide (GTK_WIDGET (window));
gtk_window_move(window, x, y);
}
/**
* glade_util_file_chooser_new:
* @title: dialog title
* @parent: the window the dialog is set transient for
* @action: a #GtkFileChooserAction to say if the dialog will open or save
*
* Returns a file chooser dialog. It's up to the caller
* to show the dialog
**/
GtkWidget *
glade_util_file_chooser_new (const gchar *title, GtkWindow *parent,
GtkFileChooserAction action)
{
GtkWidget *file_chooser;
g_return_val_if_fail ((action == GTK_FILE_CHOOSER_ACTION_OPEN ||
action == GTK_FILE_CHOOSER_ACTION_SAVE), NULL);
file_chooser = gtk_file_chooser_dialog_new (title, parent, action,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
action == GTK_FILE_CHOOSER_ACTION_OPEN ? GTK_STOCK_OPEN : GTK_STOCK_SAVE,
GTK_RESPONSE_OK,
NULL);
gtk_window_set_position (GTK_WINDOW (file_chooser), GTK_WIN_POS_CENTER_ALWAYS);
return file_chooser;
}
/**
* changes each occurence of the character a on the string str by the character b.
*/
void
glade_util_replace (char *str, char a, char b)
{
g_return_if_fail (str != NULL);
while (*str != 0)
{
if (*str == a)
*str = b;
str = g_utf8_next_char (str);
}
}
/**
* duplicates the string passed as argument, but changing each underscore
* in the original string by two underscores. Returns a newly allocated
* string.
*/
char *
glade_util_duplicate_underscores (const char *name)
{
const char *tmp;
const char *last_tmp = name;
char *underscored_name = g_malloc (strlen (name) * 2 + 1);
char *tmp_underscored = underscored_name;
for (tmp = last_tmp; *tmp; tmp = g_utf8_next_char (tmp))
{
if (*tmp == '_')
{
memcpy (tmp_underscored, last_tmp, tmp - last_tmp + 1);
tmp_underscored += tmp - last_tmp + 1;
last_tmp = tmp + 1;
*tmp_underscored++ = '_';
}
}
memcpy (tmp_underscored, last_tmp, tmp - last_tmp + 1);
return underscored_name;
}
/* This returns the window that the given widget's position is relative to.
Usually this is the widget's parent's window. But if the widget is a
toplevel, we use its own window, as it doesn't have a parent.
Some widgets also lay out widgets in different ways. */
static GdkWindow*
glade_util_get_window_positioned_in (GtkWidget *widget)
{
GtkWidget *parent;
parent = widget->parent;
#ifdef USE_GNOME
/* BonoboDockItem widgets use a different window when floating.
FIXME: I've left this here so we remember to add it when we add
GNOME support. */
if (BONOBO_IS_DOCK_ITEM (widget)
&& BONOBO_DOCK_ITEM (widget)->is_floating) {
return BONOBO_DOCK_ITEM (widget)->float_window;
}
if (parent && BONOBO_IS_DOCK_ITEM (parent)
&& BONOBO_DOCK_ITEM (parent)->is_floating) {
return BONOBO_DOCK_ITEM (parent)->float_window;
}
#endif
if (parent)
return parent->window;
return widget->window;
}
static void
glade_util_draw_nodes (GdkWindow *window, GdkGC *gc,
gint x, gint y,
gint width, gint height)
{
if (width > GLADE_UTIL_SELECTION_NODE_SIZE && height > GLADE_UTIL_SELECTION_NODE_SIZE) {
gdk_draw_rectangle (window, gc, TRUE,
x, y,
GLADE_UTIL_SELECTION_NODE_SIZE, GLADE_UTIL_SELECTION_NODE_SIZE);
gdk_draw_rectangle (window, gc, TRUE,
x, y + height - GLADE_UTIL_SELECTION_NODE_SIZE,
GLADE_UTIL_SELECTION_NODE_SIZE, GLADE_UTIL_SELECTION_NODE_SIZE);
gdk_draw_rectangle (window, gc, TRUE,
x + width - GLADE_UTIL_SELECTION_NODE_SIZE, y,
GLADE_UTIL_SELECTION_NODE_SIZE, GLADE_UTIL_SELECTION_NODE_SIZE);
gdk_draw_rectangle (window, gc, TRUE,
x + width - GLADE_UTIL_SELECTION_NODE_SIZE,
y + height - GLADE_UTIL_SELECTION_NODE_SIZE,
GLADE_UTIL_SELECTION_NODE_SIZE, GLADE_UTIL_SELECTION_NODE_SIZE);
}
gdk_draw_rectangle (window, gc, FALSE, x, y, width - 1, height - 1);
}
/* This calculates the offset of the given window within its toplevel.
It also returns the toplevel. */
static void
glade_util_calculate_window_offset (GdkWindow *window,
gint *x, gint *y,
GdkWindow **toplevel)
{
gint tmp_x, tmp_y;
/* Calculate the offset of the window within its toplevel. */
*x = 0;
*y = 0;
for (;;) {
if (gdk_window_get_window_type (window) != GDK_WINDOW_CHILD)
break;
gdk_window_get_position (window, &tmp_x, &tmp_y);
*x += tmp_x;
*y += tmp_y;
window = gdk_window_get_parent (window);
}
*toplevel = window;
}
/* This returns TRUE if it is OK to draw the selection nodes for the given
selected widget inside the given window that has received an expose event.
For most widgets it returns TRUE, but if a selected widget is inside a
widget like a viewport, that uses its own coordinate system, then it only
returns TRUE if the expose window is inside the viewport as well. */
static gboolean
glade_util_can_draw_nodes (GtkWidget *sel_widget, GdkWindow *sel_win,
GdkWindow *expose_win)
{
GtkWidget *widget, *viewport = NULL;
GdkWindow *viewport_win = NULL;
/* Check if the selected widget is inside a viewport. */
for (widget = sel_widget->parent; widget; widget = widget->parent) {
if (GTK_IS_VIEWPORT (widget)) {
viewport = widget;
viewport_win = GTK_VIEWPORT (widget)->bin_window;
break;
}
}
/* If there is no viewport-type widget above the selected widget,
it is OK to draw the selection anywhere. */
if (!viewport)
return TRUE;
/* If we have a viewport-type widget, check if the expose_win is
beneath the viewport. If it is, we can draw in it. If not, we
can't.*/
for (;;) {
if (expose_win == sel_win)
return TRUE;
if (expose_win == viewport_win)
return FALSE;
if (gdk_window_get_window_type (expose_win) != GDK_WINDOW_CHILD)
break;
expose_win = gdk_window_get_parent (expose_win);
}
return FALSE;
}
/* This is called to redraw any selection nodes that intersect the given
exposed window. It steps through all the selected widgets, converts the
coordinates so they are relative to the exposed window, then calls
glade_util_draw_nodes() to draw the selection nodes if appropriate. */
gboolean
glade_util_draw_nodes_idle (GdkWindow *expose_win)
{
GladeWidget *expose_gwidget;
GtkWidget *expose_widget;
gint expose_win_x, expose_win_y;
gint expose_win_w, expose_win_h;
GdkWindow *expose_toplevel;
GdkGC *gc;
GList *elem;
/* Check that the window is still alive. */
if (!gdk_window_is_viewable (expose_win))
goto out;
/* Find the corresponding GtkWidget and GladeWidget. */
gdk_window_get_user_data (expose_win, (gpointer*) &expose_widget);
gc = expose_widget->style->black_gc;
expose_gwidget = glade_widget_get_from_gtk_widget (expose_widget);
if (!expose_gwidget)
expose_gwidget = glade_util_get_parent (expose_widget);
if (!expose_gwidget)
goto out;
/* Calculate the offset of the expose window within its toplevel. */
glade_util_calculate_window_offset (expose_win,
&expose_win_x,
&expose_win_y,
&expose_toplevel);
gdk_drawable_get_size (expose_win,
&expose_win_w, &expose_win_h);
/* Step through all the selected widgets in the project. */
for (elem = expose_gwidget->project->selection; elem; elem = elem->next) {
GtkWidget *sel_widget;
GdkWindow *sel_win, *sel_toplevel;
gint sel_x, sel_y, x, y, w, h;
sel_widget = elem->data;
sel_win = glade_util_get_window_positioned_in (sel_widget);
/* Calculate the offset of the selected widget's window
within its toplevel. */
glade_util_calculate_window_offset (sel_win, &sel_x, &sel_y,
&sel_toplevel);
/* We only draw the nodes if the window that got the expose
event is in the same toplevel as the selected widget. */
if (expose_toplevel == sel_toplevel
&& glade_util_can_draw_nodes (sel_widget, sel_win,
expose_win)) {
x = sel_x + sel_widget->allocation.x - expose_win_x;
y = sel_y + sel_widget->allocation.y - expose_win_y;
w = sel_widget->allocation.width;
h = sel_widget->allocation.height;
/* Draw the selection nodes if they intersect the
expose window bounds. */
if (x < expose_win_w && x + w >= 0
&& y < expose_win_h && y + h >= 0) {
glade_util_draw_nodes (expose_win, gc,
x, y, w, h);
}
}
}
out:
/* Remove the reference added in glade_util_queue_draw_nodes(). */
gdk_window_unref (expose_win);
/* Return FALSE so the idle handler isn't called again. */
return FALSE;
}
#define GLADE_DRAW_NODES_IDLE_PRIORITY GTK_PRIORITY_DEFAULT + 10
/* This should be called whenever a widget in the interface receives an
expose event. It sets up an idle function which will redraw any selection
nodes that intersect the exposed window. */
void
glade_util_queue_draw_nodes (GdkWindow *window)
{
g_return_if_fail (GDK_IS_WINDOW (window));
/* We need to ref the window, to make sure it isn't freed before
our idle function is called. We unref it there. */
gdk_window_ref (window);
g_idle_add_full (GLADE_DRAW_NODES_IDLE_PRIORITY,
(GSourceFunc) glade_util_draw_nodes_idle,
window, NULL);
}
void
glade_util_add_nodes (GtkWidget *widget)
{
g_object_set_data (G_OBJECT (widget), GLADE_UTIL_HAS_NODES,
GINT_TO_POINTER (1));
gtk_widget_queue_draw (widget);
}
void
glade_util_remove_nodes (GtkWidget *widget)
{
g_object_set_data (G_OBJECT (widget), GLADE_UTIL_HAS_NODES, 0);
/* We redraw the parent, since the selection rectangle may not be
cleared if we just redraw the widget itself. */
gtk_widget_queue_draw (widget->parent ? widget->parent : widget);
}
gboolean
glade_util_has_nodes (GtkWidget *widget)
{
return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), GLADE_UTIL_HAS_NODES)) != 0;
}
void
glade_util_delete_selection (GladeProject *project)
{
GList *selection;
GList *free_me;
GList *list;
g_return_if_fail (GLADE_IS_PROJECT (project));
selection = glade_project_selection_get (project);
if (!selection)
return;
/* We have to be careful when deleting widgets from the selection
* because when we delete each widget, the selection pointer changes
* due to the g_list_remove performed by glade_command_delete.
* Copy the list and free it after we are done
*/
free_me = g_list_copy (selection);
for (list = free_me; list; list = list->next) {
GladeWidget *glade_widget;
glade_widget = glade_widget_get_from_gtk_widget (list->data);
if (glade_widget)
glade_command_delete (glade_widget);
}
g_list_free (free_me);
}
GladeWidget *
glade_util_get_parent (GtkWidget *w)
{
GtkWidget *widget = w;
GladeWidget *parent = NULL;
do
{
widget = gtk_widget_get_parent (widget);
if (widget != NULL)
{
parent = glade_widget_get_from_gtk_widget (widget);
if (parent != NULL)
return parent;
}
}
while (widget != NULL);
return NULL;
}
/*
* taken from gtk... maybe someday we can convince them to
* expose gtk_container_get_all_children
*/
static void
gtk_container_children_callback (GtkWidget *widget,
gpointer client_data)
{
GList **children;
children = (GList**) client_data;
*children = g_list_prepend (*children, widget);
}
GList *
glade_util_container_get_all_children (GtkContainer *container)
{
GList *children = NULL;
g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
gtk_container_foreach (container,
gtk_container_children_callback,
&children);
return g_list_reverse (children);
}
/**
* glade_util_uri_list_parse:
* @uri_list: the text/urilist, must be NULL terminated.
*
* Extracts a list of file names from a standard text/uri-list,
* such as one you would get on a drop operation.
* This is mostly stolen from gnome-vfs-uri.c.
*
* Returns a GList of gchars.
**/
GList *
glade_util_uri_list_parse (const gchar *uri_list)
{
const gchar *p, *q;
GList *result = NULL;
g_return_val_if_fail (uri_list != NULL, NULL);
p = uri_list;
/* We don't actually try to validate the URI according to RFC
* 2396, or even check for allowed characters - we just ignore
* comments and trim whitespace off the ends. We also
* allow LF delimination as well as the specified CRLF.
*/
while (p)
{
if (*p != '#')
{
gchar *retval;
gchar *path;
while (g_ascii_isspace (*p))
p++;
q = p;
while ((*q != '\0') && (*q != '\n') && (*q != '\r'))
q++;
if (q > p)
{
q--;
while (q > p && g_ascii_isspace (*q))
q--;
retval = g_new (gchar, q - p + 2);
memcpy (retval, p, q - p + 1);
retval[q - p + 1] = '\0';
path = g_filename_from_uri (retval, NULL, NULL);
if (!path)
{
g_free (retval);
continue;
}
result = g_list_prepend (result, path);
g_free (retval);
}
}
p = strchr (p, '\n');
if (p)
p++;
}
return g_list_reverse (result);
}
void
glade_util_object_set_property (GObject *object, GladeProperty *property)
{
GValue void_string = {0,};
GValue *value = property->value;
if (G_VALUE_HOLDS_STRING (property->value) && g_value_get_string (property->value) == NULL)
{
g_value_init (&void_string, G_TYPE_STRING);
g_value_set_static_string (&void_string, "");
value = &void_string;
}
if (property->class->set_function)
property->class->set_function (object, value);
else
g_object_set_property (object, property->class->id, value);
}