diff options
Diffstat (limited to 'tools/glade/glade/property.c')
-rw-r--r-- | tools/glade/glade/property.c | 5672 |
1 files changed, 5672 insertions, 0 deletions
diff --git a/tools/glade/glade/property.c b/tools/glade/glade/property.c new file mode 100644 index 00000000..5c21d5ec --- /dev/null +++ b/tools/glade/glade/property.c @@ -0,0 +1,5672 @@ +/* Gtk+ User Interface Builder + * Copyright (C) 1998 Damon Chaplin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include "gladeconfig.h" + +#ifdef USE_GNOME +#include <gnome.h> +#endif + +#include "editor.h" +#include "glade_atk.h" +#include "glade_keys_dialog.h" +#include "glade_project.h" +#include "glade_project_window.h" +#include "utils.h" +#include "gbwidget.h" +#include "property.h" +#include "load.h" +#include "save.h" + +/* These are the standard widget attribute names */ +const gchar *GbName = "GtkWidget::name"; +const gchar *GbClass = "GtkWidget::class"; +const gchar *GbWidth = "GtkWidget::width_request"; +const gchar *GbHeight = "GtkWidget::height_request"; +const gchar *GbVisible = "GtkWidget::visible"; +const gchar *GbSensitive = "GtkWidget::sensitive"; +const gchar *GbTooltip = "GtkWidget::tooltip"; +const gchar *GbCanDefault = "GtkWidget::can_default"; +const gchar *GbHasDefault = "GtkWidget::has_default"; +const gchar *GbCanFocus = "GtkWidget::can_focus"; +const gchar *GbHasFocus = "GtkWidget::has_focus"; +const gchar *GbEvents = "GtkWidget::events"; +const gchar *GbExtEvents = "GtkWidget::extension_events"; + +/* This is just a button to show the accelerators dialog. */ +const gchar *GbAccelerators = "GtkWidget::accelerators"; + +/* Language-Specific properties. */ +/* C-specific properties. */ +const gchar *GbCSourceFile = "GtkWidget::c_source_file"; +const gchar *GbCPublic = "GtkWidget::c_public"; + +/* C++-specific properties. */ +const gchar *GbCxxSeparateFile = "GtkWidget::cxx_separate_file"; +const gchar *GbCxxSeparateClass = "GtkWidget::cxx_separate_class"; +const gchar *GbCxxVisibility = "GtkWidget::cxx_visibility"; + +/* Visibility choices data */ +const gchar *GbCxxVisibilityChoices[] = +{N_("private"), N_("protected"), N_("public"), NULL}; +const gint GbCxxVisibilityValues[] = +{ 0, 1, 2 +}; +const gchar *GbCxxVisibilitySymbols[] = +{ + "private", + "protected", + "public" +}; + +/* Widget style properties. */ +const gchar *GbStylePropagate = "GtkWidget::style_propagate"; +const gchar *GbStyleName = "GtkWidget::style_name"; +const gchar *GbStyleFont = "GtkWidget::style_font"; + +/* Signals page */ +const gchar *GbSignalName = "GtkWidget::signal_name"; +const gchar *GbSignalHandler = "GtkWidget::signal_handler"; +const gchar *GbSignalObject = "GtkWidget::signal_object"; +const gchar *GbSignalAfter = "GtkWidget::signal_after"; +const gchar *GbSignalData = "GtkWidget::signal_data"; + +/* Accelerators page */ +const gchar *GbAccelKey = "GtkWidget::accelerator_key"; +const gchar *GbAccelSignal = "GtkWidget::accelerator_signal"; + + +const gchar *GbStateTabLabels[] = +{ N_("Normal"), N_("Active"), N_("Prelight"), + N_("Selected"), N_("Insens") }; + +/* Keys in object data hash used to store color, bg pixmap & filename */ +const gchar *GbColorKey = "GbColor"; +const gchar *GbBgPixmapKey = "GbBgPixmap"; +const gchar *GbBgFilenameKey = "GbBgFilename"; +const gchar *GbValueWidgetKey = "GbValue"; +const gchar *GbDialogValueKey = "GbDialogValue"; +const gchar *GbFilenameValueKey = "GbFilenameValue"; +const gchar *GbFontKey = "GbFont"; +const gchar *GbFontSpecKey = "GbFontSpec"; +const gchar *GladeStockIDKey = "GladeStockID"; + +/* Extension mode choices data */ +const gchar *GbExtensionModeChoices[] = +{"None", "All", "Cursor", NULL}; +const gint GbExtensionModeValues[] = +{ + GDK_EXTENSION_EVENTS_NONE, + GDK_EXTENSION_EVENTS_ALL, + GDK_EXTENSION_EVENTS_CURSOR +}; +const gchar *GbExtensionModeSymbols[] = +{ + "GDK_EXTENSION_EVENTS_NONE", + "GDK_EXTENSION_EVENTS_ALL", + "GDK_EXTENSION_EVENTS_CURSOR" +}; + +/* + * Private variables + */ + +/* Hashtables of all the label & value widgets on the various property pages */ +static GHashTable *gb_property_labels = NULL; +static GHashTable *gb_property_values = NULL; +static GHashTable *gb_property_buttons = NULL; + +/* Major components of the property window */ +#define GB_PAGE_WIDGET 0 +#define GB_PAGE_SIGNALS 3 +#define GB_PAGE_ATK 4 +GtkWidget *win_property = NULL; +static GtkWidget *main_notebook = NULL; +static GtkWidget *property_widget_notebook; +static gint property_language = GLADE_LANGUAGE_C; +static GtkWidget **lang_specific_properties; +static GtkWidget *special_child_property_notebook; +static GtkWidget *apply_button = NULL; +#ifdef GLADE_STYLE_SUPPORT +static GtkWidget *styles_notebook; +#endif + +static GtkTooltips *tooltips; + +/* The widget whose properties are currently shown (or NULL) */ +static GtkWidget *property_widget = NULL; + +/* This is used when typing over a widget in the interface to set its label. */ +static gboolean typing_over_widget = FALSE; + +static GtkStyle *invalid_style; + +/* The current table & row used when creating properties */ +static GtkWidget *property_table; +static gint property_table_row; + +/* This is used when automatically applying properties as they are changed. + It is on most of the time, except when changes are being made to property + values which we don't want to result in a callback. */ +static gboolean auto_apply; + +/* Color selection dialog */ +static GtkColorSelectionDialog *colorsel = NULL; +static GtkWidget *color_value = NULL; + +/* File selection dialog */ +static GtkWidget *filesel = NULL; +static GtkWidget *filename_value = NULL; + +/* Font selection fialog */ +static GtkFontSelectionDialog *fontsel = NULL; + +/* Widgets in Accelerators dialog. */ +#define ACCEL_MODIFIERS_COL 0 +#define ACCEL_KEY_COL 1 +#define ACCEL_SIGNAL_COL 2 +static GtkWidget *accel_dialog = NULL; +static GtkWidget *accel_clist; +static GtkWidget *accel_control_button; +static GtkWidget *accel_shift_button; +static GtkWidget *accel_alt_button; + +/* Widgets on Signals page */ +#define SIGNAL_NAME_COL 0 +#define SIGNAL_HANDLER_COL 1 +#define SIGNAL_DATA_COL 2 +#define SIGNAL_AFTER_COL 3 +#define SIGNAL_OBJECT_COL 4 +static GtkWidget *signal_clist; +/* This holds the last modification time of the GladeSignals currently being + edited. */ +static GMemChunk *signal_mem_chunk; + +/* Clipboard used for copying/pasting colors - could possibly use GTK clipbd? */ +#define GB_COLOR_CLIPBD_EMPTY 1 +#define GB_COLOR_CLIPBD_COLOR 2 +#define GB_COLOR_CLIPBD_BGPIXMAP 3 +#define GB_COLOR_CLIPBD_STATE 4 + +#ifdef GLADE_STYLE_SUPPORT +static gint clipbd_state = GB_COLOR_CLIPBD_EMPTY; +static GdkColor clipbd_colors[GB_NUM_STYLE_COLORS]; +static GdkPixmap *clipbd_bgpixmap; +static gchar *clipbd_bgfilename; +static GtkWidget *selected_style_widget = NULL; +#endif + + +/* The list of GTK+ stock items. */ +static GSList *stock_items = NULL; +static GHashTable *stock_nsizes_hash = NULL; +static GHashTable *stock_sizes_hash = NULL; + +/* Keys used in the GtkCombo value for stock items/icons. */ +static const gchar *GladeIconSizeKey = "GladeIconSizeKey"; +static const gchar *GladeNeedLabelKey = "GladeNeedLabelKey"; + + +static const gchar *GladeIsStringPropertyKey = "GladeIsStringPropertyKey"; +static const gchar *GladeShowTranslationPropertiesKey = "GladeShowTranslationPropertiesKey"; + +/* + * Private functions + */ + +static GtkWidget *create_widget_property_page (); +static GtkWidget *create_standard_property_page (); +static GtkWidget *create_special_child_properties (); +static void create_language_specific_properties (GtkWidget *vbox); +#ifdef GLADE_STYLE_SUPPORT +static GtkWidget *create_style_property_page (); +static GtkWidget *create_style_page (const gchar * state); +#endif +static GtkWidget *create_signals_property_page (); + +static const gchar* property_add (const gchar * property_name, + const gchar * label_string, + GtkWidget * value, + GtkWidget * dialog_button, + const gchar * tooltip); + +static void on_bool_property_toggle (GtkWidget * value, + gpointer data); + +static GtkWidget *create_color_preview (); +static gboolean show_color_in_preview (GtkWidget * preview, + GdkColor *color); +static void on_color_draw (GtkWidget * widget, + gpointer data); +static void on_color_expose_event (GtkWidget * widget, + GdkEvent * event, + gpointer data); +static void on_color_select (GtkWidget * widget, + gpointer data); + +static void show_pixmap_in_drawing_area (GtkWidget * drawing_area, + GdkPixmap * gdkpixmap); + +static gint expose_pixmap (GtkWidget * drawing_area, + GdkEventExpose * event, + gpointer data); + +static void show_events_dialog (GtkWidget * widget, + gpointer value); +static void on_events_dialog_response (GtkWidget * widget, + gint response_id, + GtkWidget * clist); + +static void show_keys_dialog (GtkWidget * widget, + gpointer value); +static void on_keys_clist_select (GtkWidget * widget, + gint row, + gint column, + GdkEventButton * bevent, + gpointer data); +static void on_keys_dialog_response (GtkWidget * widget, + gint response_id, + gpointer data); + +static void show_signals_dialog (GtkWidget * widget, + gpointer value); +static void on_signals_clist_select (GtkWidget * widget, + gint row, + gint column, + GdkEventButton * bevent, + gpointer data); +static void on_signals_dialog_response (GtkWidget * widget, + gint response_id, + GtkWidget * clist); + +static void show_accelerators_dialog (GtkWidget * widget, + gpointer value); +static void create_accelerators_dialog (); +static void on_accelerator_add (GtkWidget * widget, + GtkWidget * clist); +static void on_accelerator_update (GtkWidget * widget, + GtkWidget * clist); +static void on_accelerator_delete (GtkWidget * widget, + GtkWidget * clist); +static void on_accelerator_clear (GtkWidget * widget, + GtkWidget * clist); +static void on_accelerator_select (GtkWidget * clist, + gint row, + gint column, + GdkEventButton * event, + gpointer user_data); + +static void on_signal_add (GtkWidget * widget, + GtkWidget * clist); +static void on_signal_update (GtkWidget * widget, + GtkWidget * clist); +static void on_signal_delete (GtkWidget * widget, + GtkWidget * clist); +static void on_signal_clear (GtkWidget * widget, + GtkWidget * clist); +static void on_signal_select (GtkWidget * clist, + gint row, + gint column, + GdkEventButton * event, + gpointer user_data); + +#ifdef GLADE_STYLE_SUPPORT +static void on_style_copy (GtkWidget * widget, + gpointer data); +static void on_style_copy_all (GtkWidget * widget, + gpointer data); +static void on_style_paste (GtkWidget * widget, + gpointer data); +#endif + +/* Currently unused - its all auto-applied + static void on_apply (GtkWidget *widget, + gpointer data); + */ +static void on_property_changed (GtkWidget * widget, + GtkWidget * property); +static gboolean on_property_focus_out (GtkWidget * widget, + GdkEventFocus *event, + GtkWidget * property); + +static void set_pixmap (GtkWidget * drawing_area, + GdkPixmap * gdkpixmap, + const gchar * filename); + +static void show_colorsel_dialog (GtkWidget * widget, + gpointer data); +static void on_colorsel_dialog_ok (GtkWidget * widget, + gpointer data); +static gint close_dialog_event (GtkWidget * widget, + GdkEvent * event, + GtkWidget * dialog); +static void close_dialog (GtkWidget * widget, + GtkWidget * dialog); + +static void show_filesel_dialog (GtkWidget * widget, + gpointer data); +static void on_filesel_response (GtkWidget * widget, + gint response_id, + gpointer data); + +static void show_font_dialog (GtkWidget * widget, + gpointer data); +#if 0 +static gint get_font_size_from_spec (const gchar * spec); +#endif +static gchar *get_font_name_from_spec (const gchar * spec); +static void on_font_dialog_apply (GtkWidget * widget, + GtkFontSelectionDialog * fontsel); +static void on_font_dialog_ok (GtkWidget * widget, + GtkFontSelectionDialog * fontsel); + +#ifdef GLADE_STYLE_SUPPORT +static void show_style_dialog (GtkWidget * widget, + gpointer value); +static gint add_style_to_clist (const gchar * key, + gpointer data, + GtkWidget * clist); +static void on_style_clist_select (GtkWidget * widget, + gint row, + gint column, + GdkEventButton * bevent, + gpointer data); +static void on_style_dialog_new (GtkWidget * widget, + GtkWidget * clist); +static gint create_new_style (GtkWidget * widget, + const gchar * name, + GbStyle * base_gbstyle); +static void on_style_dialog_ok (GtkWidget * widget, + GtkWidget * clist); +static void on_style_dialog_copy (GtkWidget * widget, + GtkWidget * clist); +static void on_style_dialog_rename (GtkWidget * widget, + GtkWidget * clist); +static gint rename_style (GtkWidget * widget, + const gchar * name, + GbStyle * gbstyle); +static void on_style_dialog_delete (GtkWidget * widget, + GtkWidget * clist); +#endif + +static void on_toggle_set_width (GtkWidget * widget, gpointer value); +static void on_toggle_set_height (GtkWidget * widget, gpointer value); + +const gchar *GbEventMaskSymbols[GB_EVENT_MASKS_COUNT] = +{ + "GDK_EXPOSURE_MASK", + "GDK_POINTER_MOTION_MASK", + "GDK_POINTER_MOTION_HINT_MASK", + "GDK_BUTTON_MOTION_MASK", + "GDK_BUTTON1_MOTION_MASK", + "GDK_BUTTON2_MOTION_MASK", + "GDK_BUTTON3_MOTION_MASK", + "GDK_BUTTON_PRESS_MASK", + "GDK_BUTTON_RELEASE_MASK", + "GDK_KEY_PRESS_MASK", + "GDK_KEY_RELEASE_MASK", + "GDK_ENTER_NOTIFY_MASK", + "GDK_LEAVE_NOTIFY_MASK", + "GDK_FOCUS_CHANGE_MASK", + "GDK_STRUCTURE_MASK", + "GDK_PROPERTY_CHANGE_MASK", + "GDK_VISIBILITY_NOTIFY_MASK", + "GDK_PROXIMITY_IN_MASK", + "GDK_PROXIMITY_OUT_MASK", +}; + + +const gint GbEventMaskValues[GB_EVENT_MASKS_COUNT] = +{ + GDK_EXPOSURE_MASK, + GDK_POINTER_MOTION_MASK, + GDK_POINTER_MOTION_HINT_MASK, + GDK_BUTTON_MOTION_MASK, + GDK_BUTTON1_MOTION_MASK, + GDK_BUTTON2_MOTION_MASK, + GDK_BUTTON3_MOTION_MASK, + GDK_BUTTON_PRESS_MASK, + GDK_BUTTON_RELEASE_MASK, + GDK_KEY_PRESS_MASK, + GDK_KEY_RELEASE_MASK, + GDK_ENTER_NOTIFY_MASK, + GDK_LEAVE_NOTIFY_MASK, + GDK_FOCUS_CHANGE_MASK, + GDK_STRUCTURE_MASK, + GDK_PROPERTY_CHANGE_MASK, + GDK_VISIBILITY_NOTIFY_MASK, + GDK_PROXIMITY_IN_MASK, + GDK_PROXIMITY_OUT_MASK +}; + + +static const gchar *GbEventMaskDescriptions[GB_EVENT_MASKS_COUNT] = +{ + N_("When the window needs redrawing"), + N_("When the mouse moves"), + N_("Mouse movement hints"), + N_("Mouse movement with any button pressed"), + N_("Mouse movement with button 1 pressed"), + N_("Mouse movement with button 2 pressed"), + N_("Mouse movement with button 3 pressed"), + N_("Any mouse button pressed"), + N_("Any mouse button released"), + N_("Any key pressed"), + N_("Any key released"), + N_("When the mouse enters the window"), + N_("When the mouse leaves the window"), + N_("Any change in input focus"), + N_("Any change in window structure"), + N_("Any change in X Windows property"), + N_("Any change in visibility"), + N_("For cursors in XInput-aware programs"), + N_("For cursors in XInput-aware programs") +}; + +/* + * Create the properties window + */ + + +static void +create_stock_items (void) +{ + GSList *elem; + + stock_items = gtk_stock_list_ids (); + stock_items = g_slist_sort (stock_items, glade_util_compare_stock_labels); + + /* We also keep data about which icon sizes are available for each stock + item, in a GHashTable. The key is the stock_id. */ + stock_nsizes_hash = g_hash_table_new (g_str_hash, g_str_equal); + stock_sizes_hash = g_hash_table_new (g_str_hash, g_str_equal); + + for (elem = stock_items; elem; elem = elem->next) + { + gchar *stock_id; + GtkIconSet *icon_set; + GtkIconSize *sizes; + gint n_sizes; +#if 0 + gint i; +#endif + + stock_id = elem->data; + icon_set = gtk_icon_factory_lookup_default (stock_id); + if (icon_set) + { + gtk_icon_set_get_sizes (icon_set, &sizes, &n_sizes); + +#if 0 + g_print ("Stock ID %s Icon Set: %p Sizes:", stock_id, icon_set); + for (i = 0; i < n_sizes; i++) + g_print (" %i", sizes[i]); + g_print ("\n"); +#endif + + /* Note that we use the stock_id as the keys, without copying it, + so they are only valid while stock_items exists. */ + g_hash_table_insert (stock_nsizes_hash, stock_id, + GINT_TO_POINTER (n_sizes)); + g_hash_table_insert (stock_sizes_hash, stock_id, sizes); + } + } +} + + +static gboolean +propagate_key_press_to_main_notebook (GtkWidget *notebook, + GdkEventKey *event, + gpointer data) +{ + + if (event->state & GDK_CONTROL_MASK) + { + if (event->keyval == GDK_Page_Up) + { + gtk_signal_emit_by_name (GTK_OBJECT (main_notebook), + "change_current_page", -1); + return TRUE; + } + else if (event->keyval == GDK_Page_Down) + { + gtk_signal_emit_by_name (GTK_OBJECT (main_notebook), + "change_current_page", 1); + return TRUE; + } + } + + return FALSE; +} + + +void +property_init () +{ + GtkWidget *vbox1, *label, *child, *page; + + /* Create hash table for widgets containing property values */ + gb_property_labels = g_hash_table_new (g_str_hash, g_str_equal); + gb_property_values = g_hash_table_new (g_str_hash, g_str_equal); + gb_property_buttons = g_hash_table_new (g_str_hash, g_str_equal); + + /* Create the mem chunk for storing signal last modification times. */ + signal_mem_chunk = g_mem_chunk_create (time_t, 16, G_ALLOC_ONLY); + + /* Create property window */ + win_property = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_move (GTK_WINDOW (win_property), 300, 0); + gtk_window_set_default_size (GTK_WINDOW (win_property), 260, -1); + gtk_widget_set_name (win_property, "GladeObjectInspector"); + gtk_window_set_wmclass (GTK_WINDOW (win_property), + "object_inspector", "Glade"); + gtk_widget_realize (win_property); + gtk_window_add_accel_group (GTK_WINDOW (win_property), + glade_get_global_accel_group ()); + + gtk_signal_connect (GTK_OBJECT (win_property), "delete_event", + GTK_SIGNAL_FUNC (property_hide), NULL); + + gtk_signal_connect_after (GTK_OBJECT (win_property), "hide", + GTK_SIGNAL_FUNC (glade_project_window_uncheck_property_editor_menu_item), + NULL); + + gtk_window_set_title (GTK_WINDOW (win_property), _("Properties")); + gtk_container_set_border_width (GTK_CONTAINER (win_property), 0); + + tooltips = gtk_tooltips_new (); + + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (win_property), vbox1); + gtk_widget_show (vbox1); + + /* Create main notebook for different categories of widget properties */ + main_notebook = gtk_notebook_new (); + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (main_notebook), GTK_POS_TOP); + gtk_notebook_set_tab_vborder (GTK_NOTEBOOK (main_notebook), 0); + gtk_box_pack_start (GTK_BOX (vbox1), main_notebook, TRUE, TRUE, 0); + gtk_widget_show (main_notebook); + + /* Create the pages of the main notebook */ + /* NOTE: If you add/remove pages you need to change the GB_PAGE_SIGNALS + value at the top of this file */ + label = gtk_label_new (_("Widget")); + gtk_widget_show (label); + child = create_widget_property_page (); + gtk_notebook_append_page (GTK_NOTEBOOK (main_notebook), child, label); + + label = gtk_label_new (_("Packing")); + gtk_widget_show (label); + child = create_special_child_properties (); + gtk_notebook_append_page (GTK_NOTEBOOK (main_notebook), child, label); + + label = gtk_label_new (_("Common")); + gtk_widget_show (label); + child = create_standard_property_page (); + gtk_notebook_append_page (GTK_NOTEBOOK (main_notebook), child, label); + +#ifdef GLADE_STYLE_SUPPORT + label = gtk_label_new (_("Style")); + gtk_widget_show (label); + child = create_style_property_page (); + gtk_notebook_append_page (GTK_NOTEBOOK (main_notebook), child, label); +#endif + + label = gtk_label_new (_("Signals")); + gtk_widget_show (label); + child = create_signals_property_page (); + gtk_notebook_append_page (GTK_NOTEBOOK (main_notebook), child, label); + + /* Create style to use when a property value is invalid - it's not used + at present. */ + invalid_style = gtk_style_new (); + invalid_style->fg[GTK_STATE_NORMAL].red = 65535; + invalid_style->fg[GTK_STATE_NORMAL].green = 0; + invalid_style->fg[GTK_STATE_NORMAL].blue = 0; + + create_accelerators_dialog (); + + create_stock_items (); + + /* Create the ATK properties page. */ + glade_atk_create_property_page (GTK_NOTEBOOK (main_notebook)); + + /* Make sure Ctrl+PgUp/Down switch the main notebook page. */ + page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (main_notebook), + GB_PAGE_ATK); + gtk_signal_connect (GTK_OBJECT (GTK_BIN (page)->child), + "key_press_event", + GTK_SIGNAL_FUNC (propagate_key_press_to_main_notebook), NULL); +} + + +void +property_show (GtkWidget * widget, + gpointer data) +{ + gtk_widget_show (win_property); + /* This maps the window, which also de-iconifies it according to ICCCM. */ + gdk_window_show (GTK_WIDGET (win_property)->window); + gdk_window_raise (GTK_WIDGET (win_property)->window); + property_set_widget (NULL); +} + +gint +property_hide (GtkWidget * widget, + gpointer data) +{ + gtk_widget_hide (win_property); + return TRUE; +} + + +GtkWidget * +property_get_widget () +{ + return property_widget; +} + + +void +property_update_title (void) +{ + gchar buffer[128]; + gchar *name; + + if (property_widget) + { + strcpy (buffer, _("Properties: ")); + name = (char*) gtk_widget_get_name (property_widget); + strncat (buffer, name, 120 - strlen (buffer)); + gtk_window_set_title (GTK_WINDOW (win_property), buffer); + } + else + { + gtk_window_set_title (GTK_WINDOW (win_property), + _("Properties: <none>")); + } +} + + +void +property_set_widget (GtkWidget * widget) +{ + gchar buffer[128]; + gchar *name; + + if (widget) + { + strcpy (buffer, _("Properties: ")); + name = (char*) gtk_widget_get_name (widget); + strncat (buffer, name, 120 - strlen (buffer)); + gtk_window_set_title (GTK_WINDOW (win_property), buffer); + gtk_widget_set_sensitive (main_notebook, TRUE); + if (apply_button) + gtk_widget_set_sensitive (apply_button, TRUE); + property_widget = widget; + } + else + { + gtk_window_set_title (GTK_WINDOW (win_property), _("Properties: <none>")); + property_hide_gbwidget_page (); + gtk_widget_set_sensitive (main_notebook, FALSE); + if (apply_button) + gtk_widget_set_sensitive (apply_button, FALSE); + /* Need to clear the property widget before clearing the name property, + since auto-apply may be on, and hence we may clear the name of the + currently shown widget. */ + property_widget = NULL; + property_set_string (GbName, NULL); + property_set_string (GbClass, NULL); + /* Make now-empty property window display properly */ + gtk_widget_queue_resize (win_property); + } + + typing_over_widget = FALSE; + glade_atk_update_relation_dialogs (); +} + + +static GtkWidget * +create_widget_property_page () +{ + GtkWidget *page, *table, *vbox, *entry; + + page = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (page), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_widget_show (page); + gtk_signal_connect (GTK_OBJECT (page), + "key_press_event", + GTK_SIGNAL_FUNC (propagate_key_press_to_main_notebook), NULL); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (page), vbox); + gtk_viewport_set_shadow_type (GTK_VIEWPORT (GTK_BIN (page)->child), + GTK_SHADOW_NONE); + gtk_widget_show (vbox); + + table = gtk_table_new (2, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 1); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0); + + property_set_table_position (table, 0); + property_add_string (GbName, _("Name:"), + _("The name of the widget")); + property_add_string (GbClass, _("Class:"), + _("The class of the widget")); + entry = property_get_value_widget (GbClass); + gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE); + + /* Add language-specific options to a notebook at the bottom. */ + create_language_specific_properties (vbox); + + gtk_widget_show (table); + + property_widget_notebook = gtk_notebook_new (); + gtk_widget_show (property_widget_notebook); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (property_widget_notebook), + FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (property_widget_notebook), + FALSE); + gtk_box_pack_start (GTK_BOX (vbox), property_widget_notebook, + FALSE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (property_widget_notebook), + "key_press_event", + GTK_SIGNAL_FUNC (propagate_key_press_to_main_notebook), NULL); + + return page; +} + + +static GtkWidget * +create_standard_property_page () +{ + GtkWidget *page; + + page = gtk_table_new (9, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (page), 1); + + property_set_table_position (page, 0); + property_add_optional_int_range (GbWidth, _("Width:"), + _("The requested width of the widget (usually used to set the minimum width)"), + 0, 10000, 1, 10, 1, on_toggle_set_width); + property_add_optional_int_range (GbHeight, _("Height:"), + _("The requested height of the widget (usually used to set the minimum height)"), + 0, 10000, 1, 10, 1, on_toggle_set_height); + + property_add_bool (GbVisible, _("Visible:"), + _("If the widget is initially visible")); + property_add_bool (GbSensitive, _("Sensitive:"), + _("If the widget responds to input")); + property_add_string (GbTooltip, _("Tooltip:"), + _("The tooltip to display if the mouse lingers over the widget")); + + property_add_bool (GbCanDefault, _("Can Default:"), + _("If the widget can be the default action in a dialog")); + property_add_bool (GbHasDefault, _("Has Default:"), + _("If the widget is the default action in the dialog")); + property_add_bool (GbCanFocus, _("Can Focus:"), + _("If the widget can accept the input focus")); + property_add_bool (GbHasFocus, _("Has Focus:"), + _("If the widget has the input focus")); + + property_add_dialog (GbEvents, _("Events:"), + _("The X events that the widget receives"), + FALSE, show_events_dialog); + property_add_choice (GbExtEvents, _("Ext.Events:"), + _("The X Extension events mode"), + GbExtensionModeChoices); + + property_add_command (GbAccelerators, _("Accelerators:"), + _("Defines the signals to emit when keys are pressed"), + _("Edit..."), + GTK_SIGNAL_FUNC (show_accelerators_dialog)); + + gtk_widget_show (page); + return page; +} + + +#ifdef GLADE_STYLE_SUPPORT +static GtkWidget * +create_style_property_page () +{ + GtkWidget *page, *table, *label, *child, *hbox, *button; + int i; + + page = gtk_vbox_new (FALSE, 0); + + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 1); + gtk_box_pack_start (GTK_BOX (page), table, FALSE, TRUE, 0); + + property_set_table_position (table, 0); + property_add_bool (GbStylePropagate, _("Propagate:"), + _("Set True to propagate the style to the widget's children")); + property_add_dialog (GbStyleName, _("Named Style:"), + _("The name of the style, which can be shared by several widgets"), + FALSE, show_style_dialog); + property_add_font (GbStyleFont, _("Font:"), + _("The font to use for any text in the widget")); + + /* Create notebook for 5 states */ + styles_notebook = gtk_notebook_new (); + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (styles_notebook), GTK_POS_TOP); + gtk_box_pack_start (GTK_BOX (page), styles_notebook, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (styles_notebook), 0); + + for (i = 0; i < GB_NUM_STYLE_STATES; i++) + { + label = gtk_label_new (_(GbStateTabLabels[i])); + gtk_widget_show (label); + child = create_style_page (GbStateNames[i]); + gtk_notebook_append_page (GTK_NOTEBOOK (styles_notebook), child, label); + } + gtk_widget_show (styles_notebook); + + /* Add/Update/Delete buttons at bottom */ + hbox = gtk_hbox_new (TRUE, 5); + button = gtk_button_new_with_label (_("Copy")); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_style_copy), NULL); + + button = gtk_button_new_with_label (_("Copy All")); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_style_copy_all), NULL); + + button = gtk_button_new_with_label (_("Paste")); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_style_paste), NULL); + + gtk_box_pack_start (GTK_BOX (page), hbox, FALSE, TRUE, 3); + gtk_widget_show (hbox); + + gtk_widget_show (table); + gtk_widget_show (page); + return page; +} + + +static GtkWidget * +create_style_page (const gchar * state) +{ + GtkWidget *page, *table; + int i; + gchar buffer[128]; + gchar *labels[] = + {N_("Foreground:"), N_("Background:"), N_("Text:"), N_("Base:") }; + gchar *tooltips[] = + {N_("Foreground color"), N_("Background color"), N_("Text color"), + N_("Base color") }; + + page = gtk_vbox_new (FALSE, 0); + + /* Add the seven colours for this state */ + table = gtk_table_new (8, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 1); + gtk_box_pack_start (GTK_BOX (page), table, TRUE, TRUE, 0); + property_set_table_position (table, 0); + for (i = 0; i < GB_NUM_STYLE_COLORS; i++) + { + sprintf (buffer, "GtkStyle::%s[%s]", GbColorNames[i], state); + property_add_color (buffer, _(labels[i]), _(tooltips[i])); + } + + /* Add the background pixmap */ + sprintf (buffer, "GtkStyle::%s[%s]", GbBgPixmapName, state); + property_add_bgpixmap (buffer, _("Back. Pixmap:"), + _("The graphic to use as the background of the widget")); + + gtk_widget_show (table); + gtk_widget_show (page); + return page; +} +#endif + + +static GtkWidget * +create_special_child_properties () +{ + GtkWidget *page; + + page = gtk_vbox_new (FALSE, 0); + + special_child_property_notebook = gtk_notebook_new (); + gtk_widget_show (special_child_property_notebook); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (special_child_property_notebook), + FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (special_child_property_notebook), + FALSE); + gtk_box_pack_start (GTK_BOX (page), special_child_property_notebook, + TRUE, TRUE, 0); + gtk_widget_show (page); + gtk_signal_connect (GTK_OBJECT (special_child_property_notebook), + "key_press_event", + GTK_SIGNAL_FUNC (propagate_key_press_to_main_notebook), NULL); + return page; +} + + +static void +create_language_specific_properties (GtkWidget *vbox) +{ + GtkWidget *table; + gint i; + + lang_specific_properties = g_new (GtkWidget*, GladeNumLanguages); + for (i = 0; i < GladeNumLanguages; i++) + lang_specific_properties[i] = NULL; + + /* Create table for C-specific properties. */ + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 1); + if (property_language == GLADE_LANGUAGE_C) + gtk_widget_show (table); + lang_specific_properties[GLADE_LANGUAGE_C] = table; + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + + property_set_table_position (table, 0); + property_add_filename (GbCSourceFile, _("Source File:"), + _("The file to write source code into")); + property_add_bool (GbCPublic, _("Public:"), + _("If the widget is added to the component's data structure")); + + /* Create table for C++-specific properties. */ + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 1); + if (property_language == GLADE_LANGUAGE_CPP) + gtk_widget_show (table); + lang_specific_properties[GLADE_LANGUAGE_CPP] = table; + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + + property_set_table_position (table, 0); + property_add_bool (GbCxxSeparateClass, _("Separate Class:"), + _("Put this widget's subtree in a separate class")); + property_add_bool (GbCxxSeparateFile, _("Separate File:"), + _("Put this widget in a separate source file")); + property_add_choice (GbCxxVisibility, _("Visibility:"), + _("Visibility of widgets. Public widgets are exported to a global map."), + GbCxxVisibilityChoices); + + /* Create table for Ada95-specific properties. */ + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 1); + if (property_language == GLADE_LANGUAGE_ADA95) + gtk_widget_show (table); + lang_specific_properties[GLADE_LANGUAGE_ADA95] = table; + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + + property_set_table_position (table, 0); + /* No properties yet. */ + + + /* Create table for Perl-specific properties. */ +#if 0 + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 1); + if (property_language == GLADE_LANGUAGE_PERL) + gtk_widget_show (table); + lang_specific_properties[GLADE_LANGUAGE_PERL] = table; + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + + property_set_table_position (table, 0); + /* No properties yet. */ +#endif +} + + + +gint +property_add_gbwidget_page (GtkWidget * page) +{ + gtk_notebook_append_page (GTK_NOTEBOOK (property_widget_notebook), page, + NULL); + return g_list_length (GTK_NOTEBOOK (property_widget_notebook)->children) - 1; +} + + +void +property_hide_gbwidget_page () +{ + gtk_widget_hide (property_widget_notebook); +} + + +void +property_show_gbwidget_page (gint page) +{ + gtk_notebook_set_current_page (GTK_NOTEBOOK (property_widget_notebook), page); + gtk_widget_show (property_widget_notebook); +} + + +gint +property_add_child_packing_page (GtkWidget * page) +{ + gtk_notebook_append_page (GTK_NOTEBOOK (special_child_property_notebook), + page, NULL); + return g_list_length (GTK_NOTEBOOK (special_child_property_notebook)->children) - 1; +} + + +void +property_hide_child_packing_page () +{ + gtk_widget_hide (special_child_property_notebook); +} + + +void +property_show_child_packing_page (gint page) +{ + gtk_notebook_set_current_page (GTK_NOTEBOOK (special_child_property_notebook), page); + gtk_widget_show (special_child_property_notebook); +} + + +void +property_show_lang_specific_page (GladeLanguageType language) +{ + gint i; + + property_language = language; + if (win_property == NULL) + return; + + for (i = 0; i < GladeNumLanguages; i++) + { + if (lang_specific_properties[i]) + { + /* FIXME: show the C language properties when we support them. */ + if (i == language && i != GLADE_LANGUAGE_C) + gtk_widget_show (lang_specific_properties[i]); + else + gtk_widget_hide (lang_specific_properties[i]); + } + } +} + + +#ifdef GLADE_STYLE_SUPPORT +static void +on_style_copy (GtkWidget * widget, gpointer data) +{ + GdkColor *color; + + if (!selected_style_widget) + { + glade_util_show_message_box (_("You need to select a color or background to copy")); + return; + } + if ((color = gtk_object_get_data (GTK_OBJECT (selected_style_widget), + GbColorKey))) + { + clipbd_colors[0] = *color; + clipbd_state = GB_COLOR_CLIPBD_COLOR; + } + else if (GTK_IS_DRAWING_AREA (selected_style_widget)) + { + clipbd_bgpixmap = gtk_object_get_data (GTK_OBJECT (selected_style_widget), + GbBgPixmapKey); + clipbd_bgfilename = gtk_object_get_data (GTK_OBJECT (selected_style_widget), + GbBgFilenameKey); + clipbd_state = GB_COLOR_CLIPBD_BGPIXMAP; + } + else + { + g_warning (_("Invalid selection in on_style_copy()")); + } +} + + +static void +on_style_copy_all (GtkWidget * widget, gpointer data) +{ + gint page, i; + GdkColor *color; + gchar buffer[128]; + + /* Find out which state is currently displayed */ + page = gtk_notebook_get_current_page (GTK_NOTEBOOK (styles_notebook)); + + for (i = 0; i < GB_NUM_STYLE_COLORS; i++) + { + sprintf (buffer, "GtkStyle::%s[%s]", GbColorNames[i], + GbStateNames[page]); + color = property_get_color (buffer, NULL, NULL); + clipbd_colors[i] = *color; + } + sprintf (buffer, "GtkStyle::%s[%s]", GbBgPixmapName, GbStateNames[page]); + clipbd_bgpixmap = property_get_bgpixmap (buffer, NULL, NULL, + &clipbd_bgfilename); + + clipbd_state = GB_COLOR_CLIPBD_STATE; +} + + +static void +on_style_paste (GtkWidget * widget, gpointer data) +{ + gchar buffer[128]; + gint page, i; + GtkWidget *value, *preview, *drawing_area, *changed_value = NULL; + gboolean bgpixmap_changed; + gchar *filename; + + switch (clipbd_state) + { + case GB_COLOR_CLIPBD_EMPTY: + glade_util_show_message_box (_("You need to copy a color or background pixmap first")); + break; + case GB_COLOR_CLIPBD_COLOR: + if (!selected_style_widget + || !GTK_IS_DRAWING_AREA (selected_style_widget)) + { + glade_util_show_message_box (_("You need to select a color to paste into")); + return; + } + show_color_in_preview (selected_style_widget, &clipbd_colors[0]); + value = selected_style_widget->parent; + on_property_changed (value, value); + break; + case GB_COLOR_CLIPBD_BGPIXMAP: + if (!selected_style_widget || !GTK_IS_DRAWING_AREA (selected_style_widget)) + { + glade_util_show_message_box (_("You need to select a background pixmap to paste into")); + return; + } + set_pixmap (selected_style_widget, clipbd_bgpixmap, clipbd_bgfilename); + value = selected_style_widget->parent; + on_property_changed (value, value); + break; + case GB_COLOR_CLIPBD_STATE: + page = gtk_notebook_get_current_page (GTK_NOTEBOOK (styles_notebook)); + + /* We need to find one color or background pixmap which has changed so + we can call on_property_changed with it, thus recreating the style. + We don't want to call on_property_changed multiple times. */ + for (i = 0; i < GB_NUM_STYLE_COLORS; i++) + { + sprintf (buffer, "GtkStyle::%s[%s]", GbColorNames[i], + GbStateNames[page]); + value = (GtkWidget *) g_hash_table_lookup (gb_property_values, + buffer); + g_return_if_fail (value != NULL); + preview = GTK_BIN (value)->child; + g_return_if_fail (GTK_IS_DRAWING_AREA (preview)); + if (show_color_in_preview (preview, &clipbd_colors[i])) + changed_value = value; + } + + sprintf (buffer, "GtkStyle::%s[%s]", GbBgPixmapName, GbStateNames[page]); + value = (GtkWidget *) g_hash_table_lookup (gb_property_values, buffer); + g_return_if_fail (value != NULL); + drawing_area = GTK_BIN (value)->child; + g_return_if_fail (GTK_IS_DRAWING_AREA (drawing_area)); + filename = gtk_object_get_data (GTK_OBJECT (drawing_area), GbBgFilenameKey); + bgpixmap_changed = FALSE; + if (filename) + { + if (!clipbd_bgfilename || strcmp (filename, clipbd_bgfilename)) + bgpixmap_changed = TRUE; + } + else + { + if (clipbd_bgfilename) + bgpixmap_changed = TRUE; + } + if (bgpixmap_changed) + { + property_set_bgpixmap (buffer, clipbd_bgpixmap, clipbd_bgfilename); + changed_value = value; + } + + if (changed_value) + on_property_changed (changed_value, changed_value); + + break; + } +} +#endif + + +/* + * Color selection dialog + */ + +static void +show_colorsel_dialog (GtkWidget * widget, gpointer data) +{ + GdkColor *color; + gdouble rgb[4]; + GtkWidget *transient_parent ; + + /* Create the dialog if it doesn't exist yet */ + if (!colorsel) + { + colorsel = GTK_COLOR_SELECTION_DIALOG (gtk_color_selection_dialog_new (_("Color Selection Dialog"))); + /* Hide the Help button since it is not used */ + gtk_widget_hide (GTK_WIDGET (colorsel->help_button)); + gtk_signal_connect (GTK_OBJECT (colorsel), "delete_event", + GTK_SIGNAL_FUNC (close_dialog_event), colorsel); + gtk_signal_connect (GTK_OBJECT (colorsel->cancel_button), "clicked", + GTK_SIGNAL_FUNC (close_dialog), colorsel); + gtk_signal_connect (GTK_OBJECT (colorsel->ok_button), "clicked", + GTK_SIGNAL_FUNC (on_colorsel_dialog_ok), NULL); + gtk_signal_connect (GTK_OBJECT (colorsel), "key_press_event", + GTK_SIGNAL_FUNC (glade_util_check_key_is_esc), + GINT_TO_POINTER (GladeEscCloses)); + gtk_window_set_wmclass (GTK_WINDOW (colorsel), "color_selection", "Glade"); + } + + color_value = GTK_WIDGET (data); + g_return_if_fail (GTK_IS_FRAME (color_value)); + + color = gtk_object_get_data (GTK_OBJECT (GTK_BIN (color_value)->child), + GbColorKey); + g_return_if_fail (color != NULL); + + rgb[0] = ((gdouble) color->red) / 0xFFFF; + rgb[1] = ((gdouble) color->green) / 0xFFFF; + rgb[2] = ((gdouble) color->blue) / 0xFFFF; + + gtk_color_selection_set_color (GTK_COLOR_SELECTION (colorsel->colorsel), + rgb); + gtk_widget_show (GTK_WIDGET (colorsel)); + transient_parent = glade_util_get_toplevel (widget); + if (GTK_IS_WINDOW (transient_parent)) + gtk_window_set_transient_for (GTK_WINDOW (colorsel), + GTK_WINDOW (transient_parent)); + /* This maps the window, which also de-iconifies it according to ICCCM. */ + gdk_window_show (GTK_WIDGET (colorsel)->window); + gdk_window_raise (GTK_WIDGET (colorsel)->window); +} + + +static void +on_colorsel_dialog_ok (GtkWidget * widget, gpointer data) +{ + GdkColor color; + gdouble rgb[4]; + GtkWidget *preview = GTK_BIN (color_value)->child; + + gtk_color_selection_get_color (GTK_COLOR_SELECTION (colorsel->colorsel), + rgb); + + color.red = (gushort) 0xFFFF * rgb[0]; + color.green = (gushort) 0xFFFF * rgb[1]; + color.blue = (gushort) 0xFFFF * rgb[2]; + + show_color_in_preview (preview, &color); + close_dialog (widget, GTK_WIDGET (colorsel)); + on_property_changed (color_value, color_value); +} + + +static gint +close_dialog_event (GtkWidget * widget, GdkEvent * event, GtkWidget * dialog) +{ + close_dialog (widget, dialog); + return TRUE; +} + + +static void +close_dialog (GtkWidget * widget, GtkWidget * dialog) +{ + glade_util_close_window (dialog); +} + + +/* + * File selection dialog + */ +#define GLADE_RESPONSE_CLEAR 1 + +static void +show_filesel_dialog (GtkWidget * widget, gpointer data) +{ + gchar *filename; + /* value can be an Entry or Combo (for filename properties) or a Frame (for + background pixmaps) */ + filename_value = GTK_WIDGET (data); + + /* Create the dialog if it doesn't exist yet */ + if (!filesel) + { + filesel = gtk_file_chooser_dialog_new (_("Select File"), + GTK_WINDOW (win_property), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_CLEAR, GLADE_RESPONSE_CLEAR, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (filesel), + GTK_RESPONSE_OK); + + g_signal_connect (filesel, "response", + GTK_SIGNAL_FUNC (on_filesel_response), NULL); + g_signal_connect (filesel, "delete_event", + G_CALLBACK (gtk_true), NULL); + } + + /* set to current file, if there is one */ + if (GTK_IS_ENTRY (filename_value) || GTK_IS_COMBO (filename_value)) + { + filename = gtk_object_get_data (GTK_OBJECT (filename_value), + GbFilenameValueKey); + } + else + { + filename = gtk_object_get_data (GTK_OBJECT (GTK_BIN (filename_value)->child), + GbBgFilenameKey); + } + if (filename != NULL) + { + MSG1 ("Setting filename to: %s", filename); + glade_util_set_file_selection_filename (GTK_WIDGET (filesel), filename); + } + + gtk_window_present (GTK_WINDOW (filesel)); +} + + +static void +on_filesel_response (GtkWidget * widget, gint response_id, gpointer data) +{ + GdkPixmap *gdkpixmap = NULL, *old_gdkpixmap; + gchar *filename, *old_filename; + GtkWidget *drawing_area; + + if (response_id != GTK_RESPONSE_OK && response_id != GLADE_RESPONSE_CLEAR) + { + close_dialog (widget, GTK_WIDGET (filesel)); + return; + } + + if (response_id == GLADE_RESPONSE_CLEAR) + filename = NULL; + else + filename = glade_util_get_file_selection_filename (GTK_WIDGET (filesel)); + + /* For pixmaps we just show the file basename */ + if (GTK_IS_ENTRY (filename_value) || GTK_IS_COMBO (filename_value)) + { + gtk_object_set_data_full (GTK_OBJECT (filename_value), + GbFilenameValueKey, + g_strdup (filename), filename ? g_free : NULL); + if (GTK_IS_ENTRY (filename_value)) + { + gtk_entry_set_text (GTK_ENTRY (filename_value), + filename ? g_basename (filename) : ""); + on_property_changed (filename_value, filename_value); + } + else + { + /* For combos we don't have to call on_property_changed() since + the property should already have a signal connected to the + GtkEntry changed signal, so the popup list can be used. */ + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (filename_value)->entry), + filename ? g_basename (filename) : ""); + } + close_dialog (widget, GTK_WIDGET (filesel)); + g_free (filename); + return; + } + + drawing_area = GTK_BIN (filename_value)->child; + + /* For background pixmaps we show them in the property value widget */ + if (filename) + { + gdkpixmap = gdk_pixmap_create_from_xpm (drawing_area->window, NULL, + &drawing_area->style->bg[GTK_STATE_NORMAL], + filename); + if (!gdkpixmap) + { + glade_util_show_message_box (_("Couldn't create pixmap from file\n"), + GTK_WIDGET (filesel)); + g_free (filename); + return; + } + } + + old_gdkpixmap = gtk_object_get_data (GTK_OBJECT (drawing_area), GbBgPixmapKey); + if (old_gdkpixmap) + gdk_pixmap_unref (old_gdkpixmap); + old_filename = gtk_object_get_data (GTK_OBJECT (drawing_area), GbBgFilenameKey); + g_free (old_filename); + + gtk_object_set_data (GTK_OBJECT (drawing_area), GbBgPixmapKey, gdkpixmap); + gtk_object_set_data (GTK_OBJECT (drawing_area), GbBgFilenameKey, + g_strdup (filename)); + + close_dialog (widget, GTK_WIDGET (filesel)); + show_pixmap_in_drawing_area (drawing_area, gdkpixmap); + gtk_widget_queue_draw (drawing_area); + on_property_changed (filename_value, filename_value); + + g_free (filename); +} + + +/* + + */ + + +static GtkWidget * +create_signals_property_page () +{ + GtkWidget *page, *table, *hbox, *button, *scrolled_win; + gchar *signal_titles[5]; + GList *handler_list = NULL; + + page = gtk_vbox_new (FALSE, 0); + gtk_widget_show (page); + + /* List of current signal handlers - Signal/Handler/Data/Options */ + signal_titles[0] = _("Signal"); + signal_titles[1] = _("Handler"); + signal_titles[2] = _("Data"); + signal_titles[3] = _("After"); + signal_titles[4] = _("Object"); + signal_clist = gtk_clist_new_with_titles (5, signal_titles); + gtk_clist_set_column_width (GTK_CLIST (signal_clist), 0, 150); + gtk_clist_set_column_width (GTK_CLIST (signal_clist), 1, 150); + gtk_clist_set_column_width (GTK_CLIST (signal_clist), 2, 80); + gtk_clist_set_column_width (GTK_CLIST (signal_clist), 3, 50); + gtk_clist_set_column_width (GTK_CLIST (signal_clist), 4, 80); + gtk_widget_show (signal_clist); + gtk_signal_connect (GTK_OBJECT (signal_clist), "select_row", + GTK_SIGNAL_FUNC (on_signal_select), NULL); + + /* Hide the Data column. */ + gtk_clist_set_column_visibility (GTK_CLIST (signal_clist), 2, FALSE); +#if 0 + gtk_clist_set_column_visibility (GTK_CLIST (signal_clist), 4, FALSE); +#endif + + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + /* This is only here to set a minimum size for the property editor. */ + gtk_widget_set_usize (scrolled_win, 230, -1); + gtk_container_add (GTK_CONTAINER (scrolled_win), signal_clist); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (page), scrolled_win, TRUE, TRUE, 0); + gtk_widget_show (scrolled_win); + + /* Mod, Key & Signal fields */ + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 1); + gtk_widget_show (table); + property_set_table_position (table, 0); + property_add_dialog (GbSignalName, _("Signal:"), + _("The signal to add a handler for"), TRUE, + show_signals_dialog); + + /* FIXME: These are copied in gbsource.c, and should be language-specific, + i.e. we may need to change them when the project option changes. */ + handler_list = g_list_append (handler_list, "gtk_widget_show"); + handler_list = g_list_append (handler_list, "gtk_widget_hide"); + handler_list = g_list_append (handler_list, "gtk_widget_grab_focus"); + handler_list = g_list_append (handler_list, "gtk_widget_destroy"); + handler_list = g_list_append (handler_list, "gtk_window_activate_default"); + handler_list = g_list_append (handler_list, "gtk_true"); + handler_list = g_list_append (handler_list, "gtk_false"); + handler_list = g_list_append (handler_list, "gtk_main_quit"); + property_add_combo (GbSignalHandler, _("Handler:"), + _("The function to handle the signal"), handler_list); + g_list_free (handler_list); + + property_add_string (GbSignalData, _("Data:"), + _("The data passed to the handler")); + property_add_string (GbSignalObject, _("Object:"), + _("The object which receives the signal")); + property_add_bool (GbSignalAfter, _("After:"), + _("If the handler runs after the class function")); + + /* Hide the data & object properties since they make signals too complicated. + But we'll leave them in for a while so we don't break old apps. */ + property_set_visible (GbSignalData, FALSE); +#if 0 + property_set_visible (GbSignalObject, FALSE); +#endif + + gtk_box_pack_start (GTK_BOX (page), table, FALSE, TRUE, 5); + + /* Add/Update/Delete buttons at bottom */ + hbox = gtk_hbox_new (TRUE, 2); + button = gtk_button_new_with_label (_("Add")); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_signal_add), signal_clist); + + button = gtk_button_new_with_label (_("Update")); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_signal_update), signal_clist); + + button = gtk_button_new_with_label (_("Delete")); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_signal_delete), signal_clist); + + button = gtk_button_new_with_label (_("Clear")); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_signal_clear), signal_clist); + + gtk_box_pack_start (GTK_BOX (page), hbox, FALSE, TRUE, 5); + gtk_widget_show (hbox); + + return page; +} + + +static void +show_accelerators_dialog (GtkWidget * widget, + gpointer value) +{ + GtkWidget *transient_parent = glade_util_get_toplevel (widget); + if (!accel_dialog) + create_accelerators_dialog (); + + if (GTK_IS_WINDOW (transient_parent)) + gtk_window_set_transient_for (GTK_WINDOW (accel_dialog), + GTK_WINDOW (transient_parent)); + + gtk_widget_show (accel_dialog); + if (GTK_WIDGET_REALIZED (accel_dialog)) + { + gdk_window_show (accel_dialog->window); + gdk_window_raise (accel_dialog->window); + } +} + + +static void +on_accelerators_dialog_response (GtkWidget * widget, gint response_id, + gpointer data) +{ + gtk_widget_hide (widget); +} + + +static void +create_accelerators_dialog () +{ + GtkWidget *vbox, *vbox2; + gchar *accel_titles[3]; + GtkWidget *hbox, *label, *button, *table, *scrolled_win; + int row; + + accel_dialog = gtk_dialog_new_with_buttons (_("Accelerators"), NULL, 0, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (accel_dialog), GTK_RESPONSE_CLOSE); + gtk_window_set_wmclass (GTK_WINDOW (accel_dialog), "accelerators", "Glade"); + + vbox = GTK_DIALOG (accel_dialog)->vbox; + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0); + + /* List of current accelerators - Mods/Keys/Signals */ + accel_titles[0] = _("Mod"); + accel_titles[1] = _("Key"); + accel_titles[2] = _("Signal to emit"); + accel_clist = gtk_clist_new_with_titles (3, accel_titles); + gtk_clist_set_column_width (GTK_CLIST (accel_clist), 0, 30); + gtk_clist_set_column_width (GTK_CLIST (accel_clist), 1, 100); + gtk_clist_set_column_width (GTK_CLIST (accel_clist), 2, 120); + gtk_widget_show (accel_clist); + gtk_signal_connect (GTK_OBJECT (accel_clist), "select_row", + GTK_SIGNAL_FUNC (on_accelerator_select), NULL); + + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (scrolled_win), accel_clist); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (vbox2), scrolled_win, TRUE, TRUE, 0); + gtk_widget_set_usize (scrolled_win, 320, 120); + gtk_widget_show (scrolled_win); + + /* Mod, Key & Signal fields */ + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 1); + gtk_widget_show (table); + row = 0; + label = gtk_label_new (_("Modifiers:")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_widget_show (label); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_FILL, 0, 1, 1); + + hbox = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox); + accel_control_button = gtk_check_button_new_with_label (_("Ctrl")); + gtk_widget_show (accel_control_button); + gtk_box_pack_start (GTK_BOX (hbox), accel_control_button, TRUE, TRUE, 0); + accel_shift_button = gtk_check_button_new_with_label (_("Shift")); + gtk_widget_show (accel_shift_button); + gtk_box_pack_start (GTK_BOX (hbox), accel_shift_button, TRUE, TRUE, 0); + accel_alt_button = gtk_check_button_new_with_label (_("Alt")); + gtk_widget_show (accel_alt_button); + gtk_box_pack_start (GTK_BOX (hbox), accel_alt_button, TRUE, TRUE, 0); + gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, row, row + 1, + GTK_EXPAND | GTK_FILL, 0, 1, 1); + + property_set_table_position (table, ++row); + property_add_dialog (GbAccelKey, _("Key:"), + _("The accelerator key"), TRUE, show_keys_dialog); + property_add_dialog (GbAccelSignal, _("Signal:"), + _("The signal to emit when the accelerator is pressed"), + TRUE, show_signals_dialog); + gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, TRUE, 5); + + /* Add/Update/Delete buttons at bottom */ + hbox = gtk_hbox_new (TRUE, 2); + /*button = gtk_button_new_with_label (_("Add"));*/ + button = gtk_button_new_from_stock (GTK_STOCK_ADD); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_accelerator_add), accel_clist); + + /*button = gtk_button_new_with_label (_("Update"));*/ + button = gtk_button_new_from_stock (GTK_STOCK_APPLY); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_accelerator_update), accel_clist); + + /*button = gtk_button_new_with_label (_("Delete"));*/ + button = gtk_button_new_from_stock (GTK_STOCK_DELETE); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_accelerator_delete), accel_clist); + + /*button = gtk_button_new_with_label (_("Clear"));*/ + button = gtk_button_new_from_stock (GTK_STOCK_CLEAR); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_accelerator_clear), accel_clist); + + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, TRUE, 5); + gtk_widget_show (hbox); + + gtk_signal_connect (GTK_OBJECT (accel_dialog), "response", + GTK_SIGNAL_FUNC (on_accelerators_dialog_response), + NULL); + gtk_signal_connect (GTK_OBJECT (accel_dialog), "delete_event", + GTK_SIGNAL_FUNC (gtk_true), + NULL); +} + + +static void +on_color_draw (GtkWidget * widget, gpointer data) +{ +#ifdef GLADE_STYLE_SUPPORT + if (widget == selected_style_widget) + { + gdk_draw_rectangle (widget->window, widget->style->black_gc, FALSE, 0, 0, + widget->allocation.width - 1, + widget->allocation.height - 1); + gdk_draw_rectangle (widget->window, widget->style->white_gc, FALSE, 1, 1, + widget->allocation.width - 3, + widget->allocation.height - 3); + gdk_draw_rectangle (widget->window, widget->style->black_gc, FALSE, 2, 2, + widget->allocation.width - 5, + widget->allocation.height - 5); + } +#endif +} + + +static void +on_color_expose_event (GtkWidget * widget, GdkEvent * event, gpointer data) +{ + GdkColor *color = gtk_object_get_data (GTK_OBJECT (widget), GbColorKey); + g_return_if_fail (color != NULL); + gdk_window_set_background (widget->window, color); + gdk_window_clear (widget->window); + on_color_draw (widget, data); +} + + +static void +on_color_select (GtkWidget * widget, gpointer data) +{ +#ifdef GLADE_STYLE_SUPPORT + if (selected_style_widget == widget) + return; + if (selected_style_widget && selected_style_widget != widget) + gtk_widget_queue_draw (selected_style_widget); + selected_style_widget = widget; +#endif + gtk_widget_queue_draw (widget); +} + + +/* + * Creating property widgets + */ + +void +property_set_table_position (GtkWidget * table, gint row) +{ + property_table = table; + property_table_row = row; +} + + +GtkWidget* +property_get_table_position (gint *row) +{ + *row = property_table_row; + return property_table; +} + + +static void +run_text_property_dialog (const gchar *property_name) +{ + GtkWidget *dialog; + GtkWidget *vbox5; + GtkWidget *vbox6; + GtkWidget *vbox7; + GtkWidget *frame2; + GtkWidget *alignment2; + GtkWidget *scrolledwindow8; + GtkWidget *property_text; + GtkWidget *label7; + GtkWidget *hbox3; + GtkWidget *translatable_checkbutton; + GtkWidget *context_prefix; + GtkWidget *frame3; + GtkWidget *alignment3; + GtkWidget *scrolledwindow9; + GtkWidget *comments; + GtkWidget *label8; + GtkWidget *hbuttonbox1; + GtkWidget *button1; + GtkWidget *button2; + GtkWidget *value, *textview; + gchar *text, *comments_text; + GtkTextBuffer *value_text_buffer = NULL, *text_buffer, *comments_text_buffer; + GtkTextIter start, end; + gboolean translatable, context; + gboolean free_text; + gboolean show_translation_properties = FALSE; + + value = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + + if (g_object_get_data (G_OBJECT (value), GladeShowTranslationPropertiesKey)) + show_translation_properties = TRUE; + + dialog = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dialog), _("Edit Text Property")); + gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 300); + gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG); + + vbox5 = GTK_DIALOG (dialog)->vbox; + gtk_widget_show (vbox5); + + vbox6 = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox6); + gtk_box_pack_start (GTK_BOX (vbox5), vbox6, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox6), 2); + + vbox7 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox7); + gtk_box_pack_start (GTK_BOX (vbox6), vbox7, TRUE, TRUE, 0); + + frame2 = gtk_frame_new (NULL); + gtk_widget_show (frame2); + gtk_box_pack_start (GTK_BOX (vbox7), frame2, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame2), GTK_SHADOW_NONE); + + alignment2 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment2); + gtk_container_add (GTK_CONTAINER (frame2), alignment2); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment2), 2, 0, 12, 0); + + scrolledwindow8 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow8); + gtk_container_add (GTK_CONTAINER (alignment2), scrolledwindow8); + gtk_widget_set_size_request (scrolledwindow8, 400, 200); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow8), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow8), GTK_SHADOW_IN); + + property_text = gtk_text_view_new (); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (property_text), GTK_WRAP_WORD); + gtk_widget_show (property_text); + gtk_container_add (GTK_CONTAINER (scrolledwindow8), property_text); + + label7 = gtk_label_new_with_mnemonic (_("<b>_Text:</b>")); + gtk_widget_show (label7); + gtk_frame_set_label_widget (GTK_FRAME (frame2), label7); + gtk_label_set_use_markup (GTK_LABEL (label7), TRUE); + + hbox3 = gtk_hbox_new (FALSE, 12); + if (show_translation_properties) + gtk_widget_show (hbox3); + gtk_box_pack_start (GTK_BOX (vbox6), hbox3, FALSE, TRUE, 0); + + translatable_checkbutton = gtk_check_button_new_with_mnemonic (_("T_ranslatable")); + gtk_widget_show (translatable_checkbutton); + gtk_box_pack_start (GTK_BOX (hbox3), translatable_checkbutton, FALSE, FALSE, 0); + + context_prefix = gtk_check_button_new_with_mnemonic (_("Has Context _Prefix")); + gtk_widget_show (context_prefix); + gtk_box_pack_start (GTK_BOX (hbox3), context_prefix, FALSE, FALSE, 0); + + frame3 = gtk_frame_new (NULL); + if (show_translation_properties) + gtk_widget_show (frame3); + gtk_box_pack_start (GTK_BOX (vbox6), frame3, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame3), GTK_SHADOW_NONE); + + alignment3 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment3); + gtk_container_add (GTK_CONTAINER (frame3), alignment3); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment3), 2, 0, 12, 0); + + scrolledwindow9 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow9); + gtk_container_add (GTK_CONTAINER (alignment3), scrolledwindow9); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow9), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow9), GTK_SHADOW_IN); + + comments = gtk_text_view_new (); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (comments), GTK_WRAP_WORD); + gtk_widget_show (comments); + gtk_container_add (GTK_CONTAINER (scrolledwindow9), comments); + + label8 = gtk_label_new_with_mnemonic (_("<b>Co_mments For Translators:</b>")); + gtk_widget_show (label8); + gtk_frame_set_label_widget (GTK_FRAME (frame3), label8); + gtk_label_set_use_markup (GTK_LABEL (label8), TRUE); + + hbuttonbox1 = GTK_DIALOG (dialog)->action_area; + gtk_widget_show (hbuttonbox1); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_END); + + button1 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button1); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button1, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (button1, GTK_CAN_DEFAULT); + + button2 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button2); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button2, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (button2, GTK_CAN_DEFAULT); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label7), property_text); + gtk_label_set_mnemonic_widget (GTK_LABEL (label8), comments); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + + /* Set the fields according to the data stored in the widget. */ + if (GTK_IS_SCROLLED_WINDOW (value)) + { + textview = GTK_BIN (value)->child; + value_text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); + gtk_text_buffer_get_bounds (value_text_buffer, &start, &end); + text = gtk_text_buffer_get_text (value_text_buffer, &start, &end, TRUE); + free_text = TRUE; + } + else + { + text = (gchar*) gtk_entry_get_text (GTK_ENTRY (value)); + free_text = FALSE; + } + + text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (property_text)); + gtk_text_buffer_set_text (text_buffer, text ? text : "", -1); + if (free_text) + g_free (text); + + glade_util_get_translation_properties (value, property_name, &translatable, + &comments_text, &context); + + comments_text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (comments)); + gtk_text_buffer_set_text (comments_text_buffer, + comments_text ? comments_text : "", -1); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (translatable_checkbutton), + translatable); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (context_prefix), + context); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) + { + /* Copy the 3 extra settings to the widget data. */ + gtk_text_buffer_get_bounds (comments_text_buffer, &start, &end); + text = gtk_text_buffer_get_text (comments_text_buffer, &start, &end, + TRUE); + if (text && text[0] == '\0') + { + g_free (text); + text = NULL; + } + + translatable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (translatable_checkbutton)); + context = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (context_prefix)); + + if (show_translation_properties) + glade_util_set_translation_properties (value, property_name, + translatable, text, context); + g_free (text); + + /* Copy the main text back to the widget in the property editor. + Do this last since it will result in the "changed" callbacks being + called and we want to be in a good state for that. */ + gtk_text_buffer_get_bounds (text_buffer, &start, &end); + text = gtk_text_buffer_get_text (text_buffer, &start, &end, TRUE); + if (GTK_IS_SCROLLED_WINDOW (value)) + { + gtk_text_buffer_set_text (value_text_buffer, text ? text : "", -1); + } + else + { + gtk_entry_set_text (GTK_ENTRY (value), text); + } + + /* We have to call this, since if the text doesn't change it + doesn't emit the "changed" signal. */ + on_property_changed (value, value); + + g_free (text); + } + + gtk_widget_destroy (dialog); +} + + +static void +property_edit_string (GtkWidget *button, const gchar *property_name) +{ + run_text_property_dialog (property_name); +} + + +void +property_add_string (const gchar * property_name, + const gchar * label, + const gchar * tooltip) +{ + GtkWidget *value = gtk_entry_new (); + GtkWidget *dialog_button = gtk_button_new_with_label ("..."); + gtk_widget_set_usize (value, 80, -1); + property_name = property_add (property_name, label, value, dialog_button, + tooltip); + gtk_signal_connect (GTK_OBJECT (value), "changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + gtk_signal_connect (GTK_OBJECT (dialog_button), "clicked", + GTK_SIGNAL_FUNC (property_edit_string), + (gpointer) property_name); + + /* Flag that it is a string property, so we can distinguish it from dialogs + etc. in property_set_string(). */ + g_object_set_data (G_OBJECT (value), GladeIsStringPropertyKey, "TRUE"); +} + + +static void +property_edit_text (GtkWidget *button, const gchar *property_name) +{ + run_text_property_dialog (property_name); +} + + +void +property_add_text (const gchar * property_name, + const gchar * label, + const gchar * tooltip, + gint visible_lines) +{ + GtkWidget *value, *text, *dialog_button; + GtkTextBuffer *buffer; + gint line_height; + PangoFontMetrics *metrics; + PangoContext *context; + gint ascent, descent; + + value = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (value), + GTK_SHADOW_IN); + buffer = gtk_text_buffer_new (NULL); + text = gtk_text_view_new_with_buffer (buffer); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text), GTK_WRAP_WORD); + gtk_widget_show (text); + gtk_container_add (GTK_CONTAINER (value), text); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (value), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + context = gtk_widget_get_pango_context (text); + metrics = pango_context_get_metrics (context, + text->style->font_desc, + pango_context_get_language (context)); + ascent = pango_font_metrics_get_ascent (metrics); + descent = pango_font_metrics_get_descent (metrics); + line_height = PANGO_PIXELS (ascent + descent); + + /* We add extra for the text's border height, hscrollbar height etc. */ + gtk_widget_set_usize (value, 80, visible_lines * line_height + 24); + dialog_button = gtk_button_new_with_label ("..."); + property_name = property_add (property_name, label, value, dialog_button, + tooltip); + g_signal_connect (G_OBJECT (buffer), "changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + gtk_signal_connect (GTK_OBJECT (dialog_button), "clicked", + GTK_SIGNAL_FUNC (property_edit_text), + (gpointer) property_name); + g_object_unref (G_OBJECT (buffer)); +} + + +void +property_add_int (const gchar * property_name, + const gchar * label, + const gchar * tooltip) +{ + GtkWidget *value = gtk_entry_new (); + gtk_widget_set_usize (value, 80, -1); + property_add (property_name, label, value, NULL, tooltip); + gtk_signal_connect (GTK_OBJECT (value), "activate", + GTK_SIGNAL_FUNC (on_property_changed), value); + gtk_signal_connect (GTK_OBJECT (value), "focus_out_event", + GTK_SIGNAL_FUNC (on_property_focus_out), value); +} + + +void +property_add_int_range (const gchar * property_name, + const gchar * label, + const gchar * tooltip, + gint min, + gint max, + gint step_increment, + gint page_increment, + gint climb_rate) +{ + GtkObject *adjustment = gtk_adjustment_new (min, min, max, step_increment, + page_increment, page_increment); + GtkWidget *value = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), + climb_rate, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (value), TRUE); + gtk_widget_set_usize (value, 80, -1); + gtk_signal_connect (GTK_OBJECT (value), "value_changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + property_add (property_name, label, value, NULL, tooltip); +} + + +void +property_add_optional_int_range (const gchar * property_name, + const gchar * label, + const gchar * tooltip, + gint min, + gint max, + gint step_increment, + gint page_increment, + gint climb_rate, + GtkCallback callback) +{ + GtkObject *adjustment = gtk_adjustment_new (min, min, max, step_increment, + page_increment, page_increment); + GtkWidget *value = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), + climb_rate, 0); + GtkWidget *dialog_button = gtk_check_button_new (); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (value), TRUE); +#if 0 + gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (dialog_button), FALSE); + gtk_widget_set_usize (dialog_button, 16, -1); +#endif + gtk_widget_set_usize (value, 50, -1); + gtk_signal_connect (GTK_OBJECT (value), "value_changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + gtk_signal_connect (GTK_OBJECT (dialog_button), "toggled", + GTK_SIGNAL_FUNC (callback), value); + property_add (property_name, label, value, dialog_button, tooltip); +} + + +void +property_add_float (const gchar * property_name, + const gchar * label, + const gchar * tooltip) +{ + GtkWidget *value = gtk_entry_new (); + gtk_widget_set_usize (value, 80, -1); + property_add (property_name, label, value, NULL, tooltip); + gtk_signal_connect (GTK_OBJECT (value), "activate", + GTK_SIGNAL_FUNC (on_property_changed), value); + gtk_signal_connect (GTK_OBJECT (value), "focus_out_event", + GTK_SIGNAL_FUNC (on_property_focus_out), value); +} + + +void +property_add_float_range (const gchar * property_name, + const gchar * label, + const gchar * tooltip, + gfloat min, + gfloat max, + gfloat step_increment, + gfloat page_increment, + gfloat climb_rate, + gint decimal_digits) +{ + GtkObject *adjustment = gtk_adjustment_new (min, min, max, step_increment, + page_increment, page_increment); + GtkWidget *value = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), + climb_rate, decimal_digits); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (value), TRUE); + gtk_widget_set_usize (value, 80, -1); + gtk_signal_connect (GTK_OBJECT (value), "value_changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + property_add (property_name, label, value, NULL, tooltip); +} + + +static void +on_bool_property_toggle (GtkWidget * value, gpointer data) +{ + guint active = GTK_TOGGLE_BUTTON (value)->active; + GtkWidget *label = GTK_BIN (value)->child; + gtk_label_set_text (GTK_LABEL (label), active ? _("Yes") : _("No")); + on_property_changed (value, value); +} + + +void +property_add_bool (const gchar * property_name, + const gchar * label, + const gchar * tooltip) +{ + GtkWidget *value = gtk_toggle_button_new_with_label (_("No")); + GTK_WIDGET_UNSET_FLAGS (value, GTK_CAN_DEFAULT); + gtk_signal_connect (GTK_OBJECT (value), "toggled", + GTK_SIGNAL_FUNC (on_bool_property_toggle), NULL); + property_add (property_name, label, value, NULL, tooltip); +} + + +void +property_add_choice (const gchar * property_name, + const gchar * label, + const gchar * tooltip, + const gchar ** choices) +{ + GtkWidget *value = gtk_option_menu_new (); + GtkWidget *menu = gtk_menu_new (); + gint i = 0; + + GTK_WIDGET_UNSET_FLAGS (value, GTK_CAN_DEFAULT); + + while (choices[i]) + { + GtkWidget *menuitem = gtk_menu_item_new_with_label (choices[i]); + gtk_container_add (GTK_CONTAINER (menu), menuitem); + gtk_widget_show (menuitem); + i++; + } + gtk_option_menu_set_menu (GTK_OPTION_MENU (value), menu); + gtk_signal_connect_after (GTK_OBJECT (menu), "selection_done", + GTK_SIGNAL_FUNC (on_property_changed), value); + property_add (property_name, label, value, NULL, tooltip); +} + + +void +property_add_combo (const gchar * property_name, + const gchar * label, + const gchar * tooltip, + GList * choices) +{ + GtkWidget *value = gtk_combo_new (); + gtk_widget_set_usize (GTK_COMBO (value)->entry, 60, -1); + gtk_widget_set_usize (value, 80, -1); + if (choices) + gtk_combo_set_popdown_strings (GTK_COMBO (value), choices); + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (value)->entry), "changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + property_add (property_name, label, value, NULL, tooltip); +} + + +void +property_add_color (const gchar * property_name, + const gchar * label, + const gchar * tooltip) +{ + GtkWidget *value, *preview, *dialog_button; + value = gtk_frame_new (NULL); + preview = create_color_preview (); + gtk_widget_set_events (preview, gtk_widget_get_events (preview) + | GDK_BUTTON_PRESS_MASK); + gtk_signal_connect_after (GTK_OBJECT (preview), "expose_event", + GTK_SIGNAL_FUNC (on_color_expose_event), value); + gtk_signal_connect (GTK_OBJECT (preview), "button_press_event", + GTK_SIGNAL_FUNC (on_color_select), value); + + gtk_container_add (GTK_CONTAINER (value), preview); + dialog_button = gtk_button_new_with_label ("..."); + gtk_signal_connect (GTK_OBJECT (dialog_button), "clicked", + GTK_SIGNAL_FUNC (show_colorsel_dialog), value); + property_add (property_name, label, value, dialog_button, tooltip); +} + + +void +property_add_bgpixmap (const gchar * property_name, + const gchar * label, + const gchar * tooltip) +{ + GtkWidget *value, *drawing_area, *dialog_button; + value = gtk_frame_new (NULL); + drawing_area = gtk_drawing_area_new (); + gtk_widget_set_events (drawing_area, gtk_widget_get_events (drawing_area) + | GDK_BUTTON_PRESS_MASK | GDK_EXPOSURE_MASK); + gtk_widget_set_size_request (drawing_area, 100, 20); + gtk_widget_show (drawing_area); + gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event", + GTK_SIGNAL_FUNC (expose_pixmap), NULL); + gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event", + GTK_SIGNAL_FUNC (on_color_select), value); + gtk_container_add (GTK_CONTAINER (value), drawing_area); + dialog_button = gtk_button_new_with_label ("..."); + gtk_signal_connect (GTK_OBJECT (dialog_button), "clicked", + GTK_SIGNAL_FUNC (show_filesel_dialog), value); + property_add (property_name, label, value, dialog_button, tooltip); +} + + +void +property_add_dialog (const gchar * property_name, + const gchar * label, + const gchar * tooltip, + gboolean editable, + GtkCallback callback) +{ + GtkWidget *value, *dialog_button; + value = gtk_entry_new (); + gtk_widget_set_usize (value, 80, -1); + gtk_editable_set_editable (GTK_EDITABLE (value), editable); + dialog_button = gtk_button_new_with_label ("..."); + gtk_signal_connect (GTK_OBJECT (dialog_button), "clicked", + GTK_SIGNAL_FUNC (callback), value); + gtk_signal_connect (GTK_OBJECT (value), "changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + property_add (property_name, label, value, dialog_button, tooltip); +} + + +void +property_add_filename (const gchar * property_name, + const gchar * label, + const gchar * tooltip) +{ + GtkWidget *value, *dialog_button; + value = gtk_entry_new (); + gtk_widget_set_usize (value, 80, -1); + gtk_editable_set_editable (GTK_EDITABLE (value), FALSE); + dialog_button = gtk_button_new_with_label ("..."); + gtk_signal_connect (GTK_OBJECT (dialog_button), "clicked", + GTK_SIGNAL_FUNC (show_filesel_dialog), value); + property_add (property_name, label, value, dialog_button, tooltip); +} + + +void +property_add_filename_with_combo (const gchar * property_name, + const gchar * label, + const gchar * tooltip, + GList * choices) +{ + GtkWidget *value, *dialog_button; + value = gtk_combo_new (); + gtk_widget_set_usize (GTK_COMBO (value)->entry, 60, -1); + gtk_widget_set_usize (value, 80, -1); + if (choices) + gtk_combo_set_popdown_strings (GTK_COMBO (value), choices); + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (value)->entry), "changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + dialog_button = gtk_button_new_with_label ("..."); + gtk_signal_connect (GTK_OBJECT (dialog_button), "clicked", + GTK_SIGNAL_FUNC (show_filesel_dialog), value); + property_add (property_name, label, value, dialog_button, tooltip); +} + + + +void +property_add_font (const gchar * property_name, + const gchar * label, + const gchar * tooltip) +{ + GtkWidget *value, *dialog_button; + value = gtk_entry_new (); + gtk_widget_set_usize (value, 80, -1); + gtk_editable_set_editable (GTK_EDITABLE (value), FALSE); + dialog_button = gtk_button_new_with_label ("..."); + gtk_signal_connect (GTK_OBJECT (dialog_button), "clicked", + GTK_SIGNAL_FUNC (show_font_dialog), value); + gtk_signal_connect (GTK_OBJECT (value), "changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + property_add (property_name, label, value, dialog_button, tooltip); +} + + +void +property_add_command (const gchar * property_name, + const gchar * label, + const gchar * tooltip, + const gchar * command, + GtkSignalFunc callback) +{ + GtkWidget *value; + value = gtk_button_new_with_label (command); + if (callback) + { + gtk_signal_connect (GTK_OBJECT (value), "clicked", + GTK_SIGNAL_FUNC (callback), value); + } + property_add (property_name, label, value, NULL, tooltip); +} + + +/* This adds all the stock items to the combo's popup list. It uses the + smallest icon size available. It hides items that don't have an icon of the + given size, or don't have a label if need_label is TRUE. */ +static void +fill_stock_combo (GtkWidget *combo, + gboolean need_label, + GtkIconSize icon_size) +{ + GtkWidget *list, *listitem; + GSList *elem; + + list = GTK_COMBO (combo)->list; + + /* Add a 'None' item at the top of the list. */ + listitem = gtk_list_item_new_with_label (_("None")); + gtk_widget_show (listitem); + gtk_container_add (GTK_CONTAINER (list), listitem); + gtk_combo_set_item_string (GTK_COMBO (combo), GTK_ITEM (listitem), ""); + + for (elem = stock_items; elem; elem = elem->next) + { + GtkStockItem item; + GtkWidget *hbox, *image, *label; + GtkIconSize *sizes; + gint n_sizes, i, use_icon_size; + gchar *stock_id, *label_text; + gboolean show_item; + + stock_id = elem->data; + +#if 0 + g_print ("Stock ID: %s\n", stock_id); +#endif + + n_sizes = GPOINTER_TO_INT (g_hash_table_lookup (stock_nsizes_hash, + stock_id)); + sizes = g_hash_table_lookup (stock_sizes_hash, stock_id); + if (n_sizes <= 0) + continue; + + /* If we accept any icons size, then get the icon closest to + GTK_ICON_SIZE_MENU to display in the list. If we only accept a + particular size, then check the stock item has that size. */ + use_icon_size = GTK_ICON_SIZE_INVALID; + + show_item = (icon_size == GLADE_ICON_SIZE_ANY) ? TRUE : FALSE; + + for (i = 0; i < n_sizes; i++) + { + if (sizes[i] == icon_size) + show_item = TRUE; + + if (sizes[i] != GTK_ICON_SIZE_INVALID + && (use_icon_size == GTK_ICON_SIZE_INVALID + || sizes[i] < use_icon_size)) + use_icon_size = sizes[i]; + } + + if (use_icon_size == GTK_ICON_SIZE_INVALID) + { +#if 0 + g_print ("Skipping: %s\n", stock_id); +#endif + continue; + } + + if (gtk_stock_lookup (stock_id, &item)) + { + label_text = item.label; + } + else + { + if (need_label) + show_item = FALSE; + label_text = stock_id; + } + + listitem = gtk_list_item_new (); + gtk_object_set_data (GTK_OBJECT (listitem), GladeStockIDKey, stock_id); + if (show_item) + gtk_widget_show (listitem); + hbox = gtk_hbox_new (FALSE, 3); + gtk_container_add (GTK_CONTAINER (listitem), hbox); + gtk_widget_show (hbox); + + image = gtk_image_new_from_stock (stock_id, use_icon_size); + if (image) + { + gtk_widget_show (image); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + } + + label = gtk_type_new (GTK_TYPE_ACCEL_LABEL); + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), label_text); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + gtk_combo_set_item_string (GTK_COMBO (combo), GTK_ITEM (listitem), + stock_id); + + gtk_container_add (GTK_CONTAINER (list), listitem); + } +} + + +/* This shows the appropriate stock items in the popup list. If need_label + is TRUE, it only shows items which have stock labels. If icon_size is + not GLADE_ICON_SIZE_ANY, it only shows items that have icons of the given + size. */ +static void +show_stock_items (GtkWidget *combo) +{ + gboolean need_label; + GtkIconSize icon_size; + GtkWidget *list; + GList *elem; + gboolean gnome_support; + + need_label = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (combo), + GladeNeedLabelKey)); + icon_size = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (combo), + GladeIconSizeKey)); + + list = GTK_COMBO (combo)->list; + + gnome_support = glade_project_get_gnome_support (current_project); + + for (elem = GTK_LIST (list)->children; elem; elem = elem->next) + { + GtkWidget *listitem; + gchar *stock_id; + gboolean show_item; + GtkStockItem item; + + listitem = elem->data; + + /* Fetch the stock id from the item. */ + stock_id = gtk_object_get_data (GTK_OBJECT (listitem), GladeStockIDKey); + if (!stock_id) + continue; + + show_item = TRUE; + if (need_label && !gtk_stock_lookup (stock_id, &item)) + show_item = FALSE; + + /* Only show GTK+ stock items in GTK+ projects. */ + if (!gnome_support && (strncmp (stock_id, "gtk-", 4) != 0)) + show_item = FALSE; + + if (show_item && icon_size != GLADE_ICON_SIZE_ANY) + { + GtkIconSize *sizes; + gint n_sizes, i; + + n_sizes = GPOINTER_TO_INT (g_hash_table_lookup (stock_nsizes_hash, + stock_id)); + sizes = g_hash_table_lookup (stock_sizes_hash, stock_id); + + show_item = FALSE; + for (i = 0; i < n_sizes; i++) + { + if (sizes[i] == icon_size) + { + show_item = TRUE; + } + } + } + + if (show_item) + gtk_widget_show (listitem); + else + gtk_widget_hide (listitem); + } +} + + +/* A stock item is a GTK+ stock item that has a label and an icon with the + given size. Any stock items that don't have labels or don't have an icon + with the given size are not displayed in the popup list. + You can change the icon size dynamically, e.g. based on another property. + The popup list will be recreated, and if the stock item currently used does + not have the given icon size, the stock item will be set to 'None'. + Using GLADE_ICON_SIZE_ANY means any icon size is OK. */ +void +property_add_stock_item (const gchar *property_name, + const gchar *label, + const gchar *tooltip, + GtkIconSize icon_size) +{ + GtkWidget *value; + + value = gtk_combo_new (); + gtk_widget_set_usize (GTK_COMBO (value)->entry, 60, -1); + gtk_widget_set_usize (value, 80, -1); + + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (value)->entry), "changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + + fill_stock_combo (value, TRUE, icon_size); + gtk_object_set_data (GTK_OBJECT (value), GladeNeedLabelKey, + GINT_TO_POINTER (TRUE)); + gtk_object_set_data (GTK_OBJECT (value), GladeIconSizeKey, + GINT_TO_POINTER (icon_size)); + + property_add (property_name, label, value, NULL, tooltip); +} + + +extern const gint GladeNamedIconsSize; +extern const gchar *GladeNamedIcons[]; + +void +property_add_named_icon (const gchar *property_name, + const gchar *label, + const gchar *tooltip) +{ + GtkWidget *value, *list, *listitem, *hbox, *image, *label_widget; + gint i; + + value = gtk_combo_new (); + list = GTK_COMBO (value)->list; + gtk_widget_set_usize (GTK_COMBO (value)->entry, 60, -1); + gtk_widget_set_usize (value, 80, -1); + + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (value)->entry), "changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + + for (i = 0; i < GladeNamedIconsSize; i++) + { + listitem = gtk_list_item_new (); + gtk_widget_show (listitem); + + hbox = gtk_hbox_new (FALSE, 3); + gtk_container_add (GTK_CONTAINER (listitem), hbox); + gtk_widget_show (hbox); + + image = gtk_image_new_from_icon_name (GladeNamedIcons[i], + GTK_ICON_SIZE_MENU); + if (image) + { + gtk_widget_show (image); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + } + + label_widget = gtk_label_new (GladeNamedIcons[i]); + gtk_widget_show (label_widget); + gtk_box_pack_start (GTK_BOX (hbox), label_widget, FALSE, FALSE, 0); + + gtk_combo_set_item_string (GTK_COMBO (value), GTK_ITEM (listitem), + GladeNamedIcons[i]); + + gtk_container_add (GTK_CONTAINER (list), listitem); + } + + property_add (property_name, label, value, NULL, tooltip); +} + + +/* An icon is a GTK+ stock icon with the given icon size, or a user-specified + icon. It will show a GtkCombo, with a '...' button beside it for opening + a file selection dialog, so the user can specify an icon file. + As with stock item, you can change the icon size dynamically, and the popup + list of stock icons will be recreated. */ +void +property_add_icon (const gchar *property_name, + const gchar *label, + const gchar *tooltip, + GtkIconSize icon_size) +{ + GtkWidget *value, *dialog_button; + + value = gtk_combo_new (); + gtk_widget_set_usize (GTK_COMBO (value)->entry, 60, -1); + gtk_widget_set_usize (value, 80, -1); + + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (value)->entry), "changed", + GTK_SIGNAL_FUNC (on_property_changed), value); + + fill_stock_combo (value, FALSE, icon_size); + gtk_object_set_data (GTK_OBJECT (value), GladeNeedLabelKey, + GINT_TO_POINTER (FALSE)); + gtk_object_set_data (GTK_OBJECT (value), GladeIconSizeKey, + GINT_TO_POINTER (icon_size)); + + dialog_button = gtk_button_new_with_label ("..."); + gtk_signal_connect (GTK_OBJECT (dialog_button), "clicked", + GTK_SIGNAL_FUNC (show_filesel_dialog), value); + + property_add (property_name, label, value, dialog_button, tooltip); +} + + +static const gchar* +property_add (const gchar * property_name, + const gchar * label_string, + GtkWidget * value, + GtkWidget * dialog_button, + const gchar * tooltip) +{ + GtkWidget *label, *eventbox; + gchar *property_name_copy; + gint xpad = 0, ypad = 0; + + if (glade_debug_properties) + g_print ("Property: %s\n", property_name); + +#if 0 + if (GTK_IS_OPTION_MENU (value)) + xpad = ypad = 0; +#endif + + label = gtk_label_new (label_string); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.3); + /*gtk_misc_set_padding(GTK_MISC(label), 0, 5); */ + gtk_widget_show (label); + + /* We put the label in the event box so we can set a tooltip. */ + eventbox = gtk_event_box_new (); + gtk_widget_set_usize (eventbox, 100, -1); + gtk_widget_show (eventbox); + gtk_container_add (GTK_CONTAINER (eventbox), label); + + gtk_widget_show (value); + if (tooltip) + gtk_tooltips_set_tip (tooltips, eventbox, tooltip, NULL); + + gtk_table_attach (GTK_TABLE (property_table), eventbox, 0, 1, + property_table_row, property_table_row + 1, + GTK_FILL, GTK_FILL, 1, 1); + if (dialog_button) + { + GtkWidget *hbox = gtk_hbox_new (FALSE, 0); + gtk_table_attach (GTK_TABLE (property_table), hbox, 1, 3, + property_table_row, property_table_row + 1, + GTK_EXPAND | GTK_FILL, 0, xpad, ypad); + /* Empty checkbuttons are placed before the value widget. */ + if (GTK_IS_CHECK_BUTTON (dialog_button) + && GTK_BIN (dialog_button)->child == NULL) + { + gtk_box_pack_start (GTK_BOX (hbox), dialog_button, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), value, TRUE, TRUE, 0); + } + else + { + gtk_box_pack_start (GTK_BOX (hbox), value, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), dialog_button, FALSE, FALSE, 0); + } + gtk_widget_show (dialog_button); + gtk_widget_show (hbox); + } + else + { + gtk_table_attach (GTK_TABLE (property_table), value, 1, 3, + property_table_row, property_table_row + 1, + GTK_EXPAND | GTK_FILL, 0, xpad, ypad); + } + property_table_row++; + + /* Insert property label & value widgets into hash tables */ + property_name_copy = g_strdup (property_name); + g_hash_table_insert (gb_property_labels, property_name_copy, label); + g_hash_table_insert (gb_property_values, property_name_copy, value); + if (dialog_button) + g_hash_table_insert (gb_property_buttons, + property_name_copy, + dialog_button); + + return property_name_copy; +} + + +/* + * Functions for getting/setting properties. + * NOTE: should also specify whether set properties are copied and + * if values returned by get should be freed. + * For widgets which use a GtkEntry: string value set is copied, + * & don't free string returned by get (it is the actual entry text). + * + * NOTE: property_get_*() functions also set the apply flag. + * This is used to enable the automatic applying of + * properties as they are changed in the property editor. It is set + * to TRUE if the property widget matches the to_apply widget. + */ + + +static void +copy_translation_properties (const gchar *property_name, + GtkWidget *from_widget, GtkWidget *to_widget) +{ + /* Just return if either widget is NULL. This can happen for the fields on + the signals page, which aren't translated at all. */ + if (from_widget == NULL || to_widget == NULL) + return; + + /* Copy the translator comments string, and translatable & context flags + from one widget to the other. */ + glade_util_copy_translation_properties (from_widget, property_name, + to_widget, property_name); +} + + +gchar * +property_get_string (const gchar * property_name, + GtkWidget *actual_widget, + GtkWidget * to_apply, + gboolean * apply_retval) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + gboolean apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + + if (apply_retval) + *apply_retval = apply; + + g_return_val_if_fail (widget != NULL, ""); + + /* Copy the extra data from the property value to the actual widget. */ + if (apply) + copy_translation_properties (property_name, widget, actual_widget); + + return ((char*) gtk_entry_get_text (GTK_ENTRY (widget))); +} + + +void +property_set_string (const gchar * property_name, + const gchar * value) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + GtkWidget *button; + g_return_if_fail (widget != NULL); + gtk_entry_set_text (GTK_ENTRY (widget), value ? value : ""); + + /* Make sure the '...' button is not visible, only for string properties. */ + if (g_object_get_data (G_OBJECT (widget), GladeIsStringPropertyKey)) + { + button = (GtkWidget *) g_hash_table_lookup (gb_property_buttons, + property_name); + gtk_widget_hide (button); + } +} + + +void +property_set_translatable_string (const gchar * property_name, + const gchar * value, + GtkWidget *actual_widget) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + GtkWidget *button; + g_return_if_fail (widget != NULL); + g_return_if_fail (actual_widget != NULL); + gtk_entry_set_text (GTK_ENTRY (widget), value ? value : ""); + + /* Make sure the '...' button is visible. */ + button = (GtkWidget *) g_hash_table_lookup (gb_property_buttons, + property_name); + gtk_widget_show (button); + + copy_translation_properties (property_name, actual_widget, widget); + + /* Set a special flag in the property value widget so we do show the + translatable properties in the dialog. */ + g_object_set_data (G_OBJECT (widget), GladeShowTranslationPropertiesKey, "TRUE"); +} + + +/* Note: returned string must be freed with g_free() */ +gchar * +property_get_text (const gchar * property_name, + GtkWidget *actual_widget, + GtkWidget * to_apply, + gboolean * apply_retval) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + gboolean apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + GtkWidget *text = GTK_BIN (widget)->child; + GtkTextBuffer *buffer; + GtkTextIter start, end; + + if (apply_retval) + *apply_retval = apply; + + g_return_val_if_fail (text != NULL, g_strdup ("")); + g_return_val_if_fail (GTK_IS_TEXT_VIEW (text), g_strdup ("")); + + /* Copy the extra data from the property value to the actual widget. */ + if (apply) + copy_translation_properties (property_name, widget, actual_widget); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)); + gtk_text_buffer_get_bounds (buffer, &start, &end); + + return (gtk_text_buffer_get_text (buffer, &start, &end, TRUE)); +} + + +void +property_set_text (const gchar * property_name, + const gchar * value) +{ + GtkWidget *text; + GtkTextBuffer *buffer; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + + text = GTK_BIN (widget)->child; + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)); + gtk_text_buffer_set_text (buffer, value ? value : "", -1); + + /* Set a special flag in the property value widget so we don't show the + translatable properties in the dialog. */ + g_object_set_data (G_OBJECT (widget), GladeShowTranslationPropertiesKey, NULL); +} + + +void +property_set_translatable_text (const gchar * property_name, + const gchar * value, + GtkWidget * actual_widget) +{ + GtkWidget *text; + GtkTextBuffer *buffer; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + g_return_if_fail (actual_widget != NULL); + + text = GTK_BIN (widget)->child; + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)); + gtk_text_buffer_set_text (buffer, value ? value : "", -1); + + copy_translation_properties (property_name, actual_widget, widget); + + /* Set a special flag in the property value widget so we do show the + translatable properties in the dialog. */ + g_object_set_data (G_OBJECT (widget), GladeShowTranslationPropertiesKey, "TRUE"); +} + + +gint +property_get_int (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply) +{ + gchar *text; + gint value = 0; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, 0); + if (GTK_IS_SPIN_BUTTON (widget)) + { + return gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget)); + } + else + { + text = (char*) gtk_entry_get_text (GTK_ENTRY (widget)); + sscanf (text, "%i", &value); + return value; + } +} + + +gint +property_get_optional_int (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply, + gboolean * is_set) +{ + gchar *text; + gint value = 0; + GtkWidget *widget, *button; + + widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, 0); + + button = (GtkWidget *) g_hash_table_lookup (gb_property_buttons, + property_name); + g_return_val_if_fail (button != NULL, 0); + + if (is_set) + *is_set = GTK_TOGGLE_BUTTON (button)->active ? TRUE : FALSE; + + if (GTK_IS_SPIN_BUTTON (widget)) + { + return gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget)); + } + else + { + text = (char*) gtk_entry_get_text (GTK_ENTRY (widget)); + sscanf (text, "%i", &value); + return value; + } +} + + +void +property_set_int (const gchar * property_name, + gint value) +{ + gchar buffer[128]; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + if (GTK_IS_SPIN_BUTTON (widget)) + { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value); + } + else + { + sprintf (buffer, "%i", value); + gtk_entry_set_text (GTK_ENTRY (widget), buffer); + } +} + + +void +property_set_optional_int (const gchar * property_name, + gint value, + gboolean is_set) +{ + gchar buffer[128]; + GtkWidget *widget, *button; + + widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + + button = (GtkWidget *) g_hash_table_lookup (gb_property_buttons, + property_name); + g_return_if_fail (button != NULL); + + if (GTK_IS_SPIN_BUTTON (widget)) + { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value); + } + else + { + sprintf (buffer, "%i", value); + gtk_entry_set_text (GTK_ENTRY (widget), buffer); + } + + gtk_widget_set_sensitive (widget, is_set); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), is_set); +} + + +gfloat +property_get_float (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply) +{ + gchar *text; + gfloat value = 0; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, 0); + if (GTK_IS_SPIN_BUTTON (widget)) + { + return gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget)); + } + else + { + text = (char*) gtk_entry_get_text (GTK_ENTRY (widget)); + sscanf (text, "%f", &value); + return value; + } +} + + +void +property_set_float (const gchar * property_name, + gfloat value) +{ + gchar buffer[128]; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + if (GTK_IS_SPIN_BUTTON (widget)) + { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value); + } + else + { + sprintf (buffer, "%g", value); + gtk_entry_set_text (GTK_ENTRY (widget), buffer); + } +} + + +gboolean +property_get_bool (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, FALSE); + return (GTK_TOGGLE_BUTTON (widget)->active ? TRUE : FALSE); +} + + +void +property_set_bool (const gchar * property_name, + gint value) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value); +} + + +gchar * +property_get_choice (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply) +{ + GtkWidget *label; + gchar *label_text; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, NULL); + label = GTK_BIN (widget)->child; + if (!label) + { + GtkWidget *menuitem = gtk_menu_get_active (GTK_MENU (GTK_OPTION_MENU (widget)->menu)); + if (menuitem) + label = GTK_BIN (menuitem)->child; + } + if (!label || !GTK_IS_LABEL (label)) + { + g_warning ("Couldn't find option menu label"); + return NULL; + } + label_text = (gchar*) gtk_label_get_text (GTK_LABEL (label)); + return label_text; +} + + +void +property_set_choice (const gchar * property_name, + gint value) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + gtk_option_menu_set_history (GTK_OPTION_MENU (widget), value); +} + + +gchar * +property_get_combo (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, ""); + return ((char*) gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (widget)->entry))); +} + + +void +property_set_combo (const gchar * property_name, + const gchar * value) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (widget)->entry), value ? value : ""); +} + + +void +property_set_combo_strings (const gchar * property_name, + GList * choices) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + if (choices) + gtk_combo_set_popdown_strings (GTK_COMBO (widget), choices); + else + gtk_list_clear_items (GTK_LIST (GTK_COMBO (widget)->list), 0, -1); +} + + +GdkColor * +property_get_color (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply) +{ + GdkColor *color; + GtkWidget *preview; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, NULL); + preview = GTK_BIN (widget)->child; + g_return_val_if_fail (GTK_IS_DRAWING_AREA (preview), NULL); + color = gtk_object_get_data (GTK_OBJECT (preview), GbColorKey); + g_return_val_if_fail (color != NULL, NULL); + return color; +} + + +void +property_set_color (const gchar * property_name, + GdkColor * value) +{ + GtkWidget *preview; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + preview = GTK_BIN (widget)->child; + g_return_if_fail (GTK_IS_DRAWING_AREA (preview)); + show_color_in_preview (preview, value); +} + + +GdkPixmap * +property_get_bgpixmap (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply, + gchar ** filename) +{ + GtkWidget *drawing_area; + GdkPixmap *gdkpixmap; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, NULL); + drawing_area = GTK_BIN (widget)->child; + g_return_val_if_fail (GTK_IS_DRAWING_AREA (drawing_area), NULL); + + gdkpixmap = gtk_object_get_data (GTK_OBJECT (drawing_area), GbBgPixmapKey); + *filename = gtk_object_get_data (GTK_OBJECT (drawing_area), GbBgFilenameKey); + return gdkpixmap; +} + + +void +property_set_bgpixmap (const gchar * property_name, + GdkPixmap * gdkpixmap, + const gchar * filename) +{ + GtkWidget *drawing_area; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + drawing_area = GTK_BIN (widget)->child; + g_return_if_fail (GTK_IS_DRAWING_AREA (drawing_area)); + + set_pixmap (drawing_area, gdkpixmap, filename); +} + + +static void +set_pixmap (GtkWidget * drawing_area, GdkPixmap * gdkpixmap, + const gchar * filename) +{ + GdkPixmap *old_gdkpixmap; + gchar *old_filename, *filename_copy; + + /* free/unref any existing values */ + old_gdkpixmap = gtk_object_get_data (GTK_OBJECT (drawing_area), GbBgPixmapKey); + if (old_gdkpixmap) + gdk_pixmap_unref (old_gdkpixmap); + old_filename = gtk_object_get_data (GTK_OBJECT (drawing_area), GbBgFilenameKey); + g_free (old_filename); + + gtk_object_set_data (GTK_OBJECT (drawing_area), GbBgPixmapKey, gdkpixmap); + filename_copy = filename ? g_strdup (filename) : NULL; + gtk_object_set_data (GTK_OBJECT (drawing_area), GbBgFilenameKey, filename_copy); + + if (gdkpixmap) + gdk_pixmap_ref (gdkpixmap); + + show_pixmap_in_drawing_area (drawing_area, gdkpixmap); +} + + +gpointer +property_get_dialog (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply) +{ + gpointer dialog_value; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, NULL); + dialog_value = gtk_object_get_data (GTK_OBJECT (widget), GbDialogValueKey); + if (dialog_value) + return dialog_value; + else + return ((char*) gtk_entry_get_text (GTK_ENTRY (widget))); +} + + +void +property_set_dialog (const gchar * property_name, + const gchar * string, + gconstpointer value) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + if (value) + gtk_object_set_data (GTK_OBJECT (widget), GbDialogValueKey, + (gpointer) value); + gtk_entry_set_text (GTK_ENTRY (widget), string ? string : ""); +} + + +gchar * +property_get_filename (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, ""); + return gtk_object_get_data (GTK_OBJECT (widget), GbFilenameValueKey); +} + + +void +property_set_filename (const gchar * property_name, + const gchar * value) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + gtk_object_set_data_full (GTK_OBJECT (widget), GbFilenameValueKey, + g_strdup (value), value ? g_free : NULL); + + if (GTK_IS_ENTRY (widget)) + gtk_entry_set_text (GTK_ENTRY (widget), value ? g_basename (value) : ""); + else if (GTK_IS_COMBO (widget)) + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (widget)->entry), + value ? g_basename (value) : ""); + else + g_warning ("Invalid filename property"); +} + + +GdkFont * +property_get_font (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply, + gchar ** xlfd_fontname) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, NULL); + *xlfd_fontname = gtk_object_get_data (GTK_OBJECT (widget), GbFontSpecKey); + return (gtk_object_get_data (GTK_OBJECT (widget), GbFontKey)); +} + + +void +property_set_font (const gchar * property_name, + GdkFont * font, + const gchar * xlfd_fontname) +{ + GdkFont *old_font; + gchar *old_xlfd_fontname; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + + old_font = gtk_object_get_data (GTK_OBJECT (widget), GbFontKey); + if (old_font) + gdk_font_unref (old_font); + if (font) + gdk_font_ref (font); + gtk_object_set_data (GTK_OBJECT (widget), GbFontKey, font); + old_xlfd_fontname = gtk_object_get_data (GTK_OBJECT (widget), GbFontSpecKey); + g_free (old_xlfd_fontname); + gtk_object_set_data (GTK_OBJECT (widget), GbFontSpecKey, g_strdup (xlfd_fontname)); + gtk_entry_set_text (GTK_ENTRY (widget), get_font_name_from_spec (xlfd_fontname)); +} + + +/* This searches through a GtkCombo's popup list to find the listitem whose + text matches the text in the GtkEntry. If it finds it, it returns the + stock_id stored in the listitem's data, else it returns NULL. */ +#if 0 +static gchar* +find_stock_id (GtkCombo *value) +{ + gchar *text; + GList *elem; + + text = (gchar*) gtk_entry_get_text (GTK_ENTRY (value->entry)); + + for (elem = GTK_LIST (value->list)->children; elem; elem = elem->next) + { + GtkWidget *listitem = elem->data; + gchar *item_text = glade_util_gtk_combo_func (GTK_LIST_ITEM (listitem)); + if (!strcmp (item_text, text)) + return gtk_object_get_data (GTK_OBJECT (listitem), GladeStockIDKey); + } + + return NULL; +} +#endif + + +gchar* +property_get_stock_item (const gchar *property_name, + GtkWidget *to_apply, + gboolean *apply) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + gchar *stock_id; + + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, 0); + + /* We want to return the stock_id corresponding to the currently selected + item. */ + stock_id = (gchar*) gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (widget)->entry)); + return (*stock_id) ? stock_id : NULL; +} + + +/* This searches through a GtkCombo's popup list to find the listitem whose + stock_id matches the given stock_id. */ +#if 0 +static GtkWidget* +find_stock_item (GtkCombo *value, const gchar *stock_id) +{ + GList *elem; + + if (!stock_id || !stock_id[0]) + return NULL; + + for (elem = GTK_LIST (value->list)->children; elem; elem = elem->next) + { + GtkWidget *listitem = elem->data; + gchar *elem_stock_id; + + elem_stock_id = gtk_object_get_data (GTK_OBJECT (listitem), + GladeStockIDKey); + if (elem_stock_id && !strcmp (stock_id, elem_stock_id)) + return listitem; + } + + return NULL; +} +#endif + + +void +property_set_stock_item (const gchar *property_name, + const gchar *stock_id) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (widget)->entry), + stock_id ? stock_id : ""); +} + + +void +property_set_stock_item_icon_size (const gchar *property_name, + GtkIconSize icon_size) +{ + GtkWidget *value; + + value = property_get_value_widget (property_name); + + gtk_object_set_data (GTK_OBJECT (value), GladeIconSizeKey, + GINT_TO_POINTER (icon_size)); + + show_stock_items (value); +} + + +gchar* +property_get_icon (const gchar *property_name, + GtkWidget *to_apply, + gboolean *apply) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + gchar *text, *filename; + + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, 0); + + /* If the field is empty, return NULL. */ + text = (gchar*) gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (widget)->entry)); + if (!*text) + return NULL; + + /* Check if the text in the entry matches the basename of any filename + set. If it does, return the entire filename. Otherwise return the + entry contents. */ + filename = gtk_object_get_data (GTK_OBJECT (widget), GbFilenameValueKey); + if (filename) + { + const gchar *basename = g_basename (filename); + if (!strcmp (basename, text)) + return filename; + } + + return text; +} + + +void +property_set_icon (const gchar *property_name, + const gchar *icon) +{ + const gchar *value = NULL; + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + + /* We need to determine whether icon is a stock id or a filename. */ + if (glade_util_check_is_stock_id (icon)) + { + value = icon; + gtk_object_remove_data (GTK_OBJECT (widget), GbFilenameValueKey); + } + else + { + value = icon ? g_basename (icon) : ""; + gtk_object_set_data_full (GTK_OBJECT (widget), GbFilenameValueKey, + g_strdup (icon), icon ? g_free : NULL); + } + + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (widget)->entry), + value ? value : ""); + + show_stock_items (widget); +} + + +void +property_set_icon_size (const gchar *property_name, + GtkIconSize icon_size) +{ + GtkWidget *value; + + value = property_get_value_widget (property_name); + + gtk_object_set_data (GTK_OBJECT (value), GladeIconSizeKey, + GINT_TO_POINTER (icon_size)); + + show_stock_items (value); +} + + +void +property_set_icon_filesel (const gchar *property_name, + gboolean filesel) +{ + GtkWidget *button; + + button = (GtkWidget *) g_hash_table_lookup (gb_property_buttons, + property_name); + + if (filesel) + gtk_widget_show (button); + else + gtk_widget_hide (button); +} + + +gchar * +property_get_named_icon (const gchar * property_name, + GtkWidget * to_apply, + gboolean * apply) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + if (apply) + *apply = (!to_apply || to_apply == widget) ? TRUE : FALSE; + g_return_val_if_fail (widget != NULL, ""); + return ((char*) gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (widget)->entry))); +} + + +void +property_set_named_icon (const gchar * property_name, + const gchar * value) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (widget)->entry), value ? value : ""); +} + +/* + * For getting the widgets used to display properties. + */ +GtkWidget* +property_get_value_widget (const gchar *property_name) +{ + return (GtkWidget *) g_hash_table_lookup (gb_property_values, property_name); +} + + +/* + * Setting properties sensitive/insensitive + */ +void +property_set_sensitive (const gchar * property_name, gboolean sensitive) +{ + property_set_sensitive_full (property_name, sensitive, sensitive, TRUE); +} + +void +property_set_sensitive_full (const gchar * property_name, + gboolean label_sensitive, + gboolean value_sensitive, + gboolean button_visible) +{ + GtkWidget *value, *label, *button; + + value = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (value != NULL); + gtk_widget_set_sensitive (value, value_sensitive); + + label = (GtkWidget *) g_hash_table_lookup (gb_property_labels, + property_name); + if (label) + gtk_widget_set_sensitive (label, label_sensitive); + + button = (GtkWidget *) g_hash_table_lookup (gb_property_buttons, + property_name); + if (button) + { + if (button_visible) + { + gtk_widget_show (button); + if (GTK_IS_TOGGLE_BUTTON (button)) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + value_sensitive); + else + gtk_widget_set_sensitive (button, value_sensitive); + } + else + gtk_widget_hide (button); + } +} + + +void +property_set_visible (const gchar * property_name, + gboolean visible) +{ + GtkWidget *value, *label, *button; + + value = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (value != NULL); + if (visible) + gtk_widget_show (value); + else + gtk_widget_hide (value); + + + label = (GtkWidget *) g_hash_table_lookup (gb_property_labels, + property_name); + if (label) + { + if (visible) + gtk_widget_show (label); + else + gtk_widget_hide (label); + } + + button = (GtkWidget *) g_hash_table_lookup (gb_property_buttons, + property_name); + if (button) + { + if (visible) + gtk_widget_show (button); + else + gtk_widget_hide (button); + } + + /* Hide the parent of the value/button if it isn't the main table. + We need this since sometimes the table doesn't resize properly, and we + are left with a blank row. */ + if (value->parent && !GTK_IS_TABLE (value->parent)) + { + if (visible) + gtk_widget_show (value->parent); + else + gtk_widget_hide (value->parent); + } +} + + +/* + * Setting properties valid/invalid + */ +void +property_set_valid (const gchar * property_name, gboolean valid) +{ + GtkWidget *widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (widget != NULL); + + if (valid) + gtk_widget_set_style (widget, gtk_widget_get_default_style ()); + else + gtk_widget_set_style (widget, invalid_style); +} + + +/* + * Color previews - for showing style colors + */ + +static GtkWidget * +create_color_preview () +{ + GtkWidget *preview; + GdkColormap *colormap; + GdkColor *color; + + preview = gtk_drawing_area_new (); + gtk_widget_show (preview); + + color = g_new (GdkColor, 1); + color->red = color->green = color->blue = 0xFFFF; + gtk_object_set_data (GTK_OBJECT (preview), GbColorKey, color); + + /* Allocate the color. */ + colormap = gtk_widget_get_colormap (preview); + if (!gdk_color_alloc (colormap, color)) + { + g_warning ("Couldn't allocate white color"); + } + + return preview; +} + + +/* Returns TRUE if the color has changed. */ +static gboolean +show_color_in_preview (GtkWidget * preview, GdkColor *new_color) +{ + GdkColormap *colormap; + GdkColor *color; + gulong pixel; + + color = gtk_object_get_data (GTK_OBJECT (preview), GbColorKey); + g_return_val_if_fail (color != NULL, FALSE); + + /* If it is the same, return FALSE. */ + if (color->red == new_color->red + && color->green == new_color->green + && color->blue == new_color->blue) + return FALSE; + + /* Allocate the color. */ + colormap = gtk_widget_get_colormap (preview); + if (!gdk_color_alloc (colormap, new_color)) + { + /* If we can't allocate the colour, keep the old one. */ + g_warning ("Couldn't allocate color"); + return FALSE; + } + + /* Free the old color. */ + pixel = color->pixel; + gdk_colors_free (colormap, &pixel, 1, 0); + + *color = *new_color; + + /* The drawing area doesn't get a window until the notebook page is shown! */ + if (preview->window) + { + gdk_window_set_background (preview->window, color); + gdk_window_clear (preview->window); + } + + return TRUE; +} + + +/* + * Pixmap values (displayed as background of drawing area) + */ +static void +show_pixmap_in_drawing_area (GtkWidget * drawing_area, GdkPixmap * gdkpixmap) +{ + g_return_if_fail (GTK_IS_DRAWING_AREA (drawing_area)); + /* The drawing area doesn't get a window until the notebook page is shown! */ + if (drawing_area->window == NULL) + return; + if (gdkpixmap) + { + gdk_window_set_back_pixmap (drawing_area->window, gdkpixmap, FALSE); + } + else + { + gdk_window_set_background (drawing_area->window, + &drawing_area->style->bg[GTK_STATE_NORMAL]); + } + gdk_window_clear (drawing_area->window); +} + + +static gint +expose_pixmap (GtkWidget * drawing_area, GdkEventExpose * event, gpointer data) +{ + GdkPixmap *gdkpixmap; + + g_return_val_if_fail (GTK_IS_DRAWING_AREA (drawing_area), FALSE); + + gdkpixmap = gtk_object_get_data (GTK_OBJECT (drawing_area), GbBgPixmapKey); + show_pixmap_in_drawing_area (drawing_area, gdkpixmap); + on_color_draw (drawing_area, NULL); + + return FALSE; +} + + +/* + * The Events dialog, used for selecting which X events to receive. + */ + +static void +show_events_dialog (GtkWidget * widget, gpointer value) +{ + GtkWidget *dialog, *clist, *scrolled_win; + GtkWindow *transient_parent; + gchar *titles[2]; + const gchar *row[2]; + int i; + gchar *event_mask_string; + gint event_mask_value; + + transient_parent = (GtkWindow*) glade_util_get_toplevel (widget); + dialog = gtk_dialog_new_with_buttons (_("Select X Events"), + transient_parent, 0, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + gtk_window_set_wmclass (GTK_WINDOW (dialog), "select_events", "Glade"); + + titles[0] = _("Event Mask"); + titles[1] = _("Description"); + clist = gtk_clist_new_with_titles (2, titles); + gtk_clist_column_titles_passive (GTK_CLIST (clist)); + gtk_clist_set_column_width (GTK_CLIST (clist), 0, 230); + gtk_clist_set_column_width (GTK_CLIST (clist), 1, 100); + gtk_clist_set_selection_mode (GTK_CLIST (clist), GTK_SELECTION_MULTIPLE); + gtk_widget_set_usize (clist, 500, 350); + gtk_widget_show (clist); + + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (scrolled_win), clist); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), scrolled_win, + TRUE, TRUE, 0); + gtk_widget_show (scrolled_win); + + /* Insert events & descriptions */ + gtk_clist_freeze (GTK_CLIST (clist)); + for (i = 0; i < GB_EVENT_MASKS_COUNT; i++) + { + row[0] = GbEventMaskSymbols[i]; + row[1] = _(GbEventMaskDescriptions[i]); + gtk_clist_append (GTK_CLIST (clist), (gchar**)row); + } + + /* Select rows according to current mask setting */ + event_mask_string = (char*) gtk_entry_get_text (GTK_ENTRY (value)); + event_mask_value = property_events_string_to_value (event_mask_string); + for (i = 0; i < GB_EVENT_MASKS_COUNT; i++) + { + if (event_mask_value & GbEventMaskValues[i]) + { + gtk_clist_select_row (GTK_CLIST (clist), i, 0); + } + } + gtk_clist_thaw (GTK_CLIST (clist)); + + /* Save pointer to value to use when OK pressed */ + gtk_object_set_data (GTK_OBJECT (clist), GbValueWidgetKey, value); + + gtk_signal_connect (GTK_OBJECT (dialog), "response", + GTK_SIGNAL_FUNC (on_events_dialog_response), + clist); + + gtk_widget_show (GTK_WIDGET (dialog)); +} + + +static void +on_events_dialog_response (GtkWidget * widget, gint response_id, + GtkWidget * clist) +{ + gint row, mask_value = 0; + GtkWidget *dialog, *value; + GList *selection = GTK_CLIST (clist)->selection; + + dialog = gtk_widget_get_toplevel (clist); + + if (response_id == GTK_RESPONSE_OK) + { + while (selection) + { + row = GPOINTER_TO_INT (selection->data); + mask_value |= GbEventMaskValues[row]; + selection = selection->next; + } + + value = gtk_object_get_data (GTK_OBJECT (clist), GbValueWidgetKey); + g_return_if_fail (value != NULL); + gtk_entry_set_text (GTK_ENTRY (value), + property_events_value_to_string (mask_value)); + } + + gtk_widget_destroy (dialog); +} + + +/* Converts the events gint to a string of 0s and 1s for displaying */ +gchar * +property_events_value_to_string (gint event_mask) +{ + static gchar buf[GB_EVENT_MASKS_COUNT + 2]; + int i; + + for (i = 0; i < GB_EVENT_MASKS_COUNT; i++) + { + if (event_mask & GbEventMaskValues[i]) + buf[GB_EVENT_MASKS_COUNT - i - 1] = '1'; + else + buf[GB_EVENT_MASKS_COUNT - i - 1] = '0'; + } + buf[GB_EVENT_MASKS_COUNT] = '0'; + buf[GB_EVENT_MASKS_COUNT + 1] = '\0'; + return buf; +} + + +/* Converts the string of 0s and 1s back to a gint event mask */ +gint +property_events_string_to_value (const gchar * event_string) +{ + gint i, value = 0; + + if (strlen (event_string) < GB_EVENT_MASKS_COUNT) + return 0; + for (i = 0; i < GB_EVENT_MASKS_COUNT; i++) + { + if (event_string[GB_EVENT_MASKS_COUNT - i - 1] == '1') + value |= GbEventMaskValues[i]; + } + return value; +} + + +/* + * The Accelerators page + */ + +static void +on_accelerator_add (GtkWidget * widget, GtkWidget * clist) +{ + gchar modifiers[4]; + gchar *row[3]; + gchar *key, *signal; + + key = property_get_string (GbAccelKey, NULL, NULL, NULL); + if (strlen (key) == 0) + { + glade_util_show_message_box (_("You need to set the accelerator key"), + widget); + return; + } + signal = property_get_string (GbAccelSignal, NULL, NULL, NULL); + if (strlen (signal) == 0) + { + glade_util_show_message_box (_("You need to set the signal to emit"), + widget); + return; + } + + modifiers[0] = modifiers[1] = modifiers[2] = ' '; + modifiers[3] = '\0'; + if (GTK_TOGGLE_BUTTON (accel_control_button)->active) + modifiers[0] = 'C'; + if (GTK_TOGGLE_BUTTON (accel_shift_button)->active) + modifiers[1] = 'S'; + if (GTK_TOGGLE_BUTTON (accel_alt_button)->active) + modifiers[2] = 'A'; + + row[ACCEL_MODIFIERS_COL] = modifiers; + row[ACCEL_KEY_COL] = key; + row[ACCEL_SIGNAL_COL] = signal; + gtk_clist_append (GTK_CLIST (clist), row); + + /* clear the key & signal fields */ + property_set_string (GbAccelKey, ""); + property_set_string (GbAccelSignal, ""); + + on_property_changed (clist, clist); +} + + +static void +on_accelerator_update (GtkWidget * widget, GtkWidget * clist) +{ + gchar modifiers[4]; + gchar *key, *signal; + GList *selection = GTK_CLIST (clist)->selection; + gint row; + + if (!selection) + return; + row = GPOINTER_TO_INT (selection->data); + + key = property_get_string (GbAccelKey, NULL, NULL, NULL); + if (strlen (key) == 0) + { + glade_util_show_message_box (_("You need to set the accelerator key"), + widget); + return; + } + signal = property_get_string (GbAccelSignal, NULL, NULL, NULL); + if (strlen (signal) == 0) + { + glade_util_show_message_box (_("You need to set the signal to emit"), + widget); + return; + } + + modifiers[0] = modifiers[1] = modifiers[2] = ' '; + modifiers[3] = '\0'; + if (GTK_TOGGLE_BUTTON (accel_control_button)->active) + modifiers[0] = 'C'; + if (GTK_TOGGLE_BUTTON (accel_shift_button)->active) + modifiers[1] = 'S'; + if (GTK_TOGGLE_BUTTON (accel_alt_button)->active) + modifiers[2] = 'A'; + + gtk_clist_set_text (GTK_CLIST (clist), row, ACCEL_MODIFIERS_COL, modifiers); + gtk_clist_set_text (GTK_CLIST (clist), row, ACCEL_KEY_COL, key); + gtk_clist_set_text (GTK_CLIST (clist), row, ACCEL_SIGNAL_COL, signal); + + on_property_changed (clist, clist); +} + + +static void +on_accelerator_delete (GtkWidget * widget, GtkWidget * clist) +{ + GList *selection = GTK_CLIST (clist)->selection; + gint row; + + if (!selection) + return; + row = GPOINTER_TO_INT (selection->data); + gtk_clist_remove (GTK_CLIST (clist), row); + /* clear the key & signal fields */ + property_set_string (GbAccelKey, ""); + property_set_string (GbAccelSignal, ""); + + on_property_changed (clist, clist); +} + + +static void +on_accelerator_clear (GtkWidget * widget, GtkWidget * clist) +{ + property_set_string (GbAccelKey, ""); + property_set_string (GbAccelSignal, ""); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (accel_control_button), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (accel_shift_button), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (accel_alt_button), FALSE); +} + + +static void +on_accelerator_select (GtkWidget * clist, gint row, gint column, + GdkEventButton * event, gpointer user_data) +{ + gchar *modifiers, *key, *signal; + gint len; + + gtk_clist_get_text (GTK_CLIST (clist), row, ACCEL_MODIFIERS_COL, &modifiers); + gtk_clist_get_text (GTK_CLIST (clist), row, ACCEL_KEY_COL, &key); + gtk_clist_get_text (GTK_CLIST (clist), row, ACCEL_SIGNAL_COL, &signal); + + len = strlen (modifiers); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (accel_control_button), + len >= 1 && modifiers[0] != ' '); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (accel_shift_button), + len >= 2 && modifiers[1] != ' '); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (accel_alt_button), + len >= 3 && modifiers[2] != ' '); + + property_set_string (GbAccelKey, key); + property_set_string (GbAccelSignal, signal); +} + + +void +property_clear_accelerators () +{ + gtk_clist_clear (GTK_CLIST (accel_clist)); + /* clear the key & signal fields */ + property_set_string (GbAccelKey, ""); + property_set_string (GbAccelSignal, ""); +} + + +void +property_add_accelerator (GladeAccelerator * accel) +{ + gchar modifiers[4]; + gchar *row[3]; + + modifiers[0] = modifiers[1] = modifiers[2] = ' '; + modifiers[3] = '\0'; + if (accel->modifiers & GDK_CONTROL_MASK) + modifiers[0] = 'C'; + if (accel->modifiers & GDK_SHIFT_MASK) + modifiers[1] = 'S'; + /* The Alt key uses GDK_MOD1_MASK */ + if (accel->modifiers & GDK_MOD1_MASK) + modifiers[2] = 'A'; + + row[ACCEL_MODIFIERS_COL] = modifiers; + row[ACCEL_KEY_COL] = accel->key; + row[ACCEL_SIGNAL_COL] = accel->signal; + gtk_clist_append (GTK_CLIST (accel_clist), row); +} + + +gboolean +property_is_accel_clist (GtkWidget * widget) +{ + return (widget == accel_clist) ? TRUE : FALSE; +} + + +GList * +property_get_accelerators () +{ + gint row, len; + GList *accelerators = NULL; + GladeAccelerator *accel; + gchar *modifiers, *key, *signal; + + for (row = 0; row < GTK_CLIST (accel_clist)->rows; row++) + { + accel = g_new (GladeAccelerator, 1); + + gtk_clist_get_text (GTK_CLIST (accel_clist), row, + ACCEL_MODIFIERS_COL, &modifiers); + gtk_clist_get_text (GTK_CLIST (accel_clist), row, + ACCEL_KEY_COL, &key); + gtk_clist_get_text (GTK_CLIST (accel_clist), row, + ACCEL_SIGNAL_COL, &signal); + + len = strlen (modifiers); + accel->modifiers = 0; + if (len >= 1 && modifiers[0] != ' ') + accel->modifiers |= GDK_CONTROL_MASK; + if (len >= 2 && modifiers[1] != ' ') + accel->modifiers |= GDK_SHIFT_MASK; + if (len >= 3 && modifiers[2] != ' ') + accel->modifiers |= GDK_MOD1_MASK; + + accel->key = g_strdup (key); + accel->signal = g_strdup (signal); + accelerators = g_list_append (accelerators, accel); + } + return accelerators; +} + + +/* + * The Accelerator Keys dialog for selecting an accelerator key. + */ + +static void +show_keys_dialog (GtkWidget * widget, gpointer value) +{ + GladeKeysDialog *dialog; + GtkWidget *transient_parent; + + dialog = GLADE_KEYS_DIALOG (glade_keys_dialog_new ()); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + transient_parent = glade_util_get_toplevel (widget); + if (GTK_IS_WINDOW (transient_parent)) + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (transient_parent)); + /* Save pointer to value to use when OK pressed */ + gtk_object_set_data (GTK_OBJECT (dialog), GbValueWidgetKey, value); + gtk_signal_connect (GTK_OBJECT (dialog->clist), "select_row", + GTK_SIGNAL_FUNC (on_keys_clist_select), dialog); + gtk_signal_connect (GTK_OBJECT (dialog), "response", + GTK_SIGNAL_FUNC (on_keys_dialog_response), + NULL); + gtk_widget_show (GTK_WIDGET (dialog)); +} + + +static void +on_keys_dialog_response (GtkWidget * widget, gint response_id, gpointer data) +{ + GladeKeysDialog *dialog; + GtkWidget *value; + gchar *key_symbol; + + dialog = (GladeKeysDialog*) widget; + if (response_id == GTK_RESPONSE_OK) + { + key_symbol = glade_keys_dialog_get_key_symbol (dialog); + if (key_symbol) + { + value = gtk_object_get_data (GTK_OBJECT (dialog), GbValueWidgetKey); + g_return_if_fail (value != NULL); + gtk_entry_set_text (GTK_ENTRY (value), key_symbol); + } + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + + +static void +on_keys_clist_select (GtkWidget * widget, gint row, gint column, + GdkEventButton * bevent, gpointer data) +{ + if (bevent && bevent->type == GDK_2BUTTON_PRESS) + on_keys_dialog_response (GTK_WIDGET (data), GTK_RESPONSE_OK, NULL); +} + + +/* + * The Signals page + */ + +static void +on_signal_add (GtkWidget * widget, GtkWidget * clist) +{ + gchar *row_data[5]; + gchar *signal, *handler, *object, *data; + gboolean after; + gint row; + time_t *last_mod_time; + + signal = property_get_string (GbSignalName, NULL, NULL, NULL); + if (strlen (signal) == 0) + { + glade_util_show_message_box (_("You need to set the signal name"), + widget); + return; + } + handler = property_get_combo (GbSignalHandler, NULL, NULL); + if (strlen (handler) == 0) + { + glade_util_show_message_box (_("You need to set the handler for the signal"), widget); + return; + } + object = property_get_string (GbSignalObject, NULL, NULL, NULL); + after = property_get_bool (GbSignalAfter, NULL, NULL); + data = property_get_string (GbSignalData, NULL, NULL, NULL); + + row_data[SIGNAL_NAME_COL] = signal; + row_data[SIGNAL_HANDLER_COL] = handler; + row_data[SIGNAL_OBJECT_COL] = object; + row_data[SIGNAL_AFTER_COL] = after ? "Y" : ""; + row_data[SIGNAL_DATA_COL] = data; + row = gtk_clist_append (GTK_CLIST (clist), row_data); + + /* Set the last modification time to the current time. */ + last_mod_time = g_chunk_new (time_t, signal_mem_chunk); + *last_mod_time = time (NULL); + if (*last_mod_time == (time_t) -1) + g_warning ("Error getting current time"); + gtk_clist_set_row_data (GTK_CLIST (signal_clist), row, last_mod_time); + + /* clear the fields */ + property_set_string (GbSignalName, ""); + property_set_combo (GbSignalHandler, ""); + property_set_string (GbSignalObject, ""); + property_set_string (GbSignalData, ""); + property_set_bool (GbSignalAfter, FALSE); + + on_property_changed (clist, clist); +} + + +static void +on_signal_update (GtkWidget * widget, GtkWidget * clist) +{ + gchar *signal, *handler, *object, *data; + gchar *old_signal, *old_handler, *old_data; + gboolean after; + GList *selection = GTK_CLIST (clist)->selection; + gint row; + time_t *last_mod_time; + + if (!selection) + return; + row = GPOINTER_TO_INT (selection->data); + + signal = property_get_string (GbSignalName, NULL, NULL, NULL); + if (strlen (signal) == 0) + { + glade_util_show_message_box (_("You need to set the signal name"), + widget); + return; + } + handler = property_get_combo (GbSignalHandler, NULL, NULL); + if (strlen (handler) == 0) + { + glade_util_show_message_box (_("You need to set the handler for the signal"), widget); + return; + } + object = property_get_string (GbSignalObject, NULL, NULL, NULL); + after = property_get_bool (GbSignalAfter, NULL, NULL); + data = property_get_string (GbSignalData, NULL, NULL, NULL); + + /* We update the last_mod_time if the signal, handler or data have changed.*/ + gtk_clist_get_text (GTK_CLIST (clist), row, SIGNAL_NAME_COL, &old_signal); + gtk_clist_get_text (GTK_CLIST (clist), row, SIGNAL_HANDLER_COL, &old_handler); + gtk_clist_get_text (GTK_CLIST (clist), row, SIGNAL_DATA_COL, &old_data); + if (strcmp (signal, old_signal) || strcmp (handler, old_handler) + || strcmp (data, old_data)) + { + last_mod_time = (time_t*) gtk_clist_get_row_data (GTK_CLIST (signal_clist), row); + *last_mod_time = time (NULL); + if (*last_mod_time == (time_t) -1) + g_warning ("Error getting current time"); + } + + gtk_clist_set_text (GTK_CLIST (clist), row, SIGNAL_NAME_COL, signal); + gtk_clist_set_text (GTK_CLIST (clist), row, SIGNAL_HANDLER_COL, handler); + gtk_clist_set_text (GTK_CLIST (clist), row, SIGNAL_OBJECT_COL, object); + gtk_clist_set_text (GTK_CLIST (clist), row, SIGNAL_AFTER_COL, + after ? "Y" : ""); + gtk_clist_set_text (GTK_CLIST (clist), row, SIGNAL_DATA_COL, data); + + on_property_changed (clist, clist); +} + + +static void +on_signal_delete (GtkWidget * widget, GtkWidget * clist) +{ + GList *selection = GTK_CLIST (clist)->selection; + gint row; + + if (!selection) + return; + row = GPOINTER_TO_INT (selection->data); + gtk_clist_remove (GTK_CLIST (clist), row); + /* clear the key & signal fields */ + property_set_string (GbSignalName, ""); + property_set_combo (GbSignalHandler, ""); + property_set_string (GbSignalObject, ""); + property_set_string (GbSignalData, ""); + + on_property_changed (clist, clist); +} + + +static void +on_signal_clear (GtkWidget * widget, GtkWidget * clist) +{ + property_set_string (GbSignalName, ""); + property_set_combo (GbSignalHandler, ""); + property_set_string (GbSignalObject, ""); + property_set_string (GbSignalData, ""); + property_set_bool (GbSignalAfter, FALSE); +} + + +static void +on_signal_select (GtkWidget * clist, + gint row, + gint column, + GdkEventButton * event, + gpointer user_data) +{ + gchar *signal, *handler, *object, *after, *data; + gtk_clist_get_text (GTK_CLIST (clist), row, SIGNAL_NAME_COL, &signal); + gtk_clist_get_text (GTK_CLIST (clist), row, SIGNAL_HANDLER_COL, &handler); + gtk_clist_get_text (GTK_CLIST (clist), row, SIGNAL_OBJECT_COL, &object); + gtk_clist_get_text (GTK_CLIST (clist), row, SIGNAL_AFTER_COL, &after); + gtk_clist_get_text (GTK_CLIST (clist), row, SIGNAL_DATA_COL, &data); + property_set_string (GbSignalName, signal); + property_set_combo (GbSignalHandler, handler); + property_set_string (GbSignalObject, object); + if (!strcmp (after, "Y")) + property_set_bool (GbSignalAfter, TRUE); + else + property_set_bool (GbSignalAfter, FALSE); + property_set_string (GbSignalData, data); +} + + +void +property_clear_signals () +{ + gtk_clist_clear (GTK_CLIST (signal_clist)); + /* clear the fields */ + property_set_string (GbSignalName, ""); + property_set_combo (GbSignalHandler, ""); + property_set_string (GbSignalObject, ""); + property_set_string (GbSignalData, ""); + + g_mem_chunk_reset (signal_mem_chunk); +} + + +void +property_add_signal (GladeSignal * signal) +{ + gchar *row_data[5]; + time_t *last_mod_time; + gint row; + + row_data[SIGNAL_NAME_COL] = signal->name ? signal->name : ""; + row_data[SIGNAL_HANDLER_COL] = signal->handler ? signal->handler : ""; + row_data[SIGNAL_OBJECT_COL] = signal->object ? signal->object : ""; + row_data[SIGNAL_AFTER_COL] = signal->after ? "Y" : ""; + row_data[SIGNAL_DATA_COL] = signal->data ? signal->data : ""; + row = gtk_clist_append (GTK_CLIST (signal_clist), row_data); + + last_mod_time = g_chunk_new (time_t, signal_mem_chunk); + *last_mod_time = signal->last_modification_time; + gtk_clist_set_row_data (GTK_CLIST (signal_clist), row, last_mod_time); +} + + +gboolean +property_is_signal_clist (GtkWidget * widget) +{ + return (widget == signal_clist) ? TRUE : FALSE; +} + + +GList * +property_get_signals () +{ + gint row; + GList *signals = NULL; + GladeSignal *signal; + gchar *name, *handler, *object, *after, *data; + time_t *time; + + for (row = 0; row < GTK_CLIST (signal_clist)->rows; row++) + { + signal = g_new (GladeSignal, 1); + + gtk_clist_get_text (GTK_CLIST (signal_clist), row, + SIGNAL_NAME_COL, &name); + gtk_clist_get_text (GTK_CLIST (signal_clist), row, + SIGNAL_HANDLER_COL, &handler); + gtk_clist_get_text (GTK_CLIST (signal_clist), row, + SIGNAL_OBJECT_COL, &object); + gtk_clist_get_text (GTK_CLIST (signal_clist), row, + SIGNAL_AFTER_COL, &after); + gtk_clist_get_text (GTK_CLIST (signal_clist), row, + SIGNAL_DATA_COL, &data); + time = gtk_clist_get_row_data (GTK_CLIST (signal_clist), row); + + signal->name = strlen (name) > 0 ? g_strdup (name) : NULL; + signal->handler = strlen (handler) > 0 ? g_strdup (handler) : NULL; + signal->object = strlen (object) > 0 ? g_strdup (object) : NULL; + signal->after = !strcmp (after, "Y") ? TRUE : FALSE; + signal->data = strlen (data) > 0 ? g_strdup (data) : NULL; + signal->last_modification_time = *time; + signals = g_list_append (signals, signal); + } + return signals; +} + + +/* + * The Signals dialog box for selecting a signal to handle. + */ + +static void +add_signals_for_type (GType type, GtkWidget *clist, GdkColor *inactive_fg, GdkColor *inactive_bg, gchar *current_signal, gboolean show_actions_only) +{ + guint *signals; + guint nsignals; + gchar *name; + gint i; + gint row; + GList *items, *elem; + GSignalQuery query_info; + + signals = g_signal_list_ids (type, &nsignals); + + row = GTK_CLIST (clist)->rows; + if (nsignals) + { + items = NULL; + for (i = 0; i < nsignals; i++) + { + g_signal_query (signals[i], &query_info); + + if (!show_actions_only + || query_info.signal_flags & G_SIGNAL_ACTION) + { + name = g_strdup (query_info.signal_name); + g_strdelimit (name, NULL, '_'); + items = g_list_prepend (items, name); + } + } + + if (items) + { + items = g_list_sort (items, (GCompareFunc)strcmp); + + /* This groups the signals by class, e.g. 'GtkButton signals'. */ + name = g_strdup_printf (_("%s signals"), gtk_type_name (type)); + gtk_clist_append (GTK_CLIST (clist), &name); + g_free (name); + gtk_clist_set_foreground (GTK_CLIST (clist), row, inactive_fg); + gtk_clist_set_background (GTK_CLIST (clist), row, inactive_bg); + /* Set this so we know when a row containing a class name is + selected. See on_signals_clist_select(). */ + gtk_clist_set_row_data (GTK_CLIST (clist), row, "ClassName"); + row++; + + elem = items; + while (elem) + { + name = elem->data; + gtk_clist_append (GTK_CLIST (clist), &name); + gtk_clist_set_shift (GTK_CLIST (clist), row, 0, 0, 10); + if (!strcmp (current_signal, name)) + { + gtk_clist_select_row (GTK_CLIST (clist), row, 0); + } + row++; + g_free (name); + elem = elem->next; + } + g_list_free (items); + } + } + + g_free (signals); +} + +static void +show_signals_dialog (GtkWidget * widget, gpointer value) +{ + GtkWidget *dialog, *clist, *scrolled_win; + GtkWindow *transient_parent; + gchar *titles[1]; + GType type; + GdkColor *inactive_fg, *inactive_bg; + GType *interfaces; + guint n_interfaces = 0; + gchar *current_signal; + gint i; + gboolean show_actions_only = FALSE; + + if (!property_widget) + return; + + /* For keyboard accelerators we only show ACTION signals, since they are + the only signals that can be used. */ + if (property_get_value_widget (GbAccelSignal) == value) + { + g_print ("Showing ACTION signals only\n"); + show_actions_only = TRUE; + } + + transient_parent = (GtkWindow*) glade_util_get_toplevel (widget); + dialog = gtk_dialog_new_with_buttons (_("Select Signal"), + transient_parent, 0, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + gtk_window_set_wmclass (GTK_WINDOW (dialog), "select_signal", "Glade"); + + titles[0] = _("Signals"); + clist = gtk_clist_new_with_titles (1, titles); + gtk_clist_column_titles_passive (GTK_CLIST (clist)); + gtk_widget_set_usize (clist, 240, 260); + gtk_widget_show (clist); + + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (scrolled_win), clist); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), scrolled_win, + TRUE, TRUE, 0); + gtk_widget_show(scrolled_win); + + /* Insert list of signals. */ + gtk_clist_freeze (GTK_CLIST (clist)); + + current_signal = (char*) gtk_entry_get_text (GTK_ENTRY (value)); + + /* For custom widgets we only allow standard widget signals to be set. */ + if (GLADE_IS_CUSTOM_WIDGET (property_widget)) + type = gtk_widget_get_type (); + else + type = GTK_OBJECT_TYPE (property_widget); + + inactive_fg = &widget->style->fg[GTK_STATE_INSENSITIVE]; + inactive_bg = &widget->style->bg[GTK_STATE_INSENSITIVE]; + + interfaces = g_type_interfaces (type, &n_interfaces); + for (i = 0; i < n_interfaces; i++) + { + add_signals_for_type (interfaces[i], clist, inactive_fg, inactive_bg, current_signal, show_actions_only); + } + g_free (interfaces); + + while (type) + { + add_signals_for_type (type, clist, inactive_fg, inactive_bg, current_signal, show_actions_only); + type = gtk_type_parent (type); + } + + gtk_clist_thaw (GTK_CLIST (clist)); + gtk_signal_connect (GTK_OBJECT (clist), "select_row", + GTK_SIGNAL_FUNC (on_signals_clist_select), clist); + + /* Save pointer to value to use when OK pressed */ + gtk_object_set_data (GTK_OBJECT (clist), GbValueWidgetKey, value); + + gtk_signal_connect (GTK_OBJECT (dialog), "response", + GTK_SIGNAL_FUNC (on_signals_dialog_response), + clist); + + gtk_widget_show (GTK_WIDGET (dialog)); +} + + +static void +on_signals_dialog_response (GtkWidget * widget, gint response_id, + GtkWidget * clist) +{ + gint row, page; + GtkWidget *dialog, *value, *handler; + GList *selection = GTK_CLIST (clist)->selection; + gchar *name, *handler_text; + + dialog = gtk_widget_get_toplevel (widget); + + if (response_id != GTK_RESPONSE_OK) + { + gtk_widget_destroy (dialog); + return; + } + + if (selection) + { + row = GPOINTER_TO_INT (selection->data); + value = gtk_object_get_data (GTK_OBJECT (clist), GbValueWidgetKey); + g_return_if_fail (value != NULL); + gtk_clist_get_text (GTK_CLIST (clist), row, 0, &name); + gtk_entry_set_text (GTK_ENTRY (value), name); + + /* If we're on the Signals page, and the current handler is empty, + insert an initial value of "on_<widget name>_<signal name>", + with '-' in widget name changed to '_' */ + page = gtk_notebook_get_current_page (GTK_NOTEBOOK (main_notebook)); + if (page == GB_PAGE_SIGNALS) + { + handler = (GtkWidget *) g_hash_table_lookup (gb_property_values, + GbSignalHandler); + g_return_if_fail (handler != NULL); + handler_text = (char*) gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (handler)->entry)); + + if (strlen (handler_text) == 0) + { + gchar buf[128]; + gchar *widget_name; + widget_name = g_strdup (gtk_widget_get_name (property_widget)); + if (widget_name + && strlen (widget_name) + strlen (name) + 5 < 128) + { + g_strdelimit (widget_name, "-", '_'); + sprintf (buf, "on_%s_%s", widget_name, name); + gtk_entry_set_text (GTK_ENTRY ( GTK_COMBO (handler)->entry), + buf); + gtk_editable_select_region (GTK_EDITABLE ( GTK_COMBO (handler)->entry), 0, -1); + } + g_free (widget_name); + gtk_widget_grab_focus (handler); + } + } + } + + gtk_widget_destroy (dialog); +} + + +static void +on_signals_clist_select (GtkWidget * widget, gint row, gint column, + GdkEventButton * bevent, gpointer data) +{ + /* Don't allow selection of widget class names */ + if (gtk_clist_get_row_data (GTK_CLIST (widget), row)) + gtk_clist_unselect_row (GTK_CLIST (widget), row, 0); + if (bevent && bevent->type == GDK_2BUTTON_PRESS) + { + on_signals_dialog_response (widget, GTK_RESPONSE_OK, widget); + } +} + + +/* + void + on_apply(GtkWidget *widget, gpointer data) + { + if (!property_widget) return; + gb_widget_apply_properties(property_widget, NULL); + } + */ + +void +property_set_auto_apply (gboolean value) +{ + auto_apply = value; +} + + +/* This is just used for debugging */ +#ifdef GLADE_DEBUG +static void +find_hash_value (const gchar * key, gpointer data, gpointer property) +{ + if (data == property) + MSG1 (" applying property: %s", key); +} +#endif + +static void +on_property_changed (GtkWidget * widget, GtkWidget * property) +{ + if (property_widget && auto_apply) + { +#ifdef GLADE_DEBUG + g_hash_table_foreach (gb_property_values, (GHFunc) find_hash_value, + property); +#endif + gb_widget_apply_properties (property_widget, property); + } +} + + +static gboolean +on_property_focus_out (GtkWidget * widget, GdkEventFocus *event, + GtkWidget * property) +{ + MSG ("In on_property_focus_out"); + on_property_changed (widget, property); + return FALSE; +} + + +/* + * Adjustments - handles adding/showing/applying of all 6 properties + */ +void +property_add_adjustment (const gchar * Values[], gint label_type) +{ + const gchar *default_labels[] = + {N_("Value:"), N_("Min:"), N_("Max:"), N_("Step Inc:"), + N_("Page Inc:"), N_("Page Size:")}; + const gchar *horz_labels[] = + {N_("H Value:"), N_("H Min:"), N_("H Max:"), N_("H Step Inc:"), + N_("H Page Inc:"), N_("H Page Size:")}; + const gchar *vert_labels[] = + {N_("V Value:"), N_("V Min:"), N_("V Max:"), N_("V Step Inc:"), + N_("V Page Inc:"), N_("V Page Size:")}; + const gchar *tips[] = + { + N_("The initial value"), + N_("The minimum value"), + N_("The maximum value"), + N_("The step increment"), + N_("The page increment"), + N_("The page size"), + }; + const gchar **labels; + gint i; + + if (label_type == GB_ADJUST_H_LABELS) + labels = horz_labels; + else if (label_type == GB_ADJUST_V_LABELS) + labels = vert_labels; + else + labels = default_labels; + + for (i = 0; i < 6; i++) + { + if (Values[i]) + { + property_add_float (Values[i], _(labels[i]), _(tips[i])); + } + } +} + + +/* + * The Font dialog + */ + +/* This is the default GTK font spec., from gtkstyle.c */ +#define GB_DEFAULT_XLFD_FONTNAME \ + "-adobe-helvetica-medium-r-normal--*-120-*-*-*-*-*-*" + +static void +show_font_dialog (GtkWidget * widget, gpointer value) +{ + gchar *current_xlfd_fontname; + GtkWidget *transient_parent; + + /* Create the dialog if it doesn't exist yet */ + if (!fontsel) + { + fontsel = GTK_FONT_SELECTION_DIALOG (gtk_font_selection_dialog_new (NULL)); + + /* The OK/Apply/Cancel button */ + gtk_signal_connect (GTK_OBJECT (fontsel), "delete_event", + GTK_SIGNAL_FUNC (close_dialog_event), fontsel); + gtk_signal_connect (GTK_OBJECT (fontsel->ok_button), "clicked", + GTK_SIGNAL_FUNC (on_font_dialog_ok), fontsel); + gtk_widget_show (fontsel->apply_button); + gtk_signal_connect (GTK_OBJECT (fontsel->apply_button), "clicked", + GTK_SIGNAL_FUNC (on_font_dialog_apply), fontsel); + gtk_signal_connect (GTK_OBJECT (fontsel->cancel_button), "clicked", + GTK_SIGNAL_FUNC (close_dialog), fontsel); + gtk_window_set_wmclass (GTK_WINDOW (fontsel), "font_selection", "Glade"); + gtk_signal_connect (GTK_OBJECT (fontsel), "key_press_event", + GTK_SIGNAL_FUNC (glade_util_check_key_is_esc), + GINT_TO_POINTER (GladeEscCloses)); + } + + /* Select font according to current setting */ + current_xlfd_fontname = gtk_object_get_data (GTK_OBJECT (value), GbFontSpecKey); + if (!current_xlfd_fontname || current_xlfd_fontname[0] == '\0') + current_xlfd_fontname = GB_DEFAULT_XLFD_FONTNAME; + + gtk_font_selection_dialog_set_font_name (fontsel, current_xlfd_fontname); + + /* Save pointer to value to use when OK/Apply pressed */ + gtk_object_set_data (GTK_OBJECT (fontsel), GbValueWidgetKey, value); + + gtk_widget_show (GTK_WIDGET (fontsel)); + transient_parent = glade_util_get_toplevel (widget); + if (GTK_IS_WINDOW (transient_parent)) + gtk_window_set_transient_for (GTK_WINDOW (fontsel), + GTK_WINDOW (transient_parent)); + /* This maps the window, which also de-iconifies it according to ICCCM. */ + gdk_window_show (GTK_WIDGET (fontsel)->window); + gdk_window_raise (GTK_WIDGET (fontsel)->window); +} + + +/* FIXME: This isn't used at present */ +#if 0 +static gint +get_font_size_from_spec (const gchar * spec) +{ + gint i, size = -1; + + for (i = 0; i < 8; i++) + { + spec = strchr (spec, '-'); + if (spec == NULL) + return -1; + spec++; + } + sscanf (spec, "%i", &size); + return size; +} +#endif + +/* Note: this only works with standard X font specs, e.g. + -adobe-courier-bold-i-normal--0-0-75-75-m-0-iso8859-1 + It copies the first two fields, changing '-' to ' ' and capitalising the + first characters of words - after a '-', ' ' or '&'. + Note: returns pointer to static buffer, so copy it if you want to keep it */ +static gchar * +get_font_name_from_spec (const gchar * spec) +{ + static gchar buf[128]; + gint i, dashes_found = 0; + gboolean word_start = TRUE; + gchar ch; + + if (spec == NULL) + return ""; + + for (i = 0; i < 127; i++) + { + ch = spec[i + 1]; + if (ch == '\0') + break; + if (ch == '-') + { + dashes_found++; + if (dashes_found == 2) + break; + ch = ' '; + } + if (word_start) + ch = toupper (ch); + word_start = (ch == ' ' || ch == '&'); + buf[i] = ch; + } + buf[i] = '\0'; + return buf; +} + + +static void +on_font_dialog_apply (GtkWidget * widget, GtkFontSelectionDialog * fontsel) +{ + GtkWidget *value; + GdkFont *font, *old_font; + gchar *xlfd_fontname, *old_xlfd_fontname; + + value = gtk_object_get_data (GTK_OBJECT (fontsel), GbValueWidgetKey); + g_return_if_fail (value != NULL); + + /* Try to create the font, if the font spec has changed */ + xlfd_fontname = gtk_font_selection_dialog_get_font_name (fontsel); + font = gtk_font_selection_dialog_get_font (fontsel); + old_xlfd_fontname = gtk_object_get_data (GTK_OBJECT (value), GbFontSpecKey); + + if (!old_xlfd_fontname + || (xlfd_fontname && strcmp (xlfd_fontname, old_xlfd_fontname))) + { + if (font == NULL) + { + glade_util_show_message_box (_("The requested font is not available."), widget); + return; + } + + old_font = gtk_object_get_data (GTK_OBJECT (value), GbFontKey); + if (old_font) + gdk_font_unref (old_font); + gdk_font_ref (font); + gtk_object_set_data (GTK_OBJECT (value), GbFontKey, font); + g_free (old_xlfd_fontname); + gtk_object_set_data (GTK_OBJECT (value), GbFontSpecKey, g_strdup (xlfd_fontname)); + gtk_entry_set_text (GTK_ENTRY (value), get_font_name_from_spec (xlfd_fontname)); + } +} + + +static void +on_font_dialog_ok (GtkWidget * widget, GtkFontSelectionDialog * fontsel) +{ + on_font_dialog_apply (widget, fontsel); + close_dialog (widget, GTK_WIDGET (fontsel)); +} + + +/* + * The Style dialog, for selecting a style to use/copy for a widget. + */ + +#ifdef GLADE_STYLE_SUPPORT +static void +show_style_dialog (GtkWidget * widget, gpointer value) +{ + GtkWidget *dialog; + GtkWidget *hbox, *clist, *vbbox, *button, *scrolled_win; + GtkWidget *transient_parent; + int i; + gchar *current_style; + gchar *titles[1]; + gchar *text; + gchar *row[1]; + + dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 3); + + gtk_signal_connect_object (GTK_OBJECT (dialog), "delete_event", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT (dialog)); + + gtk_window_set_title (GTK_WINDOW (dialog), _("Select Named Style")); + gtk_window_set_wmclass (GTK_WINDOW (dialog), "select_named_style", "Glade"); + transient_parent = glade_util_get_toplevel (transient_for); + if (GTK_IS_WINDOW (transient_parent)) + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (transient_parent)); + + hbox = gtk_hbox_new (FALSE, 5); + gtk_container_add (GTK_CONTAINER (dialog), hbox); + gtk_widget_show (hbox); + + titles[0] = _("Styles"); + clist = gtk_clist_new_with_titles (1, titles); + gtk_clist_column_titles_passive (GTK_CLIST (clist)); + gtk_widget_set_usize (clist, 200, 200); + gtk_widget_show (clist); + + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (scrolled_win), clist); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (hbox), scrolled_win, TRUE, TRUE, 0); + gtk_widget_show(scrolled_win); + + /* Insert styles */ + gtk_clist_freeze (GTK_CLIST (clist)); + + /* Add unnamed style first */ + row[0] = GB_STYLE_UNNAMED; + gtk_clist_append (GTK_CLIST (clist), row); + g_hash_table_foreach (gb_style_hash, (GHFunc) add_style_to_clist, clist); + + current_style = gtk_entry_get_text (GTK_ENTRY (value)); + if (strlen (current_style) == 0) + { + gtk_clist_select_row (GTK_CLIST (clist), 0, 0); + } + else + { + for (i = 1; i < GTK_CLIST (clist)->rows; i++) + { + gtk_clist_get_text (GTK_CLIST (clist), i, 0, &text); + if (!strcmp (current_style, text)) + { + gtk_clist_select_row (GTK_CLIST (clist), i, 0); + } + } + } + + gtk_clist_thaw (GTK_CLIST (clist)); + gtk_signal_connect (GTK_OBJECT (clist), "select_row", + GTK_SIGNAL_FUNC (on_style_clist_select), clist); + + /* Save pointer to value to use when a button is pressed */ + gtk_object_set_data (GTK_OBJECT (clist), GbValueWidgetKey, value); + + /* Create all the buttons */ + vbbox = gtk_vbutton_box_new (); + gtk_button_box_set_layout (GTK_BUTTON_BOX (vbbox), GTK_BUTTONBOX_START); + gtk_box_set_spacing (GTK_BOX (vbbox), 2); + gtk_widget_show (vbbox); + gtk_box_pack_start (GTK_BOX (hbox), vbbox, FALSE, TRUE, 0); + + button = gtk_button_new_with_label (_("New")); + gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_style_dialog_new), clist); + + button = gtk_button_new_with_label (_("Rename")); + gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_style_dialog_rename), clist); + gtk_object_set_data (GTK_OBJECT (clist), "rename_button", button); + gtk_widget_set_sensitive (button, FALSE); + + + button = gtk_button_new_with_label (_("Delete")); + gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_style_dialog_delete), clist); + gtk_object_set_data (GTK_OBJECT (clist), "delete_button", button); + gtk_widget_set_sensitive (button, FALSE); + + button = gtk_button_new_with_label (_("Copy")); + gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_style_dialog_copy), clist); + gtk_object_set_data (GTK_OBJECT (clist), "copy_button", button); + gtk_widget_set_sensitive (button, FALSE); + + button = gtk_button_new_with_label (_("Cancel")); + gtk_box_pack_start (GTK_BOX (vbbox), button, TRUE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_show (button); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT (dialog)); + gtk_widget_show (GTK_WIDGET (dialog)); + gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event", + GTK_SIGNAL_FUNC (on_key_press), + GINT_TO_POINTER (PROP_CLOSE_DESTROYS)); + + button = gtk_button_new_with_label (_("OK")); + gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_grab_default (button); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (on_style_dialog_ok), clist); + +} + + +static gint +add_style_to_clist (const gchar * key, gpointer data, GtkWidget * clist) +{ + gint i; + gchar *text; + const gchar *row[1]; + row[0] = key; + + /* Leave unnamed style at top */ + for (i = 1; i < GTK_CLIST (clist)->rows; i++) + { + gtk_clist_get_text (GTK_CLIST (clist), i, 0, &text); + if (strcmp (key, text) < 0) + { + gtk_clist_insert (GTK_CLIST (clist), i, (gchar**) row); + return i; + } + } + return gtk_clist_append (GTK_CLIST (clist), (gchar**) row); +} + + +static void +on_style_clist_select (GtkWidget * widget, gint row, gint column, + GdkEventButton * bevent, gpointer data) +{ + gchar *text = NULL; + GtkWidget *copy_button, *rename_button, *delete_button; + gboolean copy_sensitive = TRUE; + gboolean rename_sensitive = TRUE; + gboolean delete_sensitive = TRUE; + + if (bevent && bevent->type == GDK_2BUTTON_PRESS) + { + on_style_dialog_ok (widget, widget); + return; + } + + copy_button = gtk_object_get_data (GTK_OBJECT (widget), "copy_button"); + g_return_if_fail (copy_button != NULL); + rename_button = gtk_object_get_data (GTK_OBJECT (widget), "rename_button"); + g_return_if_fail (rename_button != NULL); + delete_button = gtk_object_get_data (GTK_OBJECT (widget), "delete_button"); + g_return_if_fail (delete_button != NULL); + + /* If unnamed style selected, make copy, rename & delete buttons insensitive, + else if default style selected, make rename & delete insensitive. */ + gtk_clist_get_text (GTK_CLIST (widget), row, 0, &text); + /* Added this check since it SEGVed once here. */ + g_return_if_fail (text != NULL); + if (!strcmp (text, GB_STYLE_UNNAMED)) + { + copy_sensitive = FALSE; + rename_sensitive = FALSE; + delete_sensitive = FALSE; + } + else if (!strcmp (text, GB_STYLE_DEFAULT)) + { + rename_sensitive = FALSE; + delete_sensitive = FALSE; + } + gtk_widget_set_sensitive (copy_button, copy_sensitive); + gtk_widget_set_sensitive (rename_button, rename_sensitive); + gtk_widget_set_sensitive (delete_button, delete_sensitive); +} + + +static void +on_style_dialog_new (GtkWidget * widget, GtkWidget * clist) +{ + GList *selection = GTK_CLIST (clist)->selection; + gint row; + gchar *text; + GladeWidgetData *wdata; + GbStyle *base_gbstyle = NULL; + + if (selection) + { + row = GPOINTER_TO_INT (selection->data); + gtk_clist_get_text (GTK_CLIST (clist), row, 0, &text); + if (!strcmp (text, GB_STYLE_UNNAMED)) + { + if (property_widget) + { + wdata = gtk_object_get_data (GTK_OBJECT (property_widget), + GB_WIDGET_DATA_KEY); + if (wdata) + base_gbstyle = wdata->gbstyle; + } + } + else + { + base_gbstyle = (GbStyle *) g_hash_table_lookup (gb_style_hash, text); + g_return_if_fail (base_gbstyle != NULL); + } + } + + glade_util_show_entry_dialog (_("New Style:"), NULL, clist, + (GbEntryDialogFunc) create_new_style, + base_gbstyle); +} + + +static gint +create_new_style (GtkWidget * widget, const gchar * name, GbStyle * base_gbstyle) +{ + GbStyle *gbstyle, *existing_gbstyle; + gint row; + + if (strlen (name) == 0) + { + glade_util_show_message_box (_("Invalid style name")); + return FALSE; + } + + /* Make sure name is unique */ + existing_gbstyle = (GbStyle *) g_hash_table_lookup (gb_style_hash, name); + if (existing_gbstyle) + { + glade_util_show_message_box (_("That style name is already in use")); + return FALSE; + } + + if (!base_gbstyle) + base_gbstyle = gb_widget_default_gb_style; + g_return_val_if_fail (base_gbstyle != NULL, TRUE); + + gbstyle = gb_widget_copy_gb_style (base_gbstyle); + g_free (gbstyle->name); + gbstyle->name = g_strdup (name); + g_hash_table_insert (gb_style_hash, gbstyle->name, gbstyle); + + /* Add style to clist */ + row = add_style_to_clist (name, NULL, widget); + gtk_clist_select_row (GTK_CLIST (widget), row, 0); + + return TRUE; +} + + +static void +on_style_dialog_copy (GtkWidget * widget, GtkWidget * clist) +{ + GladeWidgetData *wdata; + gint row, i; + GtkWidget *value; + GList *selection = GTK_CLIST (clist)->selection; + gchar *text; + GbStyle *gbstyle, *other_gbstyle; + + if (selection) + { + row = GPOINTER_TO_INT (selection->data); + value = gtk_object_get_data (GTK_OBJECT (clist), GbValueWidgetKey); + g_return_if_fail (value != NULL); + + gtk_clist_get_text (GTK_CLIST (clist), row, 0, &text); + if (!strcmp (text, GB_STYLE_UNNAMED)) + return; + other_gbstyle = (GbStyle *) g_hash_table_lookup (gb_style_hash, text); + g_return_if_fail (other_gbstyle != NULL); + + if (property_widget) + { + wdata = gtk_object_get_data (GTK_OBJECT (property_widget), + GB_WIDGET_DATA_KEY); + g_return_if_fail (wdata != NULL); + gbstyle = wdata->gbstyle; + + /* If widget is using an unnamed GbStyle, just use the selected GbStyle. + else copy the selected GbStyle to the current one. */ + if (wdata->flags & GB_STYLE_IS_UNNAMED) + { + gb_widget_set_gb_style (widget, other_gbstyle); + } + else + { + g_free (gbstyle->xlfd_fontname); + gbstyle->xlfd_fontname = g_strdup (other_gbstyle->xlfd_fontname); + for (i = 0; i < GB_NUM_STYLE_STATES; i++) + { + g_free (gbstyle->bg_pixmap_filenames[i]); + gbstyle->bg_pixmap_filenames[i] + = g_strdup (other_gbstyle->bg_pixmap_filenames[i]); + } + gtk_style_unref (gbstyle->style); + gbstyle->style = other_gbstyle->style; + gtk_style_ref (gbstyle->style); + gb_widget_update_gb_styles (gbstyle, gbstyle); + } + + editor_refresh_widget (property_widget); + property_set_auto_apply (FALSE); + gb_widget_show_style (property_widget); + property_set_auto_apply (TRUE); + } + } + gtk_widget_destroy (gtk_widget_get_toplevel (clist)); +} + + +static void +on_style_dialog_rename (GtkWidget * widget, GtkWidget * clist) +{ + gint row; + GList *selection = GTK_CLIST (clist)->selection; + gchar *text; + GbStyle *gbstyle; + + if (selection) + { + row = GPOINTER_TO_INT (selection->data); + gtk_clist_get_text (GTK_CLIST (clist), row, 0, &text); + if (!strcmp (text, GB_STYLE_UNNAMED) || (!strcmp (text, GB_STYLE_DEFAULT))) + return; + gbstyle = (GbStyle *) g_hash_table_lookup (gb_style_hash, text); + g_return_if_fail (gbstyle != NULL); + glade_util_show_entry_dialog (_("Rename Style To:"), text, clist, + (GbEntryDialogFunc) rename_style, gbstyle); + } +} + + +static gint +rename_style (GtkWidget * clist, const gchar * name, GbStyle * gbstyle) +{ + GbStyle *existing_gbstyle; + gchar *text, *old_name; + gint i, row; + + if (strlen (name) == 0) + { + glade_util_show_message_box (_("Invalid style name")); + return FALSE; + } + + /* Make sure name is unique */ + existing_gbstyle = (GbStyle *) g_hash_table_lookup (gb_style_hash, text); + if (existing_gbstyle == gbstyle) + return TRUE; + if (existing_gbstyle) + { + glade_util_show_message_box (_("That style name is already in use")); + return FALSE; + } + + old_name = gbstyle->name; + gbstyle->name = g_strdup (name); + + /* Delete old entry in style hash & insert new one */ + g_hash_table_remove (gb_style_hash, old_name); + g_hash_table_insert (gb_style_hash, gbstyle->name, gbstyle); + + /* Update name in clist */ + for (i = 0; i < GTK_CLIST (clist)->rows; i++) + { + gtk_clist_get_text (GTK_CLIST (clist), i, 0, &text); + if (!strcmp (text, old_name)) + { + gtk_clist_remove (GTK_CLIST (clist), i); + break; + } + } + row = add_style_to_clist (name, NULL, clist); + gtk_clist_select_row (GTK_CLIST (clist), row, 0); + + g_free (old_name); + + return TRUE; +} + + +static void +on_style_dialog_delete (GtkWidget * widget, GtkWidget * clist) +{ + gint row; + GtkWidget *value; + GList *selection = GTK_CLIST (clist)->selection; + gchar *text; + GbStyle *gbstyle; + gboolean reshow = FALSE; + + if (selection) + { + row = GPOINTER_TO_INT (selection->data); + value = gtk_object_get_data (GTK_OBJECT (clist), GbValueWidgetKey); + g_return_if_fail (value != NULL); + + gtk_clist_get_text (GTK_CLIST (clist), row, 0, &text); + if (!strcmp (text, GB_STYLE_UNNAMED) || (!strcmp (text, GB_STYLE_DEFAULT))) + return; + + gbstyle = (GbStyle *) g_hash_table_lookup (gb_style_hash, text); + g_return_if_fail (gbstyle != NULL); + + gtk_clist_remove (GTK_CLIST (clist), row); + + if (property_widget && property_widget->style == gbstyle->style) + { + reshow = TRUE; + } + + /* Make all widgets which are using the style use the default instead */ + gb_widget_update_gb_styles (gbstyle, gb_widget_default_gb_style); + gb_widget_destroy_gb_style (gbstyle, TRUE); + + if (reshow) + { + property_set_auto_apply (FALSE); + gb_widget_show_style (property_widget); + property_set_auto_apply (TRUE); + } + } +} + + +static void +on_style_dialog_ok (GtkWidget * widget, GtkWidget * clist) +{ + GladeWidgetData *wdata; + gint row; + GtkWidget *value; + GList *selection = GTK_CLIST (clist)->selection; + gchar *text; + GbStyle *gbstyle; + + if (selection) + { + row = GPOINTER_TO_INT (selection->data); + value = gtk_object_get_data (GTK_OBJECT (clist), GbValueWidgetKey); + g_return_if_fail (value != NULL); + + gtk_clist_get_text (GTK_CLIST (clist), row, 0, &text); + + if (property_widget) + { + wdata = gtk_object_get_data (GTK_OBJECT (property_widget), + GB_WIDGET_DATA_KEY); + g_return_if_fail (wdata != NULL); + + /* If <none> is selected, just set the unnamed style flag, so if any + changes are made to the style a new GbStyle is created. */ + if (!strcmp (text, GB_STYLE_UNNAMED)) + { + if (!(wdata->flags & GB_STYLE_IS_UNNAMED)) + { + wdata->flags |= GB_STYLE_IS_UNNAMED; + property_set_auto_apply (FALSE); + gb_widget_show_style (property_widget); + property_set_auto_apply (TRUE); + } + } + else + { + gbstyle = (GbStyle *) g_hash_table_lookup (gb_style_hash, text); + g_return_if_fail (gbstyle != NULL); + wdata->flags &= ~GB_STYLE_IS_UNNAMED; + + gb_widget_set_gb_style (property_widget, gbstyle); + editor_refresh_widget (property_widget); + property_set_auto_apply (FALSE); + gb_widget_show_style (property_widget); + property_set_auto_apply (TRUE); + } + } + } + gtk_widget_destroy (gtk_widget_get_toplevel (clist)); +} +#endif + + +/* Experimental code. */ +void +property_redirect_key_press (GdkEventKey *event) +{ + GtkWidget *value_widget; + GdkEventKey tmp_event; + gchar *property_name = NULL; + + if (property_widget == NULL) + return; + + gtk_notebook_set_current_page (GTK_NOTEBOOK (main_notebook), GB_PAGE_WIDGET); + + /* Make sure subclasses are tested first. + FIXME: Shouldn't really be using copies of the strings here. */ + if (GTK_IS_ACCEL_LABEL (property_widget)) + property_name = "AccelLabel|GtkLabel::label"; + else if (GTK_IS_LABEL (property_widget)) + property_name = "GtkLabel::label"; + else if (GTK_IS_RADIO_BUTTON (property_widget)) + property_name = "RadioButton|GtkButton::label"; + else if (GTK_IS_CHECK_BUTTON (property_widget)) + property_name = "CheckButton|GtkButton::label"; + else if (GTK_IS_TOGGLE_BUTTON (property_widget)) + property_name = "ToggleButton|GtkButton::label"; + else if (GTK_IS_BUTTON (property_widget)) + property_name = "GtkButton::label"; + + if (property_name == NULL) + return; + + value_widget = (GtkWidget *) g_hash_table_lookup (gb_property_values, + property_name); + g_return_if_fail (value_widget != NULL); + + if (GTK_IS_SCROLLED_WINDOW (value_widget)) + value_widget = GTK_BIN (value_widget)->child; + + if (!GTK_WIDGET_IS_SENSITIVE (value_widget)) + return; + + /* If this is the first key-press, we delete the current text. */ + if (!typing_over_widget) + { + if (GTK_IS_ENTRY (value_widget)) + { + gtk_entry_set_text (GTK_ENTRY (value_widget), ""); + } + else if (GTK_IS_TEXT_VIEW (value_widget)) + { + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (value_widget)); + gtk_text_buffer_set_text (buffer, "", 0); + } + else + { + g_warning ("Can't redirect key press - property isn't a GtkEntry or GtkTextView"); + return; + } + + typing_over_widget = TRUE; + } + + tmp_event = *event; + tmp_event.send_event = TRUE; + + gtk_widget_event (value_widget, (GdkEvent *)&tmp_event); +} + + +gboolean +property_is_typing_over_widget (void) +{ + return typing_over_widget; +} + + +/* + * Callbacks for the buttons to reset the widget width & height. + * We set the values to 0, which will result in the widget being resized to + * the default width/height. This then results in the property being updated + * to show the default size. + */ + +static void +update_position_property (GtkWidget * widget, gpointer value, gint flag) +{ + GladeWidgetData *wdata; + gboolean value_set; + gint w, h; + + if (property_widget == NULL) + return; + wdata = gtk_object_get_data (GTK_OBJECT (property_widget), + GB_WIDGET_DATA_KEY); + g_return_if_fail (wdata != NULL); + + value_set = GTK_TOGGLE_BUTTON (widget)->active ? TRUE : FALSE; + gtk_widget_set_sensitive (GTK_WIDGET (value), value_set); + if (value_set) + wdata->flags |= flag; + else + wdata->flags &= ~flag; + + /* X & Y flags can only be changed for windows, and we don't need to update + those, so we only have to worry about the width & height flags changing.*/ + if (flag == GLADE_WIDTH_SET || flag == GLADE_HEIGHT_SET) + { + w = wdata->flags & GLADE_WIDTH_SET ? wdata->width : -1; + h = wdata->flags & GLADE_HEIGHT_SET ? wdata->height : -1; + + gb_widget_set_usize (property_widget, w, h); + } +} + + +static void +on_toggle_set_width (GtkWidget * widget, gpointer value) +{ + update_position_property (widget, value, GLADE_WIDTH_SET); +} + + +static void +on_toggle_set_height (GtkWidget * widget, gpointer value) +{ + update_position_property (widget, value, GLADE_HEIGHT_SET); +} |