mirror of
https://gitlab.gnome.org/GNOME/glade.git
synced 2025-10-15 00:02:24 -04:00
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>.
787 lines
20 KiB
C
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);
|
|
}
|