glade/gladeui/glade-utils.c
2010-12-30 01:15:19 +09:00

2085 lines
51 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Authors:
* Chema Celorio <chema@celorio.com>
*/
#include <config.h>
/**
* SECTION:glade-utils
* @Title: Glade Utils
* @Short_Description: Welcome to the zoo.
*
* This is where all of that really usefull miscalanious stuff lands up.
*/
#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-adaptor.h"
#include "glade-property.h"
#include "glade-property-class.h"
#include "glade-clipboard.h"
#include "glade-fixed.h"
#include <string.h>
#include <gdk/gdkkeysyms.h>
#include <gmodule.h>
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
#include <errno.h>
#ifdef G_OS_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#endif
#define GLADE_UTIL_SELECTION_NODE_SIZE 7
#define GLADE_UTIL_COPY_BUFFSIZE 1024
/* List of widgets that have selection
*/
static GList *glade_util_selection = NULL;
/**
* glade_util_compose_get_type_func:
* @name:
*
* TODO: write me
*
* Returns:
*/
static gchar *
glade_util_compose_get_type_func (const gchar *name)
{
gchar *retval;
GString *tmp;
gint i = 1, j;
tmp = g_string_new (name);
while (tmp->str[i])
{
if (g_ascii_isupper (tmp->str[i]))
{
tmp = g_string_insert_c (tmp, i++, '_');
j = 0;
while (g_ascii_isupper (tmp->str[i++]))
j++;
if (j > 2)
g_string_insert_c (tmp, i-2, '_');
continue;
}
i++;
}
tmp = g_string_append (tmp, "_get_type");
retval = g_ascii_strdown (tmp->str, tmp->len);
g_string_free (tmp, TRUE);
return retval;
}
/**
* glade_util_get_type_from_name:
* @name: the name of the #GType - like 'GtkWidget' or a "get-type" function.
* @have_func: function-name flag -- true if the name is a "get-type" function.
*
* Returns the type using the "get type" function name based on @name.
* If the @have_func flag is true,@name is used directly, otherwise the get-type
* function is contrived from @name then used.
*
* Returns: the new #GType
*/
GType
glade_util_get_type_from_name (const gchar *name, gboolean have_func)
{
static GModule *allsymbols = NULL;
GType (*get_type) ();
GType type = 0;
gchar *func_name = (gchar*)name;
if ((type = g_type_from_name (name)) == 0 &&
(have_func || (func_name = glade_util_compose_get_type_func (name)) != NULL))
{
if (!allsymbols)
allsymbols = g_module_open (NULL, 0);
if (g_module_symbol (allsymbols, func_name,
(gpointer) &get_type))
{
g_assert (get_type);
type = get_type ();
}
else
{
g_warning (_("We could not find the symbol \"%s\""),
func_name);
}
g_free (func_name);
}
if (type == 0)
g_warning(_("Could not get the type from \"%s\""), name);
return type;
}
/**
* glade_utils_get_pspec_from_funcname:
* @funcname: the symbol name of a function to generate a #GParamSpec
*
* Returns: A #GParamSpec created by the delagate function
* specified by @funcname
*/
GParamSpec *
glade_utils_get_pspec_from_funcname (const gchar *funcname)
{
static GModule *allsymbols = NULL;
GParamSpec *pspec = NULL;
GParamSpec *(*get_pspec)() = NULL;
if (!allsymbols)
allsymbols = g_module_open (NULL, 0);
if (!g_module_symbol (allsymbols, funcname,
(gpointer) &get_pspec)) {
g_warning (_("We could not find the symbol \"%s\""),
funcname);
return NULL;
}
g_assert (get_pspec);
pspec = get_pspec ();
return pspec;
}
/**
* glade_util_ui_message:
* @parent: a #GtkWindow cast as a #GtkWidget
* @type: a #GladeUIMessageType
* @widget: a #GtkWidget to append to the dialog vbox
* @format: a printf style format string
* @...: args for the format.
*
* Creates a new warning dialog window as a child of @parent containing
* the text of @format, runs it, then destroys it on close. Depending
* on @type, a cancel button may apear or the icon may change.
*
* Returns: True if the @type was GLADE_UI_ARE_YOU_SURE and the user
* selected "OK", True if the @type was GLADE_UI_YES_OR_NO and
* the user selected "YES"; False otherwise.
*/
gint
glade_util_ui_message (GtkWidget *parent,
GladeUIMessageType type,
GtkWidget *widget,
const gchar *format,
...)
{
GtkWidget *dialog;
GtkMessageType message_type = GTK_MESSAGE_INFO;
GtkButtonsType buttons_type = GTK_BUTTONS_OK;
va_list args;
gchar *string;
gint response;
va_start (args, format);
string = g_strdup_vprintf (format, args);
va_end (args);
/* Get message_type */
switch (type)
{
case GLADE_UI_INFO: message_type = GTK_MESSAGE_INFO; break;
case GLADE_UI_WARN:
case GLADE_UI_ARE_YOU_SURE:
message_type = GTK_MESSAGE_WARNING;
break;
case GLADE_UI_ERROR: message_type = GTK_MESSAGE_ERROR; break;
case GLADE_UI_YES_OR_NO: message_type = GTK_MESSAGE_QUESTION; break;
break;
default:
g_critical ("Bad arg for glade_util_ui_message");
break;
}
/* Get buttons_type */
switch (type)
{
case GLADE_UI_INFO:
case GLADE_UI_WARN:
case GLADE_UI_ERROR:
buttons_type = GTK_BUTTONS_OK;
break;
case GLADE_UI_ARE_YOU_SURE: buttons_type = GTK_BUTTONS_OK_CANCEL; break;
case GLADE_UI_YES_OR_NO: buttons_type = GTK_BUTTONS_YES_NO; break;
break;
default:
g_critical ("Bad arg for glade_util_ui_message");
break;
}
dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
GTK_DIALOG_DESTROY_WITH_PARENT,
message_type,
buttons_type,
"%s",
string);
gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
if (widget)
gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
widget, TRUE, TRUE, 2);
response = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
g_free (string);
return (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_YES);
}
gboolean
glade_util_check_and_warn_scrollable (GladeWidget *parent,
GladeWidgetAdaptor *child_adaptor,
GtkWidget *parent_widget)
{
if (GTK_IS_SCROLLED_WINDOW (parent->object) &&
GWA_SCROLLABLE_WIDGET (child_adaptor) == FALSE)
{
GladeWidgetAdaptor *vadaptor =
glade_widget_adaptor_get_by_type (GTK_TYPE_VIEWPORT);
glade_util_ui_message (parent_widget,
GLADE_UI_INFO, NULL,
_("Cannot add non scrollable %s widget to a %s directly.\n"
"Add a %s first."),
child_adaptor->title,
parent->adaptor->title,
vadaptor->title);
return TRUE;
}
return FALSE;
}
typedef struct {
GtkStatusbar *statusbar;
guint context_id;
guint message_id;
} FlashInfo;
static const guint flash_length = 3;
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);
g_timeout_add_seconds (flash_length, (GSourceFunc) 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;
}
/**
* glade_util_compare_stock_labels:
* @a: a #gconstpointer to a #GtkStockItem
* @b: a #gconstpointer to a #GtkStockItem
*
* This is a #GCompareFunc that compares the labels of two stock items,
* ignoring any '_' characters. It isn't particularly efficient.
*
* Returns: negative value if @a < @b; zero if @a = @b;
* positive value if @a > @b
*/
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;
}
/**
* glade_util_hide_window:
* @window: a #GtkWindow
*
* 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_dialog_new:
* @title: dialog title
* @project: a #GladeProject used when saving
* @parent: a parent #GtkWindow for the dialog
* @action: a #GladeUtilFileDialogType to say if the dialog will open or save
*
* Returns: a "glade file" file chooser dialog. The caller is responsible
* for showing the dialog
*/
GtkWidget *
glade_util_file_dialog_new (const gchar *title,
GladeProject *project,
GtkWindow *parent,
GladeUtilFileDialogType action)
{
GtkWidget *file_dialog;
GtkFileFilter *file_filter;
g_return_val_if_fail ((action == GLADE_FILE_DIALOG_ACTION_OPEN ||
action == GLADE_FILE_DIALOG_ACTION_SAVE), NULL);
g_return_val_if_fail ((action != GLADE_FILE_DIALOG_ACTION_SAVE ||
GLADE_IS_PROJECT (project)), NULL);
file_dialog = gtk_file_chooser_dialog_new (title, parent, action,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
action == GLADE_FILE_DIALOG_ACTION_OPEN ?
GTK_STOCK_OPEN : GTK_STOCK_SAVE,
GTK_RESPONSE_OK,
NULL);
file_filter = gtk_file_filter_new ();
gtk_file_filter_add_pattern (file_filter, "*");
gtk_file_filter_set_name (file_filter, _("All Files"));
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
file_filter = gtk_file_filter_new ();
gtk_file_filter_add_pattern (file_filter, "*.glade");
gtk_file_filter_set_name (file_filter, _("Libglade Files"));
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
file_filter = gtk_file_filter_new ();
gtk_file_filter_add_pattern (file_filter, "*.ui");
gtk_file_filter_set_name (file_filter, _("GtkBuilder Files"));
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
file_filter = gtk_file_filter_new ();
gtk_file_filter_add_pattern (file_filter, "*.ui");
gtk_file_filter_add_pattern (file_filter, "*.glade");
gtk_file_filter_set_name (file_filter, _("All Glade Files"));
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (file_dialog), TRUE);
gtk_dialog_set_default_response (GTK_DIALOG (file_dialog), GTK_RESPONSE_OK);
return file_dialog;
}
/**
* glade_util_replace:
* @str: a string
* @a: a #gchar
* @b: a #gchar
*
* Replaces each occurance of the character @a in @str to @b.
*/
void
glade_util_replace (gchar *str, gchar a, gchar b)
{
g_return_if_fail (str != NULL);
while (*str != 0)
{
if (*str == a)
*str = b;
str = g_utf8_next_char (str);
}
}
/**
* glade_util_read_prop_name:
* @str: a string
*
* Return a usable version of a property identifier as found
* in a freshly parserd #GladeInterface
*/
gchar *
glade_util_read_prop_name (const gchar *str)
{
gchar *id;
g_return_val_if_fail (str != NULL, NULL);
id = g_strdup (str);
glade_util_replace (id, '_', '-');
return id;
}
/**
* glade_util_duplicate_underscores:
* @name: a string
*
* Duplicates @name, but the copy has two underscores in place of any single
* underscore in the original.
*
* Returns: a newly allocated string
*/
gchar *
glade_util_duplicate_underscores (const gchar *name)
{
const gchar *tmp;
const gchar *last_tmp = name;
gchar *underscored_name = g_malloc (strlen (name) * 2 + 1);
gchar *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;
}
static void
glade_util_draw_nodes (cairo_t *cr, GdkColor *color,
gint x, gint y,
gint width, gint height)
{
if (width > GLADE_UTIL_SELECTION_NODE_SIZE && height > GLADE_UTIL_SELECTION_NODE_SIZE) {
glade_utils_cairo_draw_rectangle (cr, color, TRUE,
x, y,
GLADE_UTIL_SELECTION_NODE_SIZE,
GLADE_UTIL_SELECTION_NODE_SIZE);
glade_utils_cairo_draw_rectangle (cr, color, TRUE,
x, y + height - GLADE_UTIL_SELECTION_NODE_SIZE,
GLADE_UTIL_SELECTION_NODE_SIZE,
GLADE_UTIL_SELECTION_NODE_SIZE);
glade_utils_cairo_draw_rectangle (cr, color, TRUE,
x + width - GLADE_UTIL_SELECTION_NODE_SIZE, y,
GLADE_UTIL_SELECTION_NODE_SIZE,
GLADE_UTIL_SELECTION_NODE_SIZE);
glade_utils_cairo_draw_rectangle (cr, color, 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);
}
glade_utils_cairo_draw_rectangle (cr, color, FALSE, x, y, width - 1, height - 1);
}
/**
* glade_util_draw_selection_nodes:
* @widget: a #GtkWidget
* @cr: the #cairo_t to draw with
*
* Draws selection on @widget if @widget is selected.
*/
void
glade_util_draw_selection_nodes (GtkWidget *widget, cairo_t *cr)
{
GdkColor *color;
g_return_if_fail (GTK_IS_WIDGET (widget));
cairo_save (cr);
color = &(gtk_widget_get_style (widget)->black);
if (g_list_find (glade_util_selection, widget))
{
GtkAllocation allocation;
gtk_widget_get_allocation (widget, &allocation);
glade_util_draw_nodes (cr, color,
0, 0, allocation.width, allocation.height);
}
cairo_restore (cr);
}
/**
* glade_util_add_selection:
* @widget: a #GtkWidget
*
* Add visual selection to this GtkWidget
*/
void
glade_util_add_selection (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
if (glade_util_has_selection (widget))
return;
glade_util_selection =
g_list_prepend (glade_util_selection, widget);
gtk_widget_queue_draw (widget);
}
/**
* glade_util_remove_selection:
* @widget: a #GtkWidget
*
* Remove visual selection from this GtkWidget
*/
void
glade_util_remove_selection (GtkWidget *widget)
{
GtkWidget *parent;
g_return_if_fail (GTK_IS_WIDGET (widget));
if (!glade_util_has_selection (widget))
return;
glade_util_selection =
g_list_remove (glade_util_selection, widget);
/* We redraw the parent, since the selection rectangle may not be
cleared if we just redraw the widget itself. */
parent = gtk_widget_get_parent (widget);
if (parent)
gtk_widget_queue_draw (parent);
gtk_widget_queue_draw (widget);
}
/**
* glade_util_clear_selection:
*
* Clear all visual selections
*/
void
glade_util_clear_selection (void)
{
GtkWidget *widget;
GtkWidget *parent;
GList *list;
for (list = glade_util_selection;
list && list->data;
list = list->next)
{
widget = list->data;
parent = gtk_widget_get_parent (widget);
if (parent)
gtk_widget_queue_draw (parent);
gtk_widget_queue_draw (widget);
}
glade_util_selection =
(g_list_free (glade_util_selection), NULL);
}
/**
* glade_util_has_selection:
* @widget: a #GtkWidget
*
* Returns: %TRUE if @widget has visual selection, %FALSE otherwise
*/
gboolean
glade_util_has_selection (GtkWidget *widget)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
return g_list_find (glade_util_selection, widget) != NULL;
}
/**
* glade_util_get_selectoin:
*
* Returns: The list of selected #GtkWidgets
*/
GList *
glade_util_get_selection ()
{
return glade_util_selection;
}
/*
* 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);
}
/**
* glade_util_container_get_all_children:
* @container: a #GtkContainer
*
* Use this to itterate over all children in a GtkContainer,
* as it used _forall() instead of _foreach() (and the GTK+ version
* of this function is simply not exposed).
*
* Note that glade_widget_class_get_children() is the high-level
* abstraction and will usually end up calling this function.
*
* Returns: a #GList giving the contents of @container
*/
GList *
glade_util_container_get_all_children (GtkContainer *container)
{
GList *children = NULL;
g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
gtk_container_forall (container,
gtk_container_children_callback,
&children);
/* Preserve the natural order by reversing the list */
return g_list_reverse (children);
}
/**
* glade_util_count_placeholders:
* @parent: a #GladeWidget
*
* Returns: the amount of #GladePlaceholders parented by @parent
*/
gint
glade_util_count_placeholders (GladeWidget *parent)
{
gint placeholders = 0;
GList *list, *children;
/* count placeholders */
if ((children = glade_widget_adaptor_get_children
(parent->adaptor, parent->object)) != NULL)
{
for (list = children; list && list->data; list = list->next)
{
if (GLADE_IS_PLACEHOLDER (list->data))
placeholders++;
}
g_list_free (children);
}
return placeholders;
}
static GtkTreeIter *
glade_util_find_iter (GtkTreeModel *model,
GtkTreeIter *iter,
GladeWidget *findme,
gint column)
{
GtkTreeIter *retval = NULL;
GObject* object = NULL;
GtkTreeIter *next;
g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
g_return_val_if_fail (iter != NULL, NULL);
next = gtk_tree_iter_copy (iter);
g_return_val_if_fail (next != NULL, NULL);
while (retval == NULL)
{
GladeWidget *widget;
gtk_tree_model_get (model, next, column, &object, -1);
if (object &&
gtk_tree_model_get_column_type (model, column) == G_TYPE_OBJECT)
g_object_unref (object);
widget = glade_widget_get_from_gobject (object);
if (widget == findme)
{
retval = gtk_tree_iter_copy (next);
break;
}
else if (glade_widget_is_ancestor (findme, widget))
{
if (gtk_tree_model_iter_has_child (model, next))
{
GtkTreeIter child;
gtk_tree_model_iter_children (model, &child, next);
if ((retval = glade_util_find_iter
(model, &child, findme, column)) != NULL)
break;
}
/* Only search the branches where the searched widget
* is actually a child of the this row, optimize the
* searching this way
*/
break;
}
if (!gtk_tree_model_iter_next (model, next))
break;
}
gtk_tree_iter_free (next);
return retval;
}
/**
* glade_util_find_iter_by_widget:
* @model: a #GtkTreeModel
* @findme: a #GladeWidget
* @column: a #gint
*
* Looks through @model for the #GtkTreeIter corresponding to
* @findme under @column.
*
* Returns: a newly allocated #GtkTreeIter from @model corresponding
* to @findme which should be freed with gtk_tree_iter_free()
*
*/
GtkTreeIter *
glade_util_find_iter_by_widget (GtkTreeModel *model,
GladeWidget *findme,
gint column)
{
GtkTreeIter iter;
if (gtk_tree_model_get_iter_first (model, &iter))
{
return glade_util_find_iter (model, &iter, findme, column);
}
return NULL;
}
gboolean
glade_util_basenames_match (const gchar *path1,
const gchar *path2)
{
gboolean match = FALSE;
gchar *bname1;
gchar *bname2;
if (path1 && path2)
{
bname1 = g_path_get_basename (path1);
bname2 = g_path_get_basename (path2);
match = !strcmp (bname1, bname2);
g_free (bname1);
g_free (bname2);
}
return match;
}
/**
* glade_util_purify_list:
* @list: A #GList
*
* Returns: A newly allocated version of @list with no
* duplicate data entries
*/
GList *
glade_util_purify_list (GList *list)
{
GList *l, *newlist = NULL;
for (l = list; l; l = l->next)
if (!g_list_find (newlist, l->data))
newlist = g_list_prepend (newlist, l->data);
g_list_free (list);
return g_list_reverse (newlist);
}
/**
* glade_util_added_in_list:
* @old_list: the old #GList
* @new_list: the new #GList
*
* Returns: A newly allocated #GList of elements that
* are in @new but not in @old
*
*/
GList *
glade_util_added_in_list (GList *old_list,
GList *new_list)
{
GList *added = NULL, *list;
for (list = new_list; list; list = list->next)
{
if (!g_list_find (old_list, list->data))
added = g_list_prepend (added, list->data);
}
return g_list_reverse (added);
}
/**
* glade_util_removed_from_list:
* @old_list: the old #GList
* @new_list: the new #GList
*
* Returns: A newly allocated #GList of elements that
* are in @old no longer in @new
*
*/
GList *
glade_util_removed_from_list (GList *old_list,
GList *new_list)
{
GList *added = NULL, *list;
for (list = old_list; list; list = list->next)
{
if (!g_list_find (new_list, list->data))
added = g_list_prepend (added, list->data);
}
return g_list_reverse (added);
}
/**
* glade_util_canonical_path:
* @path: any path that may contain ".." or "." components
*
* Returns: an absolute path to the specified file or directory
* that contains no ".." or "." components (this does
* not call readlink like realpath() does).
*
* Note: on some systems; I think its possible that we dont have
* permission to execute in the directory in which the glade
* file resides; I decided finally to do it this way anyway
* since libc's realpath() does exactly the same.
*/
gchar *
glade_util_canonical_path (const gchar *path)
{
gchar *orig_dir, *dirname, *basename, *direct_dir, *direct_name = NULL;
g_return_val_if_fail (path != NULL, NULL);
basename = g_path_get_basename (path);
if ((orig_dir = g_get_current_dir ()) != NULL)
{
if ((dirname = g_path_get_dirname (path)) != NULL)
{
if (g_chdir (dirname) == 0)
{
if ((direct_dir = g_get_current_dir ()) != NULL)
direct_name =
g_build_filename (direct_dir, basename, NULL);
else
g_warning ("g_path");
if (g_chdir (orig_dir) != 0)
g_warning ("Unable to chdir back to %s directory (%s)",
orig_dir, g_strerror (errno));
} else g_warning ("Unable to chdir to %s directory (%s)",
dirname, g_strerror (errno));
g_free (dirname);
} else g_warning ("Unable to get directory component of %s\n", path);
g_free (orig_dir);
}
if (basename) g_free (basename);
return direct_name;
}
static gboolean
glade_util_canonical_match (const gchar *src_path,
const gchar *dest_path)
{
gchar *canonical_src, *canonical_dest;
gboolean match;
canonical_src = glade_util_canonical_path (src_path);
canonical_dest = glade_util_canonical_path (dest_path);
match = (strcmp (canonical_src, canonical_dest) == 0);
g_free (canonical_src);
g_free (canonical_dest);
return match;
}
/**
* glade_util_copy_file:
* @src_path: the path to the source file
* @dest_path: the path to the destination file to create or overwrite.
*
* Copies a file from @src to @dest, queries the user
* if it involves overwriting the target and displays an
* error message upon failure.
*
* Returns: True if the copy was successfull.
*/
gboolean
glade_util_copy_file (const gchar *src_path,
const gchar *dest_path)
{
GIOChannel *src, *dest;
GError *error = NULL;
GIOStatus read_status, write_status = G_IO_STATUS_ERROR;
gchar buffer[GLADE_UTIL_COPY_BUFFSIZE];
gsize bytes_read, bytes_written, written;
gboolean success = FALSE;
/* FIXME: This may break if src_path & dest_path are actually
* the same file, right now the canonical comparison is the
* best check I have.
*/
if (glade_util_canonical_match (src_path, dest_path))
return FALSE;
if (g_file_test (dest_path, G_FILE_TEST_IS_REGULAR) != FALSE)
if (glade_util_ui_message
(glade_app_get_window(), GLADE_UI_YES_OR_NO, NULL,
_("%s exists.\nDo you want to replace it?"), dest_path) == FALSE)
return FALSE;
if ((src = g_io_channel_new_file (src_path, "r", &error)) != NULL)
{
g_io_channel_set_encoding (src, NULL, NULL);
if ((dest = g_io_channel_new_file (dest_path, "w", &error)) != NULL)
{
g_io_channel_set_encoding (dest, NULL, NULL);
while ((read_status = g_io_channel_read_chars
(src, buffer, GLADE_UTIL_COPY_BUFFSIZE,
&bytes_read, &error)) != G_IO_STATUS_ERROR)
{
bytes_written = 0;
while ((write_status = g_io_channel_write_chars
(dest, buffer + bytes_written,
bytes_read - bytes_written,
&written, &error)) != G_IO_STATUS_ERROR &&
(bytes_written + written) < bytes_read)
bytes_written += written;
if (write_status == G_IO_STATUS_ERROR)
{
glade_util_ui_message (glade_app_get_window(),
GLADE_UI_ERROR, NULL,
_("Error writing to %s: %s"),
dest_path, error->message);
error = (g_error_free (error), NULL);
break;
}
/* Break on EOF & ERROR but not AGAIN and not NORMAL */
if (read_status == G_IO_STATUS_EOF) break;
}
if (read_status == G_IO_STATUS_ERROR)
{
glade_util_ui_message (glade_app_get_window(),
GLADE_UI_ERROR, NULL,
_("Error reading %s: %s"),
src_path, error->message);
error = (g_error_free (error), NULL);
}
/* From here on, unless we have problems shutting down, succuss ! */
success = (read_status == G_IO_STATUS_EOF &&
write_status == G_IO_STATUS_NORMAL);
if (g_io_channel_shutdown (dest, TRUE, &error) != G_IO_STATUS_NORMAL)
{
glade_util_ui_message
(glade_app_get_window(),
GLADE_UI_ERROR, NULL,
_("Error shutting down I/O channel %s: %s"),
dest_path, error->message);
error = (g_error_free (error), NULL);
success = FALSE;
}
}
else
{
glade_util_ui_message (glade_app_get_window(),
GLADE_UI_ERROR, NULL,
_("Failed to open %s for writing: %s"),
dest_path, error->message);
error = (g_error_free (error), NULL);
}
if (g_io_channel_shutdown (src, TRUE, &error) != G_IO_STATUS_NORMAL)
{
glade_util_ui_message (glade_app_get_window(),
GLADE_UI_ERROR, NULL,
_("Error shutting down I/O channel %s: %s"),
src_path, error->message);
success = FALSE;
}
}
else
{
glade_util_ui_message (glade_app_get_window(),
GLADE_UI_ERROR, NULL,
_("Failed to open %s for reading: %s"),
src_path, error->message);
error = (g_error_free (error), NULL);
}
return success;
}
/**
* glade_util_class_implements_interface:
* @class_type: A #GType
* @iface_type: A #GType
*
* Returns: whether @class_type implements the @iface_type interface
*/
gboolean
glade_util_class_implements_interface (GType class_type,
GType iface_type)
{
GType *ifaces;
guint n_ifaces, i;
gboolean implemented = FALSE;
if ((ifaces = g_type_interfaces (class_type, &n_ifaces)) != NULL)
{
for (i = 0; i < n_ifaces; i++)
if (ifaces[i] == iface_type)
{
implemented = TRUE;
break;
}
g_free (ifaces);
}
return implemented;
}
static GModule *
try_load_library (const gchar *library_path,
const gchar *library_name)
{
GModule *module = NULL;
gchar *path;
path = g_module_build_path (library_path, library_name);
if (g_file_test (path, G_FILE_TEST_EXISTS))
{
if (!(module = g_module_open (path, G_MODULE_BIND_LAZY)))
g_warning ("Failed to load %s: %s", path, g_module_error ());
}
g_free (path);
return module;
}
/**
* glade_util_load_library:
* @library_name: name of the library
*
* Loads the named library from the Glade modules directory, or failing that
* from the standard platform specific directories.
*
* The @library_name should not include any platform specifix prefix or suffix,
* those are automatically added, if needed, by g_module_build_path()
*
* Returns: a #GModule on success, or %NULL on failure.
*/
GModule *
glade_util_load_library (const gchar *library_name)
{
gchar *default_paths[] = { (gchar *)glade_app_get_modules_dir (),
NULL, /* <-- dynamically allocated */
"/lib",
"/usr/lib",
"/usr/local/lib",
NULL };
GModule *module = NULL;
const gchar *search_path;
gchar **split;
gint i;
if ((search_path = g_getenv (GLADE_ENV_MODULE_PATH)) != NULL)
{
if ((split = g_strsplit (search_path, ":", 0)) != NULL)
{
for (i = 0; split[i] != NULL; i++)
if ((module = try_load_library (split[i], library_name)) != NULL)
break;
g_strfreev (split);
}
}
if (!module)
{
/* Search ${prefix}/lib after searching ${prefix}/lib/glade3/modules... */
default_paths[1] = g_build_filename (glade_app_get_modules_dir (), "..", "..", NULL);
for (i = 0; default_paths[i] != NULL; i++)
if ((module = try_load_library (default_paths[i], library_name)) != NULL)
break;
g_free (default_paths[1]);
}
if (!module)
g_critical ("Unable to load module '%s' from any search paths", library_name);
return module;
}
/**
* glade_util_file_is_writeable:
* @path: the path to the file
*
* Checks whether the file at @path is writeable
*
* Returns: TRUE if file is writeable
*/
gboolean
glade_util_file_is_writeable (const gchar *path)
{
GIOChannel *channel;
g_return_val_if_fail (path != NULL, FALSE);
/* The only way to really know if the file is writable */
if ((channel = g_io_channel_new_file (path, "a+", NULL)) != NULL)
{
g_io_channel_unref (channel);
return TRUE;
}
return FALSE;
}
/**
* glade_util_have_devhelp:
*
* Returns: whether the devhelp module is loaded
*/
gboolean
glade_util_have_devhelp (void)
{
static gint have_devhelp = -1;
gchar *ptr;
gint cnt, ret, major, minor;
GError *error = NULL;
#define DEVHELP_OLD_MESSAGE \
"The DevHelp installed on your system is too old, " \
"devhelp feature will be disabled."
#define DEVHELP_MISSING_MESSAGE \
"No DevHelp installed on your system, " \
"devhelp feature will be disabled."
if (have_devhelp >= 0) return have_devhelp;
have_devhelp = 0;
if ((ptr = g_find_program_in_path ("devhelp")) != NULL)
{
g_free (ptr);
if (g_spawn_command_line_sync ("devhelp --version",
&ptr, NULL, &ret, &error))
{
/* If we have a successfull return code.. parse the output.
*/
if (ret == 0)
{
gchar name[16];
if ((cnt = sscanf (ptr, "%15s %d.%d\n",
name, &major, &minor)) == 3)
{
/* Devhelp 0.12 required.
*/
if (major >= 0 && minor >= 12)
have_devhelp = 1;
else
g_message (DEVHELP_OLD_MESSAGE);
}
else
{
if (ptr != NULL || strlen (ptr) > 0)
g_warning ("devhelp had unparsable output: "
"'%s' (parsed %d elements)", ptr, cnt);
else
g_message (DEVHELP_OLD_MESSAGE);
}
}
else g_warning ("devhelp had bad return code: '%d'", ret);
}
else
{
g_warning ("Error trying to launch devhelp: %s",
error->message);
g_error_free (error);
}
}
else g_message (DEVHELP_MISSING_MESSAGE);
return have_devhelp;
}
/**
* glade_util_get_devhelp_icon:
* @size: the preferred icon size
*
* Creates an image displaying the devhelp icon.
*
* Returns: a #GtkImage
*/
GtkWidget*
glade_util_get_devhelp_icon (GtkIconSize size)
{
GtkIconTheme *icon_theme;
GdkScreen *screen;
GtkWidget *image;
gchar *path;
image = gtk_image_new ();
screen = gtk_widget_get_screen (GTK_WIDGET (image));
icon_theme = gtk_icon_theme_get_for_screen (screen);
if (gtk_icon_theme_has_icon (icon_theme, GLADE_DEVHELP_ICON_NAME))
{
gtk_image_set_from_icon_name (GTK_IMAGE (image), GLADE_DEVHELP_ICON_NAME, size);
}
else
{
path = g_build_filename (glade_app_get_pixmaps_dir (), GLADE_DEVHELP_FALLBACK_ICON_FILE, NULL);
gtk_image_set_from_file (GTK_IMAGE (image), path);
g_free (path);
}
return image;
}
/**
* glade_util_search_devhep:
* @devhelp: the devhelp widget created by the devhelp module.
* @book: the devhelp book (or %NULL)
* @page: the page in the book (or %NULL)
* @search: the search string (or %NULL)
*
* Envokes devhelp with the appropriate search string
*
*/
void
glade_util_search_devhelp (const gchar *book,
const gchar *page,
const gchar *search)
{
GError *error = NULL;
gchar *book_comm = NULL, *page_comm = NULL, *search_comm = NULL;
gchar *string;
g_return_if_fail (glade_util_have_devhelp ());
if (book) book_comm = g_strdup_printf ("book:%s", book);
if (page) page_comm = g_strdup_printf (" page:%s", page);
if (search) search_comm = g_strdup_printf (" %s", search);
string = g_strdup_printf ("devhelp -s \"%s%s%s\"",
book_comm ? book_comm : "",
page_comm ? page_comm : "",
search_comm ? search_comm : "");
if (g_spawn_command_line_async (string, &error) == FALSE)
{
g_warning ("Error envoking devhelp: %s", error->message);
g_error_free (error);
}
g_free (string);
if (book_comm) g_free (book_comm);
if (page_comm) g_free (page_comm);
if (search_comm) g_free (search_comm);
}
GtkWidget *
glade_util_get_placeholder_from_pointer (GtkContainer *container)
{
GtkWidget *toplevel;
GtkWidget *retval = NULL, *child;
GtkAllocation allocation;
GList *c, *l;
gint x, y, x2, y2;
g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
gtk_widget_get_pointer (toplevel, &x, &y);
for (c = l = glade_util_container_get_all_children (container);
l;
l = g_list_next (l))
{
child = l->data;
if (GLADE_IS_PLACEHOLDER (child) &&
gtk_widget_get_mapped (child))
{
gtk_widget_translate_coordinates (toplevel, child,
x, y, &x2, &y2);
gtk_widget_get_allocation (child, &allocation);
if (x2 >= 0 && y2 >= 0 &&
x2 <= allocation.width &&
y2 <= allocation.height)
{
retval = child;
break;
}
}
}
g_list_free (c);
return retval;
}
/**
* glade_util_object_is_loading:
* @object: A #GObject
*
* Returns: Whether the object's project is being loaded or not.
*
*/
gboolean
glade_util_object_is_loading (GObject *object)
{
GladeProject *project;
GladeWidget *widget;
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
widget = glade_widget_get_from_gobject (object);
g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE);
project = glade_widget_get_project (widget);
return glade_project_is_loading (project);
}
/**
* glade_util_url_show:
* @url: An URL to display
*
* Portable function for showing an URL @url in a web browser.
*
* Returns: TRUE if a web browser was successfully launched, or FALSE
*
*/
gboolean
glade_util_url_show (const gchar *url)
{
GtkWidget *widget;
GError *error = NULL;
gboolean ret;
g_return_val_if_fail (url != NULL, FALSE);
widget = glade_app_get_window ();
ret = gtk_show_uri (gtk_widget_get_screen (widget),
url,
gtk_get_current_event_time (),
&error);
if (error != NULL)
{
GtkWidget *dialog_widget;
dialog_widget = gtk_message_dialog_new (GTK_WINDOW (widget),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"%s", _("Could not show link:"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog_widget),
"%s", error->message);
g_error_free (error);
g_signal_connect (dialog_widget, "response",
G_CALLBACK (gtk_widget_destroy), NULL);
gtk_window_present (GTK_WINDOW (dialog_widget));
}
return ret;
}
/**
* glade_util_get_file_mtime:
* @filename: A filename
* @error: return location for errors
*
* Gets the UTC modification time of file @filename.
*
* Returns: The mtime of the file, or %0 if the file attributes
* could not be read.
*/
time_t
glade_util_get_file_mtime (const gchar *filename, GError **error)
{
struct stat info;
gint retval;
retval = g_stat (filename, &info);
if (retval != 0) {
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
"could not stat file '%s': %s", filename, g_strerror (errno));
return (time_t) 0;
} else {
return info.st_mtime;
}
}
gchar *
glade_util_filename_to_icon_name (const gchar *value)
{
gchar *icon_name, *p;
g_return_val_if_fail (value && value[0], NULL);
icon_name = g_strdup_printf ("glade-generated-%s", value);
if ((p = strrchr (icon_name, '.')) != NULL)
*p = '-';
return icon_name;
}
gchar *
glade_util_icon_name_to_filename (const gchar *value)
{
/* sscanf makes us allocate a buffer */
gchar filename[FILENAME_MAX], *p;
g_return_val_if_fail (value && value[0], NULL);
sscanf (value, "glade-generated-%s", filename);
/* XXX: Filenames without an extention will evidently
* break here
*/
if ((p = strrchr (filename, '-')) != NULL)
*p = '.';
return g_strdup (filename);
}
gint
glade_utils_enum_value_from_string (GType enum_type, const gchar *strval)
{
gint value = 0;
const gchar *displayable;
GValue *gvalue;
g_return_val_if_fail (strval && strval[0], 0);
if (((displayable = glade_get_value_from_displayable (enum_type, strval)) != NULL &&
(gvalue = glade_utils_value_from_string (enum_type, displayable, NULL, NULL)) != NULL) ||
(gvalue = glade_utils_value_from_string (enum_type, strval, NULL, NULL)) != NULL)
{
value = g_value_get_enum (gvalue);
g_value_unset (gvalue);
g_free (gvalue);
}
return value;
}
static gchar *
glade_utils_enum_string_from_value_real (GType enum_type, gint value, gboolean displayable)
{
GValue gvalue = { 0, };
gchar *string;
g_value_init (&gvalue, enum_type);
g_value_set_enum (&gvalue, value);
string = glade_utils_string_from_value (&gvalue);
g_value_unset (&gvalue);
if (displayable && string)
{
const gchar *dstring = glade_get_displayable_value (enum_type, string);
if (dstring)
{
g_free (string);
return g_strdup (dstring);
}
}
return string;
}
gchar *
glade_utils_enum_string_from_value (GType enum_type, gint value)
{
return glade_utils_enum_string_from_value_real (enum_type, value, FALSE);
}
gchar *
glade_utils_enum_string_from_value_displayable (GType enum_type, gint value)
{
return glade_utils_enum_string_from_value_real (enum_type, value, TRUE);
}
gint
glade_utils_flags_value_from_string (GType flags_type, const gchar *strval)
{
gint value = 0;
const gchar *displayable;
GValue *gvalue;
g_return_val_if_fail (strval && strval[0], 0);
if (((displayable = glade_get_value_from_displayable (flags_type, strval)) != NULL &&
(gvalue = glade_utils_value_from_string (flags_type, displayable, NULL, NULL)) != NULL) ||
(gvalue = glade_utils_value_from_string (flags_type, strval, NULL, NULL)) != NULL)
{
value = g_value_get_flags (gvalue);
g_value_unset (gvalue);
g_free (gvalue);
}
return value;
}
static gchar *
glade_utils_flags_string_from_value_real (GType flags_type, gint value, gboolean displayable)
{
GValue gvalue = { 0, };
gchar *string;
g_value_init (&gvalue, flags_type);
g_value_set_flags (&gvalue, value);
string = glade_utils_string_from_value (&gvalue);
g_value_unset (&gvalue);
if (displayable && string)
{
const gchar *dstring = glade_get_displayable_value (flags_type, string);
if (dstring)
{
g_free (string);
return g_strdup (dstring);
}
}
return string;
}
gchar *
glade_utils_flags_string_from_value (GType flags_type, gint value)
{
return glade_utils_flags_string_from_value_real (flags_type, value, FALSE);
}
gchar *
glade_utils_flags_string_from_value_displayable (GType flags_type, gint value)
{
return glade_utils_flags_string_from_value_real (flags_type, value, TRUE);
}
/* A hash table of generically created property classes for
* fundamental types, so we can easily use glade's conversion
* system without using properties (only GTypes)
*/
static GHashTable *generic_property_classes = NULL;
static gboolean
utils_gtype_equal (gconstpointer v1,
gconstpointer v2)
{
return *((const GType*) v1) == *((const GType*) v2);
}
static guint
utils_gtype_hash (gconstpointer v)
{
return *(const GType*) v;
}
static GladePropertyClass *
pclass_from_gtype (GType type)
{
GladePropertyClass *property_class = NULL;
GParamSpec *pspec = NULL;
if (!generic_property_classes)
generic_property_classes = g_hash_table_new_full (utils_gtype_hash, utils_gtype_equal,
g_free, (GDestroyNotify)glade_property_class_free);
property_class = g_hash_table_lookup (generic_property_classes, &type);
if (!property_class)
{
/* Support enum and flag types, and a hardcoded list of fundamental types */
if (type == G_TYPE_CHAR)
pspec = g_param_spec_char ("dummy", "dummy", "dummy",
G_MININT8, G_MAXINT8, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_UCHAR)
pspec = g_param_spec_char ("dummy", "dummy", "dummy",
0, G_MAXUINT8, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_BOOLEAN)
pspec = g_param_spec_boolean ("dummy", "dummy", "dummy",
FALSE, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_INT)
pspec = g_param_spec_int ("dummy", "dummy", "dummy",
G_MININT, G_MAXINT, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_UINT)
pspec = g_param_spec_uint ("dummy", "dummy", "dummy",
0, G_MAXUINT, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_LONG)
pspec = g_param_spec_long ("dummy", "dummy", "dummy",
G_MINLONG, G_MAXLONG, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_ULONG)
pspec = g_param_spec_ulong ("dummy", "dummy", "dummy",
0, G_MAXULONG, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_INT64)
pspec = g_param_spec_int64 ("dummy", "dummy", "dummy",
G_MININT64, G_MAXINT64, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_UINT64)
pspec = g_param_spec_uint64 ("dummy", "dummy", "dummy",
0, G_MAXUINT64, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_FLOAT)
pspec = g_param_spec_float ("dummy", "dummy", "dummy",
G_MINFLOAT, G_MAXFLOAT, 1.0F, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_DOUBLE)
pspec = g_param_spec_double ("dummy", "dummy", "dummy",
G_MINDOUBLE, G_MAXDOUBLE, 1.0F, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_STRING)
pspec = g_param_spec_string ("dummy", "dummy", "dummy",
NULL, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_OBJECT || g_type_is_a (type, G_TYPE_OBJECT))
pspec = g_param_spec_object ("dummy", "dummy", "dummy",
type, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (G_TYPE_IS_ENUM (type))
{
GEnumClass *eclass = g_type_class_ref (type);
pspec = g_param_spec_enum ("dummy", "dummy", "dummy",
type, eclass->minimum, G_PARAM_READABLE|G_PARAM_WRITABLE);
g_type_class_unref (eclass);
}
else if (G_TYPE_IS_FLAGS (type))
pspec = g_param_spec_flags ("dummy", "dummy", "dummy",
type, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
if (pspec)
{
if ((property_class =
glade_property_class_new_from_spec_full (NULL, pspec, FALSE)) != NULL)
{
/* XXX If we ever free the hash table, property classes wont touch
* the allocated pspecs, so they would theoretically be leaked.
*/
g_hash_table_insert (generic_property_classes,
g_memdup (&type, sizeof (GType)), property_class);
}
else
g_warning ("Unable to create property class for type %s", g_type_name (type));
}
else
g_warning ("No generic conversion support for type %s", g_type_name (type));
}
return property_class;
}
/**
* glade_utils_value_from_string:
* @type: a #GType to convert with
* @string: the string to convert
* @project: the #GladeProject to look for formats of object names when needed
* @widget: if the value is a gobject, this #GladeWidget will be used to look
* for an object in the same widget tree.
*
* Allocates and sets a #GValue of type @type
* set to @string (using glade conversion routines)
*
* Returns: A newly allocated and set #GValue
*/
GValue *
glade_utils_value_from_string (GType type,
const gchar *string,
GladeProject *project,
GladeWidget *widget)
{
GladePropertyClass *pclass;
g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
g_return_val_if_fail (string != NULL, NULL);
if ((pclass = pclass_from_gtype (type)) != NULL)
return glade_property_class_make_gvalue_from_string (pclass, string, project, widget);
return NULL;
}
/**
* glade_utils_string_from_value:
* @value: a #GValue to convert
*
* Serializes #GValue into a string
* (using glade conversion routines)
*
* Returns: A newly allocated string
*/
gchar *
glade_utils_string_from_value (const GValue *value)
{
GladePropertyClass *pclass;
g_return_val_if_fail (value != NULL, NULL);
if ((pclass = pclass_from_gtype (G_VALUE_TYPE (value))) != NULL)
return glade_property_class_make_string_from_gvalue (pclass, value);
return NULL;
}
/**
* glade_utils_liststore_from_enum_type:
* @enum_type: A #GType
* @include_empty: wheather to prepend an "Unset" slot
*
* Creates a liststore suitable for comboboxes and such to
* chose from a variety of types.
*
* Returns: A new #GtkListStore
*/
GtkListStore *
glade_utils_liststore_from_enum_type (GType enum_type,
gboolean include_empty)
{
GtkListStore *store;
GtkTreeIter iter;
GEnumClass *eclass;
guint i;
eclass = g_type_class_ref (enum_type);
store = gtk_list_store_new (1, G_TYPE_STRING);
if (include_empty)
{
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
0, _("None"),
-1);
}
for (i = 0; i < eclass->n_values; i++)
{
const gchar *displayable = glade_get_displayable_value (enum_type, eclass->values[i].value_nick);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
0, displayable ? displayable : eclass->values[i].value_nick,
-1);
}
g_type_class_unref (eclass);
return store;
}
/**
* glade_utils_hijack_key_press:
* @win: a #GtkWindow
* event: the GdkEventKey
* user_data: unused
*
* This function is meant to be attached to key-press-event of a toplevel,
* it simply allows the window contents to treat key events /before/
* accelerator keys come into play (this way widgets dont get deleted
* when cutting text in an entry etc.).
* Creates a liststore suitable for comboboxes and such to
* chose from a variety of types.
*
* Returns: whether the event was handled
*/
gint
glade_utils_hijack_key_press (GtkWindow *win,
GdkEventKey *event,
gpointer user_data)
{
GtkWidget *focus_widget;
focus_widget = gtk_window_get_focus (win);
if (focus_widget &&
(event->keyval == GDK_KEY_Delete || /* Filter Delete from accelerator keys */
((event->state & GDK_CONTROL_MASK) && /* CNTL keys... */
((event->keyval == GDK_KEY_c || event->keyval == GDK_KEY_C) || /* CNTL-C (copy) */
(event->keyval == GDK_KEY_x || event->keyval == GDK_KEY_X) || /* CNTL-X (cut) */
(event->keyval == GDK_KEY_v || event->keyval == GDK_KEY_V) || /* CNTL-V (paste) */
(event->keyval == GDK_KEY_n || event->keyval == GDK_KEY_N))))) /* CNTL-N (new project) */
{
return gtk_widget_event (focus_widget,
(GdkEvent *)event);
}
return FALSE;
}
void
glade_utils_cairo_draw_line (cairo_t *cr,
GdkColor *color,
gint x1,
gint y1,
gint x2,
gint y2)
{
cairo_save (cr);
gdk_cairo_set_source_color (cr, color);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
cairo_move_to (cr, x1 + 0.5, y1 + 0.5);
cairo_line_to (cr, x2 + 0.5, y2 + 0.5);
cairo_stroke (cr);
cairo_restore (cr);
}
void
glade_utils_cairo_draw_rectangle (cairo_t *cr,
GdkColor *color,
gboolean filled,
gint x,
gint y,
gint width,
gint height)
{
gdk_cairo_set_source_color (cr, color);
if (filled)
{
cairo_rectangle (cr, x, y, width, height);
cairo_fill (cr);
}
else
{
cairo_rectangle (cr, x + 0.5, y + 0.5, width, height);
cairo_stroke (cr);
}
}
/* copied from gedit */
gchar *
glade_utils_replace_home_dir_with_tilde (const gchar *path)
{
#ifdef G_OS_UNIX
gchar *tmp;
gchar *home;
g_return_val_if_fail (path != NULL, NULL);
/* Note that g_get_home_dir returns a const string */
tmp = (gchar *) g_get_home_dir ();
if (tmp == NULL)
return g_strdup (path);
home = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
if (home == NULL)
return g_strdup (path);
if (strcmp (path, home) == 0)
{
g_free (home);
return g_strdup ("~");
}
tmp = home;
home = g_strdup_printf ("%s/", tmp);
g_free (tmp);
if (g_str_has_prefix (path, home))
{
gchar *res;
res = g_strdup_printf ("~/%s", path + strlen (home));
g_free (home);
return res;
}
g_free (home);
return g_strdup (path);
#else
return g_strdup (path);
#endif
}