/* * A subclass of GtkAction that wraps a GtkComboBoxEntry. * Features: * Setting GtkEntryBox width in characters. * Passing a function for formatting cells. * Displaying a warning if entry text isn't in list. * Check comma separated values in text against list. (Useful for font-family fallbacks.) * Setting names for GtkComboBoxEntry and GtkEntry (actionName_combobox, actionName_entry) * to allow setting resources. * * Author(s): * Tavmjong Bah * Jon A. Cruz * * Copyright (C) 2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ /* * We must provide for both a toolbar item and a menu item. * As we don't know which widgets are used (or even constructed), * we must keep track of things like active entry ourselves. */ #include #include #include #include #include #include "ink-comboboxentry-action.h" #include "ui/icon-names.h" // Must handle both tool and menu items! static GtkWidget* create_tool_item( GtkAction* action ); static GtkWidget* create_menu_item( GtkAction* action ); // Internal static gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text, gboolean exclude = false ); static Glib::ustring check_comma_separated_text( Ink_ComboBoxEntry_Action* action ); // Callbacks static void combo_box_changed_cb( GtkComboBox* widget, gpointer data ); static void entry_activate_cb( GtkEntry* widget, gpointer data ); static gboolean match_selected_cb( GtkEntryCompletion* widget, GtkTreeModel* model, GtkTreeIter* iter, gpointer data ); static gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data ); enum { PROP_MODEL = 1, PROP_COMBOBOX, PROP_ENTRY, PROP_ENTRY_WIDTH, PROP_EXTRA_WIDTH, PROP_CELL_DATA_FUNC, PROP_SEPARATOR_FUNC, PROP_POPUP, PROP_FOCUS_WIDGET }; enum { CHANGED = 0, ACTIVATED, N_SIGNALS }; static guint signals[N_SIGNALS] = {0}; static GQuark gDataName = 0; static void ink_comboboxentry_action_init (Ink_ComboBoxEntry_Action *action); static void ink_comboboxentry_action_class_init (Ink_ComboBoxEntry_ActionClass *klass); G_DEFINE_TYPE(Ink_ComboBoxEntry_Action, ink_comboboxentry_action, GTK_TYPE_ACTION); static void ink_comboboxentry_action_finalize (GObject *object) { // Free any allocated resources. G_OBJECT_CLASS (ink_comboboxentry_action_parent_class)->finalize (object); } static void ink_comboboxentry_action_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION (object); switch(property_id) { case PROP_MODEL: action->model = GTK_TREE_MODEL( g_value_get_object( value )); break; case PROP_COMBOBOX: action->combobox = GTK_COMBO_BOX (g_value_get_object (value)); break; case PROP_ENTRY: action->entry = GTK_ENTRY( g_value_get_object( value )); break; case PROP_ENTRY_WIDTH: action->entry_width = g_value_get_int( value ); break; case PROP_EXTRA_WIDTH: action->extra_width = g_value_get_int( value ); break; case PROP_CELL_DATA_FUNC: action->cell_data_func = g_value_get_pointer( value ); break; case PROP_SEPARATOR_FUNC: action->separator_func = g_value_get_pointer( value ); break; case PROP_POPUP: action->popup = g_value_get_boolean( value ); break; case PROP_FOCUS_WIDGET: action->focusWidget = (GtkWidget*)g_value_get_pointer( value ); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void ink_comboboxentry_action_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION (object); switch(property_id) { case PROP_MODEL: g_value_set_object (value, action->model); break; case PROP_COMBOBOX: g_value_set_object (value, action->combobox); break; case PROP_ENTRY: g_value_set_object (value, action->entry); break; case PROP_ENTRY_WIDTH: g_value_set_int (value, action->entry_width); break; case PROP_EXTRA_WIDTH: g_value_set_int (value, action->extra_width); break; case PROP_CELL_DATA_FUNC: g_value_set_pointer (value, action->cell_data_func); break; case PROP_SEPARATOR_FUNC: g_value_set_pointer (value, action->separator_func); break; case PROP_POPUP: g_value_set_boolean (value, action->popup); break; case PROP_FOCUS_WIDGET: g_value_set_pointer (value, action->focusWidget); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void ink_comboboxentry_action_connect_proxy (GtkAction *action, GtkWidget *proxy) { /* Override any proxy properties. */ // if (GTK_IS_MENU_ITEM (proxy)) { // } GTK_ACTION_CLASS (ink_comboboxentry_action_parent_class)->connect_proxy (action, proxy); } static void ink_comboboxentry_action_class_init (Ink_ComboBoxEntry_ActionClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkActionClass *gtkaction_class = GTK_ACTION_CLASS (klass); gtkaction_class->connect_proxy = ink_comboboxentry_action_connect_proxy; gobject_class->finalize = ink_comboboxentry_action_finalize; gobject_class->set_property = ink_comboboxentry_action_set_property; gobject_class->get_property = ink_comboboxentry_action_get_property; gDataName = g_quark_from_string("ink_comboboxentry-action"); klass->parent_class.create_tool_item = create_tool_item; klass->parent_class.create_menu_item = create_menu_item; g_object_class_install_property ( gobject_class, PROP_MODEL, g_param_spec_object ("model", "Tree Model", "Tree Model", GTK_TYPE_TREE_MODEL, (GParamFlags)G_PARAM_READWRITE)); g_object_class_install_property ( gobject_class, PROP_COMBOBOX, g_param_spec_object ("combobox", "GtkComboBoxEntry", "GtkComboBoxEntry", GTK_TYPE_WIDGET, (GParamFlags)G_PARAM_READABLE)); g_object_class_install_property ( gobject_class, PROP_ENTRY, g_param_spec_object ("entry", "GtkEntry", "GtkEntry", GTK_TYPE_WIDGET, (GParamFlags)G_PARAM_READABLE)); g_object_class_install_property ( gobject_class, PROP_ENTRY_WIDTH, g_param_spec_int ("entry_width", "EntryBox width", "EntryBox width (characters)", -1.0, 100, -1.0, (GParamFlags)G_PARAM_READWRITE)); g_object_class_install_property ( gobject_class, PROP_EXTRA_WIDTH, g_param_spec_int ("extra_width", "Extra width", "Extra width (px)", -1.0, 500, -1.0, (GParamFlags)G_PARAM_READWRITE)); g_object_class_install_property ( gobject_class, PROP_CELL_DATA_FUNC, g_param_spec_pointer ("cell_data_func", "Cell Data Func", "Cell Deta Function", (GParamFlags)G_PARAM_READWRITE)); g_object_class_install_property ( gobject_class, PROP_SEPARATOR_FUNC, g_param_spec_pointer ("separator_func", "Separator Func", "Separator Function", (GParamFlags)G_PARAM_READWRITE)); g_object_class_install_property ( gobject_class, PROP_POPUP, g_param_spec_boolean ("popup", "Entry Popup", "Entry Popup", false, (GParamFlags)G_PARAM_READWRITE)); g_object_class_install_property( gobject_class, PROP_FOCUS_WIDGET, g_param_spec_pointer( "focus-widget", "Focus Widget", "The widget to return focus to", (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); // We need to know when GtkComboBoxEvent or Menu ready for reading signals[CHANGED] = g_signal_new( "changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(Ink_ComboBoxEntry_ActionClass, changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); // Probably not needed... originally to keep track of key-presses. signals[ACTIVATED] = g_signal_new( "activated", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(Ink_ComboBoxEntry_ActionClass, activated), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void ink_comboboxentry_action_init (Ink_ComboBoxEntry_Action *action) { action->active = -1; action->text = strdup(""); action->entry_completion = NULL; action->indicator = NULL; action->popup = false; action->info = NULL; action->info_cb = NULL; action->info_cb_id = 0; action->info_cb_blocked = false; action->warning = NULL; action->warning_cb = NULL; action->warning_cb_id = 0; action->warning_cb_blocked = false; action->altx_name = NULL; action->focusWidget = NULL; } Ink_ComboBoxEntry_Action *ink_comboboxentry_action_new (const gchar *name, const gchar *label, const gchar *tooltip, const gchar *stock_id, GtkTreeModel *model, gint entry_width, gint extra_width, void *cell_data_func, void *separator_func, GtkWidget *focusWidget) { g_return_val_if_fail (name != NULL, NULL); return (Ink_ComboBoxEntry_Action*)g_object_new (INK_COMBOBOXENTRY_TYPE_ACTION, "name", name, "label", label, "tooltip", tooltip, "stock-id", stock_id, "model", model, "entry_width", entry_width, "extra_width", extra_width, "cell_data_func", cell_data_func, "separator_func", separator_func, "focus-widget", focusWidget, NULL); } // Create a widget for a toolbar. GtkWidget* create_tool_item( GtkAction* action ) { GtkWidget* item = 0; if ( INK_COMBOBOXENTRY_IS_ACTION( action ) && INK_COMBOBOXENTRY_ACTION(action)->model ) { Ink_ComboBoxEntry_Action* ink_comboboxentry_action = INK_COMBOBOXENTRY_ACTION( action ); gchar *action_name = g_strdup( gtk_action_get_name( action ) ); gchar *combobox_name = g_strjoin( NULL, action_name, "_combobox", NULL ); gchar *entry_name = g_strjoin( NULL, action_name, "_entry", NULL ); g_free( action_name ); item = GTK_WIDGET( gtk_tool_item_new() ); GtkWidget* comboBoxEntry = gtk_combo_box_new_with_model_and_entry (ink_comboboxentry_action->model); gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (comboBoxEntry), 0); // Name it so we can muck with it using an RC file gtk_widget_set_name( comboBoxEntry, combobox_name ); g_free( combobox_name ); { GtkWidget *align = gtk_alignment_new(0, 0.5, 0, 0); gtk_container_add( GTK_CONTAINER(align), comboBoxEntry ); gtk_container_add( GTK_CONTAINER(item), align ); } ink_comboboxentry_action->combobox = GTK_COMBO_BOX (comboBoxEntry); //gtk_combo_box_set_active( GTK_COMBO_BOX( comboBoxEntry ), ink_comboboxentry_action->active ); gtk_combo_box_set_active( GTK_COMBO_BOX( comboBoxEntry ), 0 ); g_signal_connect( G_OBJECT(comboBoxEntry), "changed", G_CALLBACK(combo_box_changed_cb), action ); // Optionally add formatting... if( ink_comboboxentry_action->cell_data_func != NULL ) { GtkCellRenderer *cell = gtk_cell_renderer_text_new(); gtk_cell_layout_clear( GTK_CELL_LAYOUT( comboBoxEntry ) ); gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( comboBoxEntry ), cell, true ); gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT( comboBoxEntry ), cell, GtkCellLayoutDataFunc (ink_comboboxentry_action->cell_data_func), NULL, NULL ); } // Optionally add separator function... if( ink_comboboxentry_action->separator_func != NULL ) { gtk_combo_box_set_row_separator_func( ink_comboboxentry_action->combobox, GtkTreeViewRowSeparatorFunc (ink_comboboxentry_action->separator_func), NULL, NULL ); } // Optionally widen the combobox width... which widens the drop-down list in list mode. if( ink_comboboxentry_action->extra_width > 0 ) { GtkRequisition req; #if GTK_CHECK_VERSION(3,0,0) gtk_widget_get_preferred_size(GTK_WIDGET(ink_comboboxentry_action->combobox), &req, NULL); #else gtk_widget_size_request( GTK_WIDGET( ink_comboboxentry_action->combobox ), &req ); #endif gtk_widget_set_size_request( GTK_WIDGET( ink_comboboxentry_action->combobox ), req.width + ink_comboboxentry_action->extra_width, -1 ); } // Get reference to GtkEntry and fiddle a bit with it. GtkWidget *child = gtk_bin_get_child( GTK_BIN(comboBoxEntry) ); // Name it so we can muck with it using an RC file gtk_widget_set_name( child, entry_name ); g_free( entry_name ); if( child && GTK_IS_ENTRY( child ) ) { ink_comboboxentry_action->entry = GTK_ENTRY(child); // Change width if( ink_comboboxentry_action->entry_width > 0 ) { gtk_entry_set_width_chars (GTK_ENTRY (child), ink_comboboxentry_action->entry_width ); } // Add pop-up entry completion if required if( ink_comboboxentry_action->popup ) { ink_comboboxentry_action_popup_enable( ink_comboboxentry_action ); } // Add altx_name if required if( ink_comboboxentry_action->altx_name ) { g_object_set_data( G_OBJECT( child ), ink_comboboxentry_action->altx_name, ink_comboboxentry_action->entry ); } // Add signal for GtkEntry to check if finished typing. g_signal_connect( G_OBJECT(child), "activate", G_CALLBACK(entry_activate_cb), action ); g_signal_connect( G_OBJECT(child), "key-press-event", G_CALLBACK(keypress_cb), action ); } gtk_activatable_set_related_action( GTK_ACTIVATABLE (item), GTK_ACTION( action ) ); gtk_widget_show_all( item ); } else { item = GTK_ACTION_CLASS(ink_comboboxentry_action_parent_class)->create_tool_item( action ); } return item; } // Create a drop-down menu. GtkWidget* create_menu_item( GtkAction* action ) { GtkWidget* item = 0; item = GTK_ACTION_CLASS(ink_comboboxentry_action_parent_class)->create_menu_item( action ); g_warning( "ink_comboboxentry_action: create_menu_item not implemented" ); // One can easily modify ege-select-one-action routine to implement this. return item; } // Setters/Getters --------------------------------------------------- GtkTreeModel *ink_comboboxentry_action_get_model( Ink_ComboBoxEntry_Action* action ) { return action->model; } GtkComboBox *ink_comboboxentry_action_get_comboboxentry( Ink_ComboBoxEntry_Action* action ) { return action->combobox; } gchar* ink_comboboxentry_action_get_active_text( Ink_ComboBoxEntry_Action* action ) { gchar* text = g_strdup( action->text ); return text; } /* * For the font-family list we need to handle two cases: * Text is in list store: * In this case we use row number as the font-family list can have duplicate * entries, one in the document font part and one in the system font part. In * order that scrolling through the list works properly we must distinguish * between the two. * Text is not in the list store (i.e. default font-family is not on system): * In this case we have a row number of -1, and the text must be set by hand. */ gboolean ink_comboboxentry_action_set_active_text( Ink_ComboBoxEntry_Action* action, const gchar* text, int row ) { if( strcmp( action->text, text ) != 0 ) { g_free( action->text ); action->text = g_strdup( text ); } // Get active row or -1 if none if( row < 0 ) { row = get_active_row_from_text( action, action->text ); } action->active = row; // Set active row, check that combobox has been created. if( action->combobox ) { gtk_combo_box_set_active( GTK_COMBO_BOX( action->combobox ), action->active ); } // Fiddle with entry if( action->entry ) { // Explicitly set text in GtkEntry box (won't be set if text not in list). gtk_entry_set_text( action->entry, text ); // Show or hide warning -- this might be better moved to text-toolbox.cpp if( action->info_cb_id != 0 && !action->info_cb_blocked ) { g_signal_handler_block (G_OBJECT(action->entry), action->info_cb_id ); action->info_cb_blocked = true; } if( action->warning_cb_id != 0 && !action->warning_cb_blocked ) { g_signal_handler_block (G_OBJECT(action->entry), action->warning_cb_id ); action->warning_cb_blocked = true; } bool set = false; if( action->warning != NULL ) { Glib::ustring missing = check_comma_separated_text( action ); if( !missing.empty() ) { gtk_entry_set_icon_from_icon_name( action->entry, GTK_ENTRY_ICON_SECONDARY, INKSCAPE_ICON("dialog-warning") ); // Can't add tooltip until icon set Glib::ustring warning = action->warning; warning += ": "; warning += missing; gtk_entry_set_icon_tooltip_text( action->entry, GTK_ENTRY_ICON_SECONDARY, warning.c_str() ); if( action->warning_cb ) { // Add callback if we haven't already if( action->warning_cb_id == 0 ) { action->warning_cb_id = g_signal_connect( G_OBJECT(action->entry), "icon-press", G_CALLBACK(action->warning_cb), action); } // Unblock signal if( action->warning_cb_blocked ) { g_signal_handler_unblock (G_OBJECT(action->entry), action->warning_cb_id ); action->warning_cb_blocked = false; } } set = true; } } if( !set && action->info != NULL ) { gtk_entry_set_icon_from_icon_name( GTK_ENTRY(action->entry), GTK_ENTRY_ICON_SECONDARY, INKSCAPE_ICON("edit-select-all") ); gtk_entry_set_icon_tooltip_text( action->entry, GTK_ENTRY_ICON_SECONDARY, action->info ); if( action->info_cb ) { // Add callback if we haven't already if( action->info_cb_id == 0 ) { action->info_cb_id = g_signal_connect( G_OBJECT(action->entry), "icon-press", G_CALLBACK(action->info_cb), action); } // Unblock signal if( action->info_cb_blocked ) { g_signal_handler_unblock (G_OBJECT(action->entry), action->info_cb_id ); action->info_cb_blocked = false; } } set = true; } if( !set ) { gtk_entry_set_icon_from_icon_name( GTK_ENTRY(action->entry), GTK_ENTRY_ICON_SECONDARY, NULL ); } } // Return if active text in list gboolean found = ( action->active != -1 ); return found; } void ink_comboboxentry_action_set_entry_width( Ink_ComboBoxEntry_Action* action, gint entry_width ) { action->entry_width = entry_width; // Widget may not have been created.... if( action->entry ) { gtk_entry_set_width_chars( GTK_ENTRY(action->entry), entry_width ); } } void ink_comboboxentry_action_set_extra_width( Ink_ComboBoxEntry_Action* action, gint extra_width ) { action->extra_width = extra_width; // Widget may not have been created.... if( action->combobox ) { GtkRequisition req; #if GTK_CHECK_VERSION(3,0,0) gtk_widget_get_preferred_size(GTK_WIDGET(action->combobox), &req, NULL); #else gtk_widget_size_request( GTK_WIDGET( action->combobox ), &req ); #endif gtk_widget_set_size_request( GTK_WIDGET( action->combobox ), req.width + action->extra_width, -1 ); } } void ink_comboboxentry_action_popup_enable( Ink_ComboBoxEntry_Action* action ) { action->popup = true; // Widget may not have been created.... if( action->entry ) { // Check we don't already have a GtkEntryCompletion if( action->entry_completion ) return; action->entry_completion = gtk_entry_completion_new(); gtk_entry_set_completion( action->entry, action->entry_completion ); gtk_entry_completion_set_model( action->entry_completion, action->model ); gtk_entry_completion_set_text_column( action->entry_completion, 0 ); gtk_entry_completion_set_popup_completion( action->entry_completion, true ); gtk_entry_completion_set_inline_completion( action->entry_completion, false ); gtk_entry_completion_set_inline_selection( action->entry_completion, true ); g_signal_connect (G_OBJECT (action->entry_completion), "match-selected", G_CALLBACK (match_selected_cb), action ); } } void ink_comboboxentry_action_popup_disable( Ink_ComboBoxEntry_Action* action ) { action->popup = false; if( action->entry_completion ) { gtk_widget_destroy(GTK_WIDGET(action->entry_completion)); action->entry_completion = 0; } } void ink_comboboxentry_action_set_tooltip( Ink_ComboBoxEntry_Action* action, const gchar* tooltip ) { // Widget may not have been created.... if( action->entry ) { gtk_widget_set_tooltip_text ( GTK_WIDGET(action->entry), tooltip); } if( action->combobox ) { gtk_widget_set_tooltip_text ( GTK_WIDGET(action->combobox), tooltip); } } void ink_comboboxentry_action_set_info( Ink_ComboBoxEntry_Action* action, const gchar* info ) { g_free( action->info ); action->info = g_strdup( info ); // Widget may not have been created.... if( action->entry ) { gtk_entry_set_icon_tooltip_text( GTK_ENTRY(action->entry), GTK_ENTRY_ICON_SECONDARY, action->info ); } } void ink_comboboxentry_action_set_info_cb( Ink_ComboBoxEntry_Action* action, gpointer info_cb ) { action->info_cb = info_cb; } void ink_comboboxentry_action_set_warning( Ink_ComboBoxEntry_Action* action, const gchar* warning ) { g_free( action->warning ); action->warning = g_strdup( warning ); // Widget may not have been created.... if( action->entry ) { gtk_entry_set_icon_tooltip_text( GTK_ENTRY(action->entry), GTK_ENTRY_ICON_SECONDARY, action->warning ); } } void ink_comboboxentry_action_set_warning_cb( Ink_ComboBoxEntry_Action* action, gpointer warning_cb ) { action->warning_cb = warning_cb; } void ink_comboboxentry_action_set_altx_name( Ink_ComboBoxEntry_Action* action, const gchar* altx_name ) { g_free( action->altx_name ); action->altx_name = g_strdup( altx_name ); // Widget may not have been created.... if( action->entry ) { g_object_set_data( G_OBJECT(action->entry), action->altx_name, action->entry ); } } // Internal --------------------------------------------------- // Return row of active text or -1 if not found. If exclude is true, // use 3d colunm if available to exclude row from checking (useful to // skip rows added for font-families included in doc and not on // system) gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text, gboolean exclude ) { // Check if text in list gint row = 0; gboolean found = false; GtkTreeIter iter; gboolean valid = gtk_tree_model_get_iter_first( action->model, &iter ); while ( valid ) { // See if we should exclude a row gboolean check = true; // If true, font-family is on system. if( exclude && gtk_tree_model_get_n_columns( action->model ) > 2 ) { gtk_tree_model_get( action->model, &iter, 2, &check, -1 ); } if( check ) { // Get text from list entry gchar* text = 0; gtk_tree_model_get( action->model, &iter, 0, &text, -1 ); // Column 0 // Check for match if( strcmp( target_text, text ) == 0 ){ found = true; break; } } ++row; valid = gtk_tree_model_iter_next( action->model, &iter ); } if( !found ) row = -1; return row; } // Checks if all comma separated text fragments are in the list and // returns a ustring with a list of missing fragments. // This is useful for checking if all fonts in a font-family fallback // list are available on the system. // // This routine could also create a Pango Markup string to show which // fragments are invalid in the entry box itself. See: // http://developer.gnome.org/pango/stable/PangoMarkupFormat.html // However... it appears that while one can retrieve the PangoLayout // for a GtkEntry box, it is only a copy and changing it has no effect. // PangoLayout * pl = gtk_entry_get_layout( entry ); // pango_layout_set_markup( pl, "NEW STRING", -1 ); // DOESN'T WORK static Glib::ustring check_comma_separated_text( Ink_ComboBoxEntry_Action* action ) { Glib::ustring missing; // Parse fallback_list using a comma as deliminator gchar** tokens = g_strsplit( action->text, ",", 0 ); gint i = 0; while( tokens[i] != NULL ) { // Remove any surrounding white space. g_strstrip( tokens[i] ); if( get_active_row_from_text( action, tokens[i], true ) == -1 ) { missing += tokens[i]; missing += ", "; } ++i; } g_strfreev( tokens ); // Remove extra comma and space from end. if( missing.size() >= 2 ) { missing.resize( missing.size()-2 ); } return missing; } // Callbacks --------------------------------------------------- static void combo_box_changed_cb( GtkComboBox* widget, gpointer data ) { // Two things can happen to get here: // An item is selected in the drop-down menu. // Text is typed. // We only react here if an item is selected. // Get action Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION( data ); // Check if item selected: gint newActive = gtk_combo_box_get_active(widget); if( newActive >= 0 && newActive != action->active ) { action->active = newActive; GtkTreeIter iter; if( gtk_combo_box_get_active_iter( GTK_COMBO_BOX( action->combobox ), &iter ) ) { gchar* text = 0; gtk_tree_model_get( action->model, &iter, 0, &text, -1 ); gtk_entry_set_text( action->entry, text ); g_free( action->text ); action->text = text; } // Now let the world know g_signal_emit( G_OBJECT(action), signals[CHANGED], 0 ); } } static void entry_activate_cb( GtkEntry* widget, gpointer data ) { // Get text from entry box.. check if it matches a menu entry. // Get action Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION( data ); // Get text g_free( action->text ); action->text = g_strdup( gtk_entry_get_text( widget ) ); // Get row action->active = get_active_row_from_text( action, action->text ); // Set active row gtk_combo_box_set_active( GTK_COMBO_BOX( action->combobox), action->active ); // Now let the world know g_signal_emit( G_OBJECT(action), signals[CHANGED], 0 ); } static gboolean match_selected_cb( GtkEntryCompletion* /*widget*/, GtkTreeModel* model, GtkTreeIter* iter, gpointer data ) { // Get action Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION( data ); GtkEntry *entry = action->entry; if( entry) { gchar *family = 0; gtk_tree_model_get(model, iter, 0, &family, -1); // Set text in GtkEntry gtk_entry_set_text (GTK_ENTRY (entry), family ); // Set text in GtkAction g_free( action->text ); action->text = family; // Get row action->active = get_active_row_from_text( action, action->text ); // Set active row gtk_combo_box_set_active( GTK_COMBO_BOX( action->combobox), action->active ); // Now let the world know g_signal_emit( G_OBJECT(action), signals[CHANGED], 0 ); return true; } return false; } static void ink_comboboxentry_action_defocus( Ink_ComboBoxEntry_Action* action ) { if ( action->focusWidget ) { gtk_widget_grab_focus( action->focusWidget ); } } gboolean keypress_cb( GtkWidget * /*widget*/, GdkEventKey *event, gpointer data ) { gboolean wasConsumed = FALSE; /* default to report event not consumed */ guint key = 0; Ink_ComboBoxEntry_Action* action = INK_COMBOBOXENTRY_ACTION( data ); gdk_keymap_translate_keyboard_state( gdk_keymap_get_for_display( gdk_display_get_default() ), event->hardware_keycode, (GdkModifierType)event->state, 0, &key, 0, 0, 0 ); switch ( key ) { // TODO Add bindings for Tab/LeftTab case GDK_KEY_Escape: { //gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal ); ink_comboboxentry_action_defocus( action ); wasConsumed = TRUE; } break; case GDK_KEY_Return: case GDK_KEY_KP_Enter: { ink_comboboxentry_action_defocus( action ); //wasConsumed = TRUE; } break; } return wasConsumed; }