/* -*- 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>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/**
* SECTION:glade-editor
* @Short_Description: A Widget to edit a #GladeWidget.
*
* This is the Glade Notebook containing all the controls needed to configure a #GladeWidget.
*/
#include <stdio.h>
#include <string.h>
#include <glib/gi18n-lib.h>
#include "glade.h"
#include "glade-widget.h"
#include "glade-widget-adaptor.h"
#include "glade-editor.h"
#include "glade-signal-editor.h"
#include "glade-parameter.h"
#include "glade-property.h"
#include "glade-property-class.h"
#include "glade-command.h"
#include "glade-debug.h"
#include "glade-marshallers.h"
#include "glade-project.h"
#include "glade-utils.h"
#include "glade-editor-property.h"
enum
{
PROP_0,
PROP_SHOW_INFO,
PROP_SHOW_CONTEXT_INFO
};
enum {
GTK_DOC_SEARCH,
LAST_SIGNAL
};
static GtkVBoxClass *parent_class = NULL;
static guint glade_editor_signals[LAST_SIGNAL] = { 0 };
static void glade_editor_reset_dialog (GladeEditor *editor);
static void
glade_editor_gtk_doc_search_cb (GladeEditorProperty *eprop,
const gchar *book,
const gchar *page,
const gchar *search,
GladeEditor *editor)
{
/* Just act as a hub for search signals here */
g_signal_emit (G_OBJECT (editor),
glade_editor_signals[GTK_DOC_SEARCH],
0, book, page, search);
}
static void
glade_editor_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GladeEditor *editor = GLADE_EDITOR (object);
switch (prop_id)
{
case PROP_SHOW_INFO:
if (g_value_get_boolean (value))
glade_editor_show_info (editor);
else
glade_editor_hide_info (editor);
break;
case PROP_SHOW_CONTEXT_INFO:
if (g_value_get_boolean (value))
glade_editor_show_context_info (editor);
else
glade_editor_hide_context_info (editor);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
glade_editor_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GladeEditor *editor = GLADE_EDITOR (object);
switch (prop_id)
{
case PROP_SHOW_INFO:
g_value_set_boolean (value, editor->show_info);
break;
case PROP_SHOW_CONTEXT_INFO:
g_value_set_boolean (value, editor->show_context_info);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
glade_editor_class_init (GladeEditorClass *klass)
{
GObjectClass *object_class;
parent_class = g_type_class_peek_parent (klass);
object_class = G_OBJECT_CLASS (klass);
object_class->set_property = glade_editor_set_property;
object_class->get_property = glade_editor_get_property;
klass->gtk_doc_search = NULL;
/* Properties */
g_object_class_install_property
(object_class, PROP_SHOW_INFO,
g_param_spec_boolean ("show-info",
_("Show info"),
_("Whether to show an informational "
"button for the loaded widget"),
FALSE, G_PARAM_READABLE));
g_object_class_install_property
(object_class, PROP_SHOW_CONTEXT_INFO,
g_param_spec_boolean ("show-context-info",
_("Show context info"),
_("Whether to show an informational button for "
"each property and signal in the editor"),
FALSE, G_PARAM_READABLE));
/**
* GladeEditor::gtk-doc-search:
* @gladeeditor: the #GladeEditor which received the signal.
* @arg1: the (#gchar *) book to search or %NULL
* @arg2: the (#gchar *) page to search or %NULL
* @arg3: the (#gchar *) search string or %NULL
*
* Emitted when the editor requests that a doc-search be performed.
*/
glade_editor_signals[GTK_DOC_SEARCH] =
g_signal_new ("gtk-doc-search",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GladeEditorClass,
gtk_doc_search),
NULL, NULL,
glade_marshal_VOID__STRING_STRING_STRING,
G_TYPE_NONE, 3,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
}
static GtkWidget *
glade_editor_notebook_page (GladeEditor *editor, const gchar *name)
{
GtkWidget *alignment;
GtkWidget *sw;
GtkWidget *label_widget;
GtkWidget *image;
static gchar *path;
static gint page = 0;
/* alignment is needed to ensure property labels have some padding on the left */
alignment = gtk_alignment_new (0.5, 0, 1, 0);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 6, 0);
/* construct tab label widget */
if (g_utf8_collate (name, _("Accessibility")) == 0)
{
path = g_build_filename (glade_app_get_pixmaps_dir (), "atk.png", NULL);
image = gtk_image_new_from_file (path);
label_widget = gtk_event_box_new ();
gtk_container_add (GTK_CONTAINER (label_widget), image);
gtk_widget_show (label_widget);
gtk_widget_show (image);
glade_util_widget_set_tooltip (label_widget, name);
}
else
{
label_widget = gtk_label_new_with_mnemonic (name);
}
/* configure page container */
if (g_utf8_collate (name, _("_Signals")) == 0)
{
gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 1, 1);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 0, 0);
gtk_container_set_border_width (GTK_CONTAINER (alignment), 6);
gtk_notebook_insert_page (GTK_NOTEBOOK (editor->notebook), alignment,
label_widget, page++);
}
else
{
/* wrap the alignment in a scrolled window */
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw),
GTK_WIDGET (alignment));
gtk_container_set_border_width (GTK_CONTAINER (sw), 6);
gtk_notebook_insert_page (GTK_NOTEBOOK (editor->notebook), sw,
label_widget, page++);
}
return alignment;
}
static void
glade_editor_on_reset_click (GtkButton *button,
GladeEditor *editor)
{
glade_editor_reset_dialog (editor);
}
static void
glade_editor_on_docs_click (GtkButton *button,
GladeEditor *editor)
{
gchar *book;
if (editor->loaded_widget)
{
g_object_get (editor->loaded_widget->adaptor, "book", &book, NULL);
g_signal_emit (G_OBJECT (editor),
glade_editor_signals[GTK_DOC_SEARCH],
0, book, editor->loaded_widget->adaptor->name, NULL);
g_free (book);
}
}
static GtkWidget *
glade_editor_create_info_button (GladeEditor *editor)
{
GtkWidget *button;
GtkWidget *image;
button = gtk_button_new ();
image = glade_util_get_devhelp_icon (GTK_ICON_SIZE_BUTTON);
gtk_widget_show (image);
gtk_container_add (GTK_CONTAINER (button), image);
glade_util_widget_set_tooltip (button, _("View documentation for the selected widget"));
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (glade_editor_on_docs_click), editor);
return button;
}
static GtkWidget *
glade_editor_create_reset_button (GladeEditor *editor)
{
GtkWidget *image;
GtkWidget *button;
button = gtk_button_new ();
image = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (button), image);
glade_util_widget_set_tooltip (button, _("Reset widget properties to their defaults"));
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (glade_editor_on_reset_click), editor);
return button;
}
static void
glade_editor_init (GladeEditor *editor)
{
GtkSizeGroup *size_group;
GtkWidget *hbox;
editor->notebook = gtk_notebook_new ();
editor->page_widget = glade_editor_notebook_page (editor, _("_General"));
editor->page_packing = glade_editor_notebook_page (editor, _("_Packing"));
editor->page_common = glade_editor_notebook_page (editor, _("_Common"));
editor->page_signals = glade_editor_notebook_page (editor, _("_Signals"));
editor->page_atk = glade_editor_notebook_page (editor, _("Accessibility"));
editor->widget_tables = NULL;
editor->packing_etable = NULL;
editor->loading = FALSE;
gtk_container_set_border_width (GTK_CONTAINER (editor->notebook), 0);
gtk_box_pack_start (GTK_BOX (editor), editor->notebook, TRUE, TRUE, 0);
hbox = gtk_hbox_new (FALSE, 6);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
gtk_box_pack_start (GTK_BOX (editor), hbox, FALSE, FALSE, 0);
size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
/* Reset button */
editor->reset_button = glade_editor_create_reset_button (editor);
gtk_box_pack_end (GTK_BOX (hbox), editor->reset_button, FALSE, FALSE, 0);
gtk_size_group_add_widget (size_group, editor->reset_button);
/* Documentation button */
editor->info_button = glade_editor_create_info_button (editor);
gtk_box_pack_end (GTK_BOX (hbox), editor->info_button, FALSE, FALSE, 0);
gtk_size_group_add_widget (size_group, editor->info_button);
g_object_unref(size_group);
gtk_widget_show_all (GTK_WIDGET (editor));
if (editor->show_info)
gtk_widget_show (editor->info_button);
else
gtk_widget_hide (editor->info_button);
gtk_widget_hide (GTK_WIDGET (editor));
}
GType
glade_editor_get_type (void)
{
static GType type = 0;
if (!type) {
static const GTypeInfo info = {
sizeof (GladeEditorClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) glade_editor_class_init,
(GClassFinalizeFunc) NULL,
NULL,
sizeof (GladeEditor),
0,
(GInstanceInitFunc) glade_editor_init
};
type = g_type_register_static (GTK_TYPE_VBOX, "GladeEditor", &info, 0);
}
return type;
}
/**
* glade_editor_new:
*
* Returns: a new #GladeEditor
*/
GladeEditor *
glade_editor_new (void)
{
GladeEditor *editor;
editor = g_object_new (GLADE_TYPE_EDITOR,
"spacing", 6,
NULL);
return editor;
}
/*
* We call this function when the user changes the widget name using the
* entry on the properties editor.
*/
static void
glade_editor_widget_name_changed (GtkWidget *editable, GladeEditor *editor)
{
GladeWidget *widget;
gchar *new_name;
g_return_if_fail (GTK_IS_EDITABLE (editable));
g_return_if_fail (GLADE_IS_EDITOR (editor));
if (editor->loading) return;
widget = editor->loaded_widget;
new_name = gtk_editable_get_chars (GTK_EDITABLE (editable), 0, -1);
glade_command_set_name (widget, new_name);
g_free (new_name);
}
static gboolean
glade_editor_widget_name_focus_out (GtkWidget *entry,
GdkEventFocus *event,
GladeEditor *editor)
{
glade_editor_widget_name_changed (entry, editor);
return FALSE;
}
static void
glade_editor_table_attach (GtkWidget *table, GtkWidget *child, gint pos, gint row)
{
gtk_table_attach (GTK_TABLE (table), child,
pos, pos+1, row, row +1,
pos ? GTK_EXPAND | GTK_FILL : GTK_FILL,
GTK_EXPAND | GTK_FILL,
3, 1);
}
static GladeEditorProperty *
glade_editor_table_append_item (GladeEditorTable *table,
GladePropertyClass *klass,
gboolean from_query_dialog)
{
GladeEditorProperty *property;
property = glade_editor_property_new (klass, from_query_dialog == FALSE);
gtk_widget_show (GTK_WIDGET (property));
gtk_widget_show_all (property->eventbox);
if (table->editor->show_context_info && from_query_dialog == FALSE)
glade_editor_property_show_info (property);
else
glade_editor_property_hide_info (property);
g_signal_connect (G_OBJECT (property), "gtk-doc-search",
G_CALLBACK (glade_editor_gtk_doc_search_cb),
table->editor);
glade_editor_table_attach (table->table_widget, property->eventbox, 0, table->rows);
glade_editor_table_attach (table->table_widget, GTK_WIDGET (property), 1, table->rows);
table->rows++;
return property;
}
static void
glade_editor_table_append_name_field (GladeEditorTable *table)
{
GtkWidget *label;
/* Name */
label = gtk_label_new (_("Name:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
gtk_widget_show (label);
table->name_entry = gtk_entry_new ();
gtk_widget_show (table->name_entry);
g_signal_connect (G_OBJECT (table->name_entry), "activate",
G_CALLBACK (glade_editor_widget_name_changed),
table->editor);
g_signal_connect (G_OBJECT (table->name_entry), "focus-out-event",
G_CALLBACK (glade_editor_widget_name_focus_out),
table->editor);
glade_editor_table_attach (table->table_widget, label, 0, table->rows);
glade_editor_table_attach (table->table_widget, table->name_entry, 1, table->rows);
table->rows++;
}
static void
glade_editor_table_append_class_field (GladeEditorTable *table)
{
GtkWidget *label;
GtkWidget *class_entry;
/* Class */
label = gtk_label_new (_("Class:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
gtk_widget_show (label);
class_entry = gtk_entry_new ();
gtk_entry_set_text (GTK_ENTRY (class_entry), table->adaptor->name);
gtk_editable_set_editable (GTK_EDITABLE (class_entry), FALSE);
gtk_widget_show (class_entry);
glade_editor_table_attach (table->table_widget, label, 0, table->rows);
glade_editor_table_attach (table->table_widget, class_entry, 1, table->rows);
table->rows++;
}
static gint
glade_editor_property_class_comp (gconstpointer a, gconstpointer b)
{
const GladePropertyClass *ca = a, *cb = b;
if (ca->pspec->owner_type == cb->pspec->owner_type)
{
gdouble result = ca->weight - cb->weight;
/* Avoid cast to int */
if (result < 0.0) return -1;
else if (result > 0.0) return 1;
else return 0;
}
else
{
if (g_type_is_a (ca->pspec->owner_type, cb->pspec->owner_type))
return (ca->common || ca->packing) ? 1 : -1;
else
return (ca->common || ca->packing) ? -1 : 1;
}
}
static GList *
glade_editor_get_sorted_properties (GladeWidgetAdaptor *adaptor)
{
GList *l, *a = NULL, *b = NULL;
for (l = adaptor->properties; l && l->data; l = g_list_next (l))
{
GladePropertyClass *klass = l->data;
if (klass->common || klass->packing)
a = g_list_prepend (a, klass);
else
b = g_list_prepend (b, klass);
}
a = g_list_sort (a, glade_editor_property_class_comp);
b = g_list_sort (b, glade_editor_property_class_comp);
return g_list_concat (a, b);
}
static gboolean
glade_editor_table_append_items (GladeEditorTable *table,
GladeWidgetAdaptor *adaptor,
GladeEditorTableType type)
{
GladeEditorProperty *property;
GladePropertyClass *property_class;
GList *list, *sorted_list;
sorted_list = glade_editor_get_sorted_properties (adaptor);
for (list = sorted_list; list != NULL; list = list->next)
{
property_class = (GladePropertyClass *) list->data;
if (!glade_property_class_is_visible (property_class))
continue;
if (type == TABLE_TYPE_QUERY && !property_class->query)
continue;
else if (type == TABLE_TYPE_COMMON && !property_class->common)
continue;
else if (type == TABLE_TYPE_GENERAL && property_class->common)
continue;
else if (type == TABLE_TYPE_ATK &&
(property_class->type == GPC_NORMAL ||
property_class->type == GPC_ACCEL_PROPERTY))
continue;
else if (type != TABLE_TYPE_ATK &&
(property_class->type != GPC_NORMAL &&
property_class->type != GPC_ACCEL_PROPERTY))
continue;
property = glade_editor_table_append_item (table, property_class,
type == TABLE_TYPE_QUERY);
table->properties = g_list_prepend (table->properties, property);
}
g_list_free (sorted_list);
return TRUE;
}
static GladeEditorTable *
glade_editor_table_new (void)
{
GladeEditorTable *table;
table = g_new0 (GladeEditorTable, 1);
table->table_widget = gtk_table_new (0, 0, FALSE);
g_object_ref (G_OBJECT(table->table_widget));
return table;
}
static void
glade_editor_table_free (GladeEditorTable *etable)
{
g_object_unref (G_OBJECT(etable->table_widget));
g_free (etable);
}
static GladeEditorTable *
glade_editor_table_create (GladeEditor *editor,
GladeWidgetAdaptor *adaptor,
GladeEditorTableType type)
{
GladeEditorTable *table;
g_return_val_if_fail (GLADE_IS_EDITOR (editor), NULL);
g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL);
table = glade_editor_table_new ();
table->editor = editor;
table->adaptor = adaptor;
table->type = type;
if (type == TABLE_TYPE_GENERAL)
{
glade_editor_table_append_class_field (table);
glade_editor_table_append_name_field (table);
}
if (!glade_editor_table_append_items (table, adaptor, type))
return NULL;
gtk_widget_show (table->table_widget);
return table;
}
static GladeEditorTable *
glade_editor_get_table_from_class (GladeEditor *editor,
GladeWidgetAdaptor *adaptor,
GladeEditorTableType type)
{
GladeEditorTable *table;
GList *list;
for (list = editor->widget_tables; list; list = list->next)
{
table = list->data;
if (type != table->type)
continue;
if (table->adaptor == adaptor)
return table;
}
table = glade_editor_table_create (editor, adaptor, type);
g_return_val_if_fail (table != NULL, NULL);
editor->widget_tables = g_list_prepend (editor->widget_tables, table);
return table;
}
static void
glade_editor_load_page (GladeEditor *editor,
GladeWidgetAdaptor *adaptor,
GladeEditorTableType type)
{
GladeEditorTable *table;
GtkContainer *container = NULL;
GtkWidget *scrolled_window;
GList *list, *children;
GtkAdjustment *adj;
/* Remove the old table that was in this container */
switch (type)
{
case TABLE_TYPE_GENERAL:
container = GTK_CONTAINER (editor->page_widget);
break;
case TABLE_TYPE_COMMON:
container = GTK_CONTAINER (editor->page_common);
break;
case TABLE_TYPE_ATK:
container = GTK_CONTAINER (editor->page_atk);
break;
case TABLE_TYPE_PACKING:
case TABLE_TYPE_QUERY:
default:
g_critical ("Unreachable code reached !");
break;
}
children = gtk_container_get_children (container);
for (list = children; list; list = list->next) {
GtkWidget *widget = list->data;
g_return_if_fail (GTK_IS_WIDGET (widget));
gtk_widget_ref (widget);
gtk_container_remove (container, widget);
}
g_list_free (children);
if (!adaptor)
return;
table = glade_editor_get_table_from_class (editor, adaptor, type);
/* Attach the new table */
gtk_container_add (GTK_CONTAINER (container), table->table_widget);
/* Enable tabbed keynav in the editor */
if (table)
{
scrolled_window = gtk_widget_get_parent (GTK_WIDGET (container));
scrolled_window = gtk_widget_get_parent (scrolled_window);
/* FIXME: Save pointer to the scrolled window (or just the
adjustments) before hand. */
g_assert (GTK_IS_SCROLLED_WINDOW (scrolled_window));
adj = gtk_scrolled_window_get_vadjustment
(GTK_SCROLLED_WINDOW (scrolled_window));
gtk_container_set_focus_vadjustment
(GTK_CONTAINER (table->table_widget), adj);
adj = gtk_scrolled_window_get_hadjustment
(GTK_SCROLLED_WINDOW (scrolled_window));
gtk_container_set_focus_hadjustment
(GTK_CONTAINER (table->table_widget), adj);
}
}
/**
* glade_editor_update_widget_name:
* @editor: a #GladeEditor
*
* TODO: write me
*/
void
glade_editor_update_widget_name (GladeEditor *editor)
{
GladeEditorTable *table = glade_editor_get_table_from_class
(editor, editor->loaded_adaptor, TABLE_TYPE_GENERAL);
g_signal_handlers_block_by_func (G_OBJECT (table->name_entry), glade_editor_widget_name_changed, editor);
gtk_entry_set_text (GTK_ENTRY (table->name_entry), editor->loaded_widget->name);
g_signal_handlers_unblock_by_func (G_OBJECT (table->name_entry), glade_editor_widget_name_changed, editor);
}
static void
glade_editor_load_signal_page (GladeEditor *editor)
{
if (editor->signal_editor == NULL) {
editor->signal_editor = glade_signal_editor_new ((gpointer) editor);
gtk_container_add (GTK_CONTAINER (editor->page_signals),
glade_signal_editor_get_widget (editor->signal_editor));
}
}
static void
glade_editor_load_widget_class (GladeEditor *editor, GladeWidgetAdaptor *adaptor)
{
glade_editor_load_page (editor, adaptor, TABLE_TYPE_GENERAL);
glade_editor_load_page (editor, adaptor, TABLE_TYPE_COMMON);
glade_editor_load_page (editor, adaptor, TABLE_TYPE_ATK);
glade_editor_load_signal_page (editor);
editor->loaded_adaptor = adaptor;
}
static gint
glade_editor_property_comp (gconstpointer a, gconstpointer b)
{
const GladeProperty *prop_a = a, *prop_b = b;
return glade_editor_property_class_comp (prop_a->klass, prop_b->klass);
}
static void
glade_editor_load_packing_page (GladeEditor *editor, GladeWidget *widget)
{
GladeEditorProperty *editor_property;
GladeProperty *property;
GladeWidget *parent;
GList *list, *sorted_list;
GtkWidget *child;
/* Remove the old properties */
if ((child = gtk_bin_get_child (GTK_BIN (editor->page_packing))) != NULL)
gtk_container_remove (GTK_CONTAINER (editor->page_packing), child);
/* Free the packing editor table */
if (editor->packing_etable)
editor->packing_etable =
(glade_editor_table_free (editor->packing_etable), NULL);
/* Free the packing editor properties (we gave ownership to
* packing_etable->table_widget, so no need to unref them here).
*/
editor->packing_eprops = (g_list_free (editor->packing_eprops), NULL);
/* if the widget is a toplevel there are no packing properties */
if (widget == NULL || (parent = glade_widget_get_parent (widget)) == NULL)
return;
/* Now add the new properties */
editor->packing_etable = glade_editor_table_new ();
editor->packing_etable->editor = editor;
editor->packing_etable->type = TABLE_TYPE_PACKING;
/* Sort packing properties by weight */
sorted_list = g_list_copy (widget->packing_properties);
sorted_list = g_list_sort (sorted_list, glade_editor_property_comp);
for (list = sorted_list; list && list->data; list = list->next)
{
property = GLADE_PROPERTY (list->data);
if (glade_property_class_is_visible (property->klass) == FALSE)
continue;
editor_property = glade_editor_table_append_item (editor->packing_etable,
property->klass, FALSE);
editor->packing_eprops = g_list_prepend (editor->packing_eprops, editor_property);
glade_editor_property_load (editor_property, property);
}
g_list_free (sorted_list);
gtk_widget_show (editor->packing_etable->table_widget);
gtk_container_add (GTK_CONTAINER (editor->page_packing),
editor->packing_etable->table_widget);
}
static void
glade_editor_close_cb (GladeProject *project,
GladeEditor *editor)
{
/* Detected project we are viewing was closed,
* detatch from editor.
*/
glade_editor_load_widget (editor, NULL);
}
static void
glade_editor_load_table (GladeEditor *editor,
GladeWidget *widget,
GladeEditorTableType type)
{
GladeEditorProperty *property;
GladeEditorTable *table;
GList *list;
table = glade_editor_get_table_from_class
(editor, widget->adaptor, type);
if (table->name_entry)
gtk_entry_set_text (GTK_ENTRY (table->name_entry), widget->name);
for (list = table->properties; list; list = list->next)
{
property = list->data;
glade_editor_property_load_by_widget (property, widget);
}
}
static void
glade_editor_load_widget_real (GladeEditor *editor, GladeWidget *widget)
{
GladeWidgetAdaptor *adaptor;
GladeProject *project;
gchar *book;
/* Disconnect from last widget */
if (editor->loaded_widget != NULL)
{
project = glade_widget_get_project (editor->loaded_widget);
g_signal_handler_disconnect (G_OBJECT (project),
editor->project_closed_signal_id);
}
/* Load the GladeWidgetClass */
adaptor = widget ? widget->adaptor : NULL;
if (editor->loaded_adaptor != adaptor || adaptor == NULL)
glade_editor_load_widget_class (editor, adaptor);
glade_editor_load_packing_page (editor, widget);
glade_signal_editor_load_widget (editor->signal_editor, widget);
/* we are just clearing, we are done */
if (widget == NULL)
{
gtk_widget_set_sensitive (editor->reset_button, FALSE);
gtk_widget_set_sensitive (editor->info_button, FALSE);
editor->loaded_widget = NULL;
return;
}
gtk_widget_set_sensitive (editor->reset_button, TRUE);
g_object_get (editor->loaded_adaptor, "book", &book, NULL);
gtk_widget_set_sensitive (editor->info_button, book != NULL);
g_free (book);
editor->loading = TRUE;
/* Load each GladeEditorProperty from 'widget' */
glade_editor_load_table (editor, widget, TABLE_TYPE_GENERAL);
glade_editor_load_table (editor, widget, TABLE_TYPE_COMMON);
glade_editor_load_table (editor, widget, TABLE_TYPE_ATK);
editor->loaded_widget = widget;
editor->loading = FALSE;
/* Connect to new widget */
project = glade_widget_get_project (editor->loaded_widget);
editor->project_closed_signal_id =
g_signal_connect (G_OBJECT (project), "close",
G_CALLBACK (glade_editor_close_cb), editor);
}
/**
* glade_editor_load_widget:
* @editor: a #GladeEditor
* @widget: a #GladeWidget
*
* Load @widget into @editor. If @widget is %NULL, clear the editor.
*/
void
glade_editor_load_widget (GladeEditor *editor, GladeWidget *widget)
{
g_return_if_fail (GLADE_IS_EDITOR (editor));
g_return_if_fail (widget == NULL || GLADE_IS_WIDGET (widget));
if (editor->loaded_widget == widget)
return;
glade_editor_load_widget_real (editor, widget);
}
/**
* glade_editor_refresh:
* @editor: a #GladeEditor
*
* Synchronize @editor with the currently loaded widget.
*/
void
glade_editor_refresh (GladeEditor *editor)
{
g_return_if_fail (GLADE_IS_EDITOR (editor));
glade_editor_load_widget_real (editor, editor->loaded_widget);
}
static void
query_dialog_style_set_cb (GtkWidget *dialog,
GtkStyle *previous_style,
gpointer user_data)
{
gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 12);
gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 12);
gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 0);
gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6);
}
gboolean
glade_editor_query_dialog (GladeEditor *editor, GladeWidget *widget)
{
GtkWidget *dialog;
GladeEditorTable *table;
gchar *title;
GList *list;
GladeEditorProperty *property;
gint answer;
gboolean retval = TRUE;
title = g_strdup_printf (_("Create a %s"), widget->adaptor->name);
dialog = gtk_dialog_new_with_buttons (title, NULL,
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT |
GTK_DIALOG_NO_SEPARATOR,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
g_free (title);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
table = glade_editor_get_table_from_class (editor,
widget->adaptor,
TABLE_TYPE_QUERY);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
table->table_widget,
FALSE, FALSE, 6);
for (list = table->properties; list; list = list->next)
{
property = list->data;
glade_editor_property_load_by_widget (property, widget);
}
g_signal_connect (dialog, "style-set",
G_CALLBACK (query_dialog_style_set_cb),
NULL);
answer = gtk_dialog_run (GTK_DIALOG (dialog));
/*
* If user cancel's we cancel the whole "create operation" by
* return FALSE. glade_widget_new() will see the FALSE, and
* take care of canceling the "create" operation.
*/
if (answer == GTK_RESPONSE_CANCEL)
retval = FALSE;
gtk_container_remove (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
table->table_widget);
gtk_widget_destroy (dialog);
return retval;
}
enum {
COLUMN_ENABLED = 0,
COLUMN_PROP_NAME,
COLUMN_PROPERTY,
COLUMN_PARENT,
COLUMN_CHILD,
COLUMN_DEFAULT,
COLUMN_NDEFAULT,
COLUMN_DEFSTRING,
NUM_COLUMNS
};
static void
glade_editor_reset_toggled (GtkCellRendererToggle *cell,
gchar *path_str,
GtkTreeModel *model)
{
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
GtkTreeIter iter;
gboolean enabled;
/* get toggled iter */
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter,
COLUMN_ENABLED, &enabled, -1);
gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
COLUMN_ENABLED, !enabled, -1);
gtk_tree_path_free (path);
}
static GtkWidget *
glade_editor_reset_view (GladeEditor *editor)
{
GtkWidget *view_widget;
GtkTreeModel *model;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
model = (GtkTreeModel *)gtk_tree_store_new
(NUM_COLUMNS,
G_TYPE_BOOLEAN, /* Enabled value */
G_TYPE_STRING, /* Property name */
GLADE_TYPE_PROPERTY, /* The property */
G_TYPE_BOOLEAN, /* Parent node ? */
G_TYPE_BOOLEAN, /* Child node ? */
G_TYPE_BOOLEAN, /* Has default value */
G_TYPE_BOOLEAN, /* Doesn't have defaut */
G_TYPE_STRING); /* Default string */
view_widget = gtk_tree_view_new_with_model (model);
g_object_set (G_OBJECT (view_widget), "enable-search", FALSE, NULL);
/********************* fake invisible column *********************/
renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer), "editable", FALSE, "visible", FALSE, NULL);
column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (view_widget), column);
gtk_tree_view_column_set_visible (column, FALSE);
gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view_widget), column);
/************************ enabled column ************************/
renderer = gtk_cell_renderer_toggle_new ();
g_object_set (G_OBJECT (renderer),
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
"activatable", TRUE,
NULL);
g_signal_connect (renderer, "toggled",
G_CALLBACK (glade_editor_reset_toggled), model);
gtk_tree_view_insert_column_with_attributes
(GTK_TREE_VIEW (view_widget), COLUMN_ENABLED,
_("Reset"), renderer,
"sensitive", COLUMN_NDEFAULT,
"activatable", COLUMN_NDEFAULT,
"active", COLUMN_ENABLED,
"visible", COLUMN_CHILD,
NULL);
/********************* property name column *********************/
renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer),
"editable", FALSE,
"weight", PANGO_WEIGHT_BOLD, NULL);
gtk_tree_view_insert_column_with_attributes
(GTK_TREE_VIEW (view_widget), COLUMN_PROP_NAME,
_("Property"), renderer,
"text", COLUMN_PROP_NAME,
"weight-set", COLUMN_PARENT,
NULL);
/******************* default indicator column *******************/
renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer),
"editable", FALSE,
"style", PANGO_STYLE_ITALIC,
"foreground", "Gray", NULL);
gtk_tree_view_insert_column_with_attributes
(GTK_TREE_VIEW (view_widget), COLUMN_DEFSTRING,
NULL, renderer,
"text", COLUMN_DEFSTRING,
"visible", COLUMN_DEFAULT,
NULL);
return view_widget;
}
static void
glade_editor_populate_reset_view (GladeEditor *editor,
GtkTreeView *tree_view)
{
GtkTreeStore *model = (GtkTreeStore *)gtk_tree_view_get_model (tree_view);
GtkTreeIter property_iter, general_iter, common_iter, atk_iter, *iter;
GList *list;
GladeProperty *property;
gboolean def;
g_return_if_fail (editor->loaded_widget != NULL);
gtk_tree_store_append (model, &general_iter, NULL);
gtk_tree_store_set (model, &general_iter,
COLUMN_PROP_NAME, _("General"),
COLUMN_PROPERTY, NULL,
COLUMN_PARENT, TRUE,
COLUMN_CHILD, FALSE,
COLUMN_DEFAULT, FALSE,
COLUMN_NDEFAULT, FALSE,
-1);
gtk_tree_store_append (model, &common_iter, NULL);
gtk_tree_store_set (model, &common_iter,
COLUMN_PROP_NAME, _("Common"),
COLUMN_PROPERTY, NULL,
COLUMN_PARENT, TRUE,
COLUMN_CHILD, FALSE,
COLUMN_DEFAULT, FALSE,
COLUMN_NDEFAULT, FALSE,
-1);
gtk_tree_store_append (model, &atk_iter, NULL);
gtk_tree_store_set (model, &atk_iter,
COLUMN_PROP_NAME, _("Accessibility"),
COLUMN_PROPERTY, NULL,
COLUMN_PARENT, TRUE,
COLUMN_CHILD, FALSE,
COLUMN_DEFAULT, FALSE,
COLUMN_NDEFAULT, FALSE,
-1);
/* General & Common */
for (list = editor->loaded_widget->properties; list; list = list->next)
{
property = list->data;
if (glade_property_class_is_visible (property->klass) == FALSE)
continue;
if (property->klass->type != GPC_NORMAL &&
property->klass->type != GPC_ACCEL_PROPERTY)
iter = &atk_iter;
else if (property->klass->common)
iter = &common_iter;
else
iter = &general_iter;
def = glade_property_default (property);
gtk_tree_store_append (model, &property_iter, iter);
gtk_tree_store_set (model, &property_iter,
COLUMN_ENABLED, !def,
COLUMN_PROP_NAME, property->klass->name,
COLUMN_PROPERTY, property,
COLUMN_PARENT, FALSE,
COLUMN_CHILD, TRUE,
COLUMN_DEFAULT, def,
COLUMN_NDEFAULT, !def,
COLUMN_DEFSTRING, _("(default)"),
-1);
}
}
static gboolean
glade_editor_reset_selection_changed_cb (GtkTreeSelection *selection,
GtkTextView *desc_view)
{
GtkTreeIter iter;
GladeProperty *property = NULL;
GtkTreeModel *model = NULL;
GtkTextBuffer *text_buffer;
const gchar *message =
_("Select the properties that you want to reset to their default values");
/* Find selected data and show property blurb here */
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (desc_view));
gtk_tree_model_get (model, &iter, COLUMN_PROPERTY, &property, -1);
gtk_text_buffer_set_text (text_buffer,
property ? glade_property_get_tooltip (property) : message,
-1);
if (property)
g_object_unref (G_OBJECT (property));
}
return TRUE;
}
static gboolean
glade_editor_reset_foreach_selection (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gboolean select)
{
gboolean def;
gtk_tree_model_get (model, iter, COLUMN_DEFAULT, &def, -1);
/* Dont modify rows that are already default */
if (def == FALSE)
gtk_tree_store_set (GTK_TREE_STORE (model), iter,
COLUMN_ENABLED, select, -1);
return FALSE;
}
static void
glade_editor_reset_select_all_clicked (GtkButton *button,
GtkTreeView *tree_view)
{
GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc)
glade_editor_reset_foreach_selection,
GINT_TO_POINTER (TRUE));
}
static void
glade_editor_reset_unselect_all_clicked (GtkButton *button,
GtkTreeView *tree_view)
{
GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc)
glade_editor_reset_foreach_selection,
GINT_TO_POINTER (FALSE));
}
static gboolean
glade_editor_reset_accumulate_selected_props (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
GList **accum)
{
GladeProperty *property;
gboolean enabled, def;
gtk_tree_model_get (model, iter,
COLUMN_PROPERTY, &property,
COLUMN_ENABLED, &enabled,
COLUMN_DEFAULT, &def,
-1);
if (property && enabled && !def)
*accum = g_list_prepend (*accum, property);
if (property)
g_object_unref (G_OBJECT (property));
return FALSE;
}
static GList *
glade_editor_reset_get_selected_props (GtkTreeModel *model)
{
GList *ret = NULL;
gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc)
glade_editor_reset_accumulate_selected_props, &ret);
return ret;
}
static void
glade_editor_reset_properties (GList *props)
{
GList *list, *sdata_list = NULL;
GCSetPropData *sdata;
GladeProperty *prop;
GladeProject *project = NULL;
for (list = props; list; list = list->next)
{
prop = list->data;
project = glade_widget_get_project (prop->widget);
sdata = g_new (GCSetPropData, 1);
sdata->property = prop;
sdata->old_value = g_new0 (GValue, 1);
sdata->new_value = g_new0 (GValue, 1);
glade_property_get_value (prop, sdata->old_value);
glade_property_get_default (prop, sdata->new_value);
sdata_list = g_list_prepend (sdata_list, sdata);
}
if (project)
/* GladeCommand takes ownership of allocated list, ugly but practicle */
glade_command_set_properties_list (project, sdata_list);
}
static void
glade_editor_reset_dialog (GladeEditor *editor)
{
GtkTreeSelection *selection;
GtkWidget *dialog, *parent;
GtkWidget *vbox, *hbox, *label, *sw, *button;
GtkWidget *tree_view, *description_view;
gint res;
GList *list;
parent = gtk_widget_get_toplevel (GTK_WIDGET (editor));
dialog = gtk_dialog_new_with_buttons (_("Reset Widget Properties"),
GTK_WINDOW (parent),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
vbox = gtk_vbox_new (FALSE, 6);
gtk_widget_show (vbox);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
/* Checklist */
label = gtk_label_new_with_mnemonic (_("_Properties:"));
gtk_widget_show (label);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (sw);
gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
gtk_widget_set_size_request (sw, 400, 200);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
tree_view = glade_editor_reset_view (editor);
if (editor->loaded_widget)
glade_editor_populate_reset_view (editor, GTK_TREE_VIEW (tree_view));
gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
gtk_widget_show (tree_view);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), tree_view);
gtk_container_add (GTK_CONTAINER (sw), tree_view);
/* Select all / Unselect all */
hbox = gtk_hbutton_box_new ();
gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
gtk_widget_show (hbox);
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
button = gtk_button_new_with_mnemonic (_("_Select All"));
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (button), 6);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (glade_editor_reset_select_all_clicked), tree_view);
button = gtk_button_new_with_mnemonic (_("_Unselect All"));
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (button), 6);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (glade_editor_reset_unselect_all_clicked), tree_view);
/* Description */
label = gtk_label_new_with_mnemonic (_("Property _Description:"));
gtk_widget_show (label);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (sw);
gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
gtk_widget_set_size_request (sw, 400, 80);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
description_view = gtk_text_view_new ();
gtk_text_view_set_editable (GTK_TEXT_VIEW (description_view), FALSE);
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (description_view), GTK_WRAP_WORD);
gtk_widget_show (description_view);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), description_view);
gtk_container_add (GTK_CONTAINER (sw), description_view);
/* Update description */
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
g_signal_connect (G_OBJECT (selection), "changed",
G_CALLBACK (glade_editor_reset_selection_changed_cb),
description_view);
/* Run the dialog */
res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_OK) {
/* get all selected properties and reset properties through glade_command */
if ((list = glade_editor_reset_get_selected_props
(gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)))) != NULL)
{
glade_editor_reset_properties (list);
g_list_free (list);
}
}
gtk_widget_destroy (dialog);
}
void
glade_editor_show_info (GladeEditor *editor)
{
g_return_if_fail (GLADE_IS_EDITOR (editor));
if (editor->show_info != TRUE)
{
editor->show_info = TRUE;
gtk_widget_show (editor->info_button);
g_object_notify (G_OBJECT (editor), "show-info");
}
}
void
glade_editor_hide_info (GladeEditor *editor)
{
g_return_if_fail (GLADE_IS_EDITOR (editor));
if (editor->show_info != FALSE)
{
editor->show_info = FALSE;
gtk_widget_hide (editor->info_button);
g_object_notify (G_OBJECT (editor), "show-info");
}
}
void
glade_editor_show_context_info (GladeEditor *editor)
{
GList *list, *props;
GladeEditorTable *etable;
g_return_if_fail (GLADE_IS_EDITOR (editor));
if (editor->show_context_info != TRUE)
{
editor->show_context_info = TRUE;
for (list = editor->widget_tables; list; list = list->next)
{
etable = list->data;
for (props = etable->properties; props; props = props->next)
glade_editor_property_show_info
(GLADE_EDITOR_PROPERTY (props->data));
}
if (editor->packing_etable)
{
etable = editor->packing_etable;
for (props = etable->properties; props; props = props->next)
glade_editor_property_show_info
(GLADE_EDITOR_PROPERTY (props->data));
}
g_object_notify (G_OBJECT (editor), "show-context-info");
}
}
void
glade_editor_hide_context_info (GladeEditor *editor)
{
GList *list, *props;
GladeEditorTable *etable;
g_return_if_fail (GLADE_IS_EDITOR (editor));
if (editor->show_context_info != FALSE)
{
editor->show_context_info = FALSE;
for (list = editor->widget_tables; list; list = list->next)
{
etable = list->data;
for (props = etable->properties; props; props = props->next)
glade_editor_property_hide_info
(GLADE_EDITOR_PROPERTY (props->data));
}
if (editor->packing_etable)
{
etable = editor->packing_etable;
for (props = etable->properties; props; props = props->next)
glade_editor_property_hide_info
(GLADE_EDITOR_PROPERTY (props->data));
}
g_object_notify (G_OBJECT (editor), "show-context-info");
}
}
syntax highlighted by Code2HTML, v. 0.9.1