glade/gladeui/glade-signal-editor.c
Johannes Schmid 02c435f354 Merge branch 'master' into signal-tree-model
Conflicts:
	gladeui/Makefile.am
	gladeui/glade-app.c
	gladeui/glade-app.h
	gladeui/glade-editor.c
	gladeui/glade-signal-editor.c
	gladeui/glade-signal-editor.h
	gladeui/glade-signal.h
	gladeui/glade-widget.c
	gladeui/glade-widget.h
2011-01-05 10:19:38 +01:00

558 lines
17 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:
* Shane Butler <shane_b@users.sourceforge.net>
* Joaquin Cuenca Abela <e98cuenc@yahoo.com>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/**
* SECTION:glade-signal-editor
* @Title: GladeSignalEditor
* @Short_Description: An interface to edit signals for a #GladeWidget.
*W
*/
#include <string.h>
#include <glib/gi18n-lib.h>
#include "glade.h"
#include "glade-widget.h"
#include "glade-widget-adaptor.h"
#include "glade-signal.h"
#include "glade-signal-editor.h"
#include "glade-signal-model.h"
#include "glade-cell-renderer-icon.h"
#include "glade-editor.h"
#include "glade-command.h"
#include "glade-marshallers.h"
#include "glade-accumulators.h"
#include "glade-project.h"
G_DEFINE_TYPE (GladeSignalEditor, glade_signal_editor, GTK_TYPE_VBOX)
struct _GladeSignalEditorPrivate
{
GtkTreeModel *model;
GladeWidget *widget;
GladeWidgetAdaptor* adaptor;
GtkWidget* signal_tree;
GtkTreeViewColumn* column_name;
GtkTreeViewColumn* column_handler;
GtkTreeViewColumn* column_userdata;
GtkTreeViewColumn* column_swap;
GtkTreeViewColumn* column_after;
GtkCellRenderer* renderer_userdata;
};
/* Signal handlers */
static void
on_handler_edited (GtkCellRendererText* renderer,
gchar* path,
gchar* handler,
gpointer user_data)
{
GladeSignalEditor* self = GLADE_SIGNAL_EDITOR(user_data);
GtkTreePath* tree_path = gtk_tree_path_new_from_string (path);
GtkTreeIter iter;
gchar* old_handler;
gboolean not_dummy;
g_return_if_fail (self->priv->widget != NULL);
gtk_tree_model_get_iter (self->priv->model,
&iter,
tree_path);
gtk_tree_model_get (self->priv->model, &iter,
GLADE_SIGNAL_COLUMN_NOT_DUMMY, &not_dummy,
GLADE_SIGNAL_COLUMN_HANDLER, &old_handler, -1);
/* False alarm ? */
if (handler && !g_str_equal (old_handler, handler))
{
if (not_dummy)
{
if (strlen (handler))
{
/* change an existing signal handler */
GladeSignal* old_signal;
GladeSignal* new_signal;
gtk_tree_model_get (self->priv->model,
&iter,
GLADE_SIGNAL_COLUMN_SIGNAL,
&old_signal, -1);
new_signal = glade_signal_clone (old_signal);
/* Change the new signal handler */
g_free (new_signal->handler);
new_signal->handler = g_strdup(handler);
glade_command_change_signal (self->priv->widget, old_signal, new_signal);
glade_signal_free (new_signal);
}
else
{
GladeSignal* deleted_signal;
gtk_tree_model_get (self->priv->model,
&iter,
GLADE_SIGNAL_COLUMN_SIGNAL,
&deleted_signal, -1);
/* Delete signal */
glade_command_remove_signal (self->priv->widget,
deleted_signal);
}
}
else
{
GtkTreeIter parent;
GladeSignal* signal;
gchar* name;
/* Get the signal name */
gtk_tree_model_iter_parent (self->priv->model, &parent, &iter);
gtk_tree_model_get (self->priv->model, &parent,
GLADE_SIGNAL_COLUMN_NAME, &name,
-1);
/* Add a new signal handler */
signal = glade_signal_new (name, handler, NULL, FALSE, FALSE);
glade_command_add_signal (self->priv->widget, signal);
/* Select next column */
gtk_tree_view_set_cursor (GTK_TREE_VIEW(self->priv->signal_tree),
tree_path,
self->priv->column_userdata,
TRUE);
glade_signal_free (signal);
g_free (name);
}
}
g_free (old_handler);
gtk_tree_path_free (tree_path);
}
static void
on_userdata_edited (GtkCellRendererText* renderer,
gchar* path,
gchar* new_userdata,
gpointer user_data)
{
GladeSignalEditor* self = GLADE_SIGNAL_EDITOR(user_data);
GtkTreePath* tree_path = gtk_tree_path_new_from_string (path);
GtkTreeIter iter;
gchar* old_userdata;
g_return_if_fail (self->priv->widget != NULL);
gtk_tree_model_get_iter (self->priv->model,
&iter,
tree_path);
gtk_tree_model_get (self->priv->model, &iter,
GLADE_SIGNAL_COLUMN_OBJECT, &old_userdata, -1);
/* False alarm ? */
if (new_userdata && !g_str_equal (old_userdata, new_userdata))
{
/* change an existing signal handler */
GladeSignal* old_signal;
GladeSignal* new_signal;
gtk_tree_model_get (self->priv->model,
&iter,
GLADE_SIGNAL_COLUMN_SIGNAL,
&old_signal, -1);
new_signal = glade_signal_clone (old_signal);
/* Change the new signal handler */
g_free (new_signal->userdata);
new_signal->userdata = g_strdup(new_userdata);
glade_command_change_signal (self->priv->widget, old_signal, new_signal);
glade_signal_free (new_signal);
}
g_free (old_userdata);
gtk_tree_path_free (tree_path);
}
static void
on_swap_toggled (GtkCellRendererToggle* renderer,
gchar* path,
gpointer user_data)
{
GladeSignalEditor* self = GLADE_SIGNAL_EDITOR(user_data);
GtkTreePath* tree_path = gtk_tree_path_new_from_string (path);
GtkTreeIter iter;
GladeSignal* old_signal;
GladeSignal* new_signal;
g_return_if_fail (self->priv->widget != NULL);
gtk_tree_model_get_iter (self->priv->model,
&iter,
tree_path);
gtk_tree_model_get (self->priv->model,
&iter,
GLADE_SIGNAL_COLUMN_SIGNAL,
&old_signal, -1);
new_signal = glade_signal_clone (old_signal);
/* Change the new signal handler */
new_signal->swapped = !gtk_cell_renderer_toggle_get_active (renderer);
glade_command_change_signal (self->priv->widget, old_signal, new_signal);
glade_signal_free (new_signal);
gtk_tree_path_free (tree_path);
}
static void
on_after_toggled (GtkCellRendererToggle* renderer,
gchar* path,
gpointer user_data)
{
GladeSignalEditor* self = GLADE_SIGNAL_EDITOR(user_data);
GtkTreePath* tree_path = gtk_tree_path_new_from_string (path);
GtkTreeIter iter;
GladeSignal* old_signal;
GladeSignal* new_signal;
g_return_if_fail (self->priv->widget != NULL);
gtk_tree_model_get_iter (self->priv->model,
&iter,
tree_path);
gtk_tree_model_get (self->priv->model,
&iter,
GLADE_SIGNAL_COLUMN_SIGNAL,
&old_signal, -1);
new_signal = glade_signal_clone (old_signal);
/* Change the new signal handler */
new_signal->after = !gtk_cell_renderer_toggle_get_active (renderer);
glade_command_change_signal (self->priv->widget, old_signal, new_signal);
glade_signal_free (new_signal);
gtk_tree_path_free (tree_path);
}
/**
* glade_signal_editor_new:
*
* Returns: a new #GladeSignalEditor
*/
GladeSignalEditor *
glade_signal_editor_new ()
{
GladeSignalEditor *signal_editor;
signal_editor = GLADE_SIGNAL_EDITOR (g_object_new (GLADE_TYPE_SIGNAL_EDITOR,
NULL, NULL));
return signal_editor;
}
/**
* glade_signal_editor_load_widget:
* @editor: a #GladeSignalEditor
* @widget: a #GladeWidget or NULL
*
* Load a widget in the signal editor. This will show all signals of the widget
* an it's accessors in the signal editor where the user can edit them.
*/
void
glade_signal_editor_load_widget (GladeSignalEditor *editor,
GladeWidget *widget)
{
GladeSignalEditorPrivate *priv = editor->priv;
if (priv->widget != widget)
{
priv->widget = widget;
priv->adaptor = widget ? widget->adaptor : NULL;
if (priv->widget)
{
g_object_ref (priv->widget);
}
}
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->signal_tree), NULL);
priv->model = NULL;
if (!widget)
return;
priv->model = glade_widget_get_signal_model (widget);
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->signal_tree), priv->model);
g_object_set (priv->renderer_userdata, "model", glade_app_get_project (), NULL);
}
/**
* glade_signal_editor_enable_dnd:
* @editor: a #GladeSignalEditor
* @enabled: whether the drag and drop support should be enabled
*
* If drag and drop support is enabled, the user will be able to drag signal handler
* from the tree to some editor. The type of the dnd data will be "application/x-glade-signal"
* and it will be in the form of "widget:signal:handler" so for example
* "GtkToggleButton:toggled:on_toggle_button_toggled".
*/
void
glade_signal_editor_enable_dnd (GladeSignalEditor *editor, gboolean enabled)
{
GladeSignalEditorPrivate *priv = editor->priv;
if (enabled)
{
const GtkTargetEntry entry = {
"application/x-glade-signal",
GTK_TARGET_OTHER_WIDGET,
1
};
/* DND */
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW(priv->signal_tree),
GDK_BUTTON1_MASK,
&entry,
1,
GDK_ACTION_COPY);
}
else
{
gtk_tree_view_unset_rows_drag_source (GTK_TREE_VIEW (priv->signal_tree));
}
}
static void
glade_signal_editor_dispose (GObject *object)
{
G_OBJECT_CLASS (glade_signal_editor_parent_class)->dispose (object);
}
static void
name_cell_data_func (GtkTreeViewColumn* column,
GtkCellRenderer* renderer,
GtkTreeModel* model,
GtkTreeIter* iter,
gpointer data)
{
gchar* name;
gboolean bold;
gboolean handler;
PangoWeight weight = PANGO_WEIGHT_NORMAL;
gtk_tree_model_get (model, iter,
GLADE_SIGNAL_COLUMN_NAME, &name,
GLADE_SIGNAL_COLUMN_HAS_HANDLERS, &bold,
GLADE_SIGNAL_COLUMN_IS_HANDLER, &handler,
-1);
if (bold)
weight = PANGO_WEIGHT_BOLD;
g_object_set (renderer,
"text", name,
"weight", weight,
"visible", !handler,
NULL);
g_free (name);
}
static cairo_surface_t*
create_rich_drag_surface (GtkWidget* widget, const gchar* text)
{
PangoLayout* layout = pango_layout_new (gtk_widget_get_pango_context (widget));
cairo_t* cr;
cairo_surface_t* s;
gint width, height;
pango_layout_set_text (layout, text, -1);
pango_layout_get_size (layout, &width, &height);
width = PANGO_PIXELS(width) + 10;
height = PANGO_PIXELS(height) + 10;
s = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
cr = cairo_create (s);
cairo_rectangle (cr, 0, 0, 1, 1);
cairo_show_text (cr, text);
cairo_stroke (cr);
g_object_unref (layout);
return s;;
}
static void
glade_signal_editor_drag_begin (GtkWidget* widget,
GdkDragContext* context,
gpointer user_data)
{
cairo_surface_t *s = NULL;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeSelection* selection;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
gchar* handler;
gtk_tree_model_get (model, &iter,
GLADE_SIGNAL_COLUMN_HANDLER, &handler, -1);
s = create_rich_drag_surface (widget, handler);
}
if (s)
{
gtk_drag_set_icon_surface (context, s);
g_object_unref (s);
}
else
{
gtk_drag_set_icon_default (context);
}
}
static void
glade_signal_editor_init (GladeSignalEditor *self)
{
GtkWidget *scroll;
GtkCellRenderer* renderer;
GladeSignalEditorPrivate* priv;
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GLADE_TYPE_SIGNAL_EDITOR, GladeSignalEditorPrivate);
priv = self->priv;
/* Create signal tree */
priv->signal_tree = gtk_tree_view_new ();
/* Create columns */
/* Signal name */
renderer = gtk_cell_renderer_text_new ();
priv->column_name = gtk_tree_view_column_new_with_attributes (_("Signal"),
renderer,
NULL);
gtk_tree_view_column_set_cell_data_func (priv->column_name, renderer,
name_cell_data_func, self, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (self->priv->signal_tree), priv->column_name);
/* Signal handler */
renderer = gtk_cell_renderer_text_new ();
g_object_set (renderer,
"editable", TRUE, NULL);
g_signal_connect (renderer, "edited", G_CALLBACK(on_handler_edited), self);
priv->column_handler = gtk_tree_view_column_new_with_attributes (_("Handler"),
renderer,
"text", GLADE_SIGNAL_COLUMN_HANDLER,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (self->priv->signal_tree), priv->column_handler);
/* Signal user_data */
priv->renderer_userdata = gtk_cell_renderer_combo_new ();
g_signal_connect (priv->renderer_userdata, "edited", G_CALLBACK(on_userdata_edited),
self);
g_object_set (priv->renderer_userdata,
"has-entry", FALSE,
"text-column", GLADE_PROJECT_MODEL_COLUMN_NAME,
NULL);
priv->column_userdata = gtk_tree_view_column_new_with_attributes (_("User data"),
priv->renderer_userdata,
"text", GLADE_SIGNAL_COLUMN_OBJECT,
"sensitive", GLADE_SIGNAL_COLUMN_NOT_DUMMY,
"editable", GLADE_SIGNAL_COLUMN_NOT_DUMMY,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (self->priv->signal_tree), priv->column_userdata);
/* Swap signal */
renderer = gtk_cell_renderer_toggle_new ();
g_signal_connect (renderer, "toggled", G_CALLBACK (on_swap_toggled), self);
priv->column_swap = gtk_tree_view_column_new_with_attributes (_("Swap"),
renderer,
"active", GLADE_SIGNAL_COLUMN_SWAP,
"sensitive", GLADE_SIGNAL_COLUMN_NOT_DUMMY,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (self->priv->signal_tree), priv->column_swap);
/* After */
renderer = gtk_cell_renderer_toggle_new ();
g_signal_connect (renderer, "toggled", G_CALLBACK (on_after_toggled), self);
priv->column_after = gtk_tree_view_column_new_with_attributes (_("After"),
renderer,
"active", GLADE_SIGNAL_COLUMN_AFTER,
"sensitive", GLADE_SIGNAL_COLUMN_NOT_DUMMY,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (self->priv->signal_tree), priv->column_after);
/* Create scrolled window */
scroll = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll),
GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (scroll), self->priv->signal_tree);
gtk_box_pack_start (GTK_BOX (self), scroll, TRUE, TRUE, 0);
/* Dnd */
g_signal_connect_after (self->priv->signal_tree,
"drag-begin",
G_CALLBACK(glade_signal_editor_drag_begin),
self);
gtk_widget_show_all (GTK_WIDGET(self));
}
static void
glade_signal_editor_class_init (GladeSignalEditorClass *klass)
{
GObjectClass *object_class;
glade_signal_editor_parent_class = g_type_class_peek_parent (klass);
object_class = G_OBJECT_CLASS (klass);
object_class->dispose = glade_signal_editor_dispose;
g_type_class_add_private (klass, sizeof (GladeSignalEditorPrivate));
}