Code

OCAL. Fix for Bug #638844 (Errors printed to console if openclipart search fails).
[inkscape.git] / src / ink-comboboxentry-action.cpp
1 /*
2  * A subclass of GtkAction that wraps a GtkComboBoxEntry.
3  * Features:
4  *   Setting GtkEntryBox width in characters.
5  *   Passing a function for formatting cells.
6  *   Displaying a warning if text isn't in list.
7  *   Setting names for GtkComboBoxEntry and GtkEntry (actionName_combobox, actionName_entry)
8  *     to allow setting resources.
9  *
10  * Author(s):
11  *   Tavmjong Bah
12  *   Jon A. Cruz <jon@joncruz.org>
13  *
14  * Copyright (C) 2010 Authors
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 /*
20  * We must provide for both a toolbar item and a menu item.
21  * As we don't know which widgets are used (or even constructed),
22  * we must keep track of things like active entry ourselves.
23  */
25 #include <iostream>
26 #include <string.h>
28 #include <gtk/gtk.h>
29 #include <gtk/gtktoolitem.h>
30 #include <gtk/gtkcomboboxentry.h>
31 #include <gtk/gtkentrycompletion.h>
33 #include "ink-comboboxentry-action.h"
35 // Must handle both tool and menu items!
36 static GtkWidget* create_tool_item( GtkAction* action );
37 static GtkWidget* create_menu_item( GtkAction* action );
39 // Internal
40 static gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text );
42 // Callbacks
43 static void combo_box_changed_cb( GtkComboBoxEntry* widget, gpointer data );
44 static void entry_activate_cb( GtkEntry* widget, gpointer data );
45 static gboolean match_selected_cb( GtkEntryCompletion* widget, GtkTreeModel* model, GtkTreeIter* iter, gpointer data );
47 enum {
48   PROP_MODEL = 1,
49   PROP_COMBOBOX,
50   PROP_ENTRY,
51   PROP_ENTRY_WIDTH,
52   PROP_EXTRA_WIDTH,
53   PROP_CELL_DATA_FUNC,
54   PROP_POPUP
55 };
57 enum {
58   CHANGED = 0,
59   ACTIVATED,
60   N_SIGNALS
61 };
62 static guint signals[N_SIGNALS] = {0};
64 static GtkActionClass *ink_comboboxentry_action_parent_class = NULL;
65 static GQuark gDataName = 0;
67 static void ink_comboboxentry_action_finalize (GObject *object)
68 {
69   // Free any allocated resources.
71   G_OBJECT_CLASS (ink_comboboxentry_action_parent_class)->finalize (object);
72 }
75 static void ink_comboboxentry_action_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
76 {
77   Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION (object);
79   switch(property_id) {
81   case PROP_MODEL:
82     action->model = GTK_TREE_MODEL( g_value_get_object( value ));
83     break;
85   case PROP_COMBOBOX:
86     action->combobox = GTK_COMBO_BOX_ENTRY( g_value_get_object( value ));
87     break;
89   case PROP_ENTRY:
90     action->entry = GTK_ENTRY( g_value_get_object( value ));
91     break;
93   case PROP_ENTRY_WIDTH:
94     action->entry_width = g_value_get_int( value );
95     break;
97   case PROP_EXTRA_WIDTH:
98     action->extra_width = g_value_get_int( value );
99     break;
101   case PROP_CELL_DATA_FUNC:
102     action->cell_data_func = g_value_get_pointer( value );
103     break;
105   case PROP_POPUP:
106     action->popup  = g_value_get_boolean( value );
107     break;
109   default:
110     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
111   }
115 static void ink_comboboxentry_action_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
117   Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION (object);
119   switch(property_id) {
121   case PROP_MODEL:
122     g_value_set_object (value, action->model);
123     break;
125   case PROP_COMBOBOX:
126     g_value_set_object (value, action->combobox);
127     break;
129   case PROP_ENTRY:
130     g_value_set_object (value, action->entry);
131     break;
133   case PROP_ENTRY_WIDTH:
134     g_value_set_int (value, action->entry_width);
135     break;
137   case PROP_EXTRA_WIDTH:
138     g_value_set_int (value, action->extra_width);
139     break;
141   case PROP_CELL_DATA_FUNC:
142     g_value_set_pointer (value, action->cell_data_func);
143     break;
145   case PROP_POPUP:
146     g_value_set_boolean (value, action->popup);
147     break;
149   default:
150     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
151   }
154 static void
155 ink_comboboxentry_action_connect_proxy (GtkAction *action,
156                                         GtkWidget *proxy)
158   /* Override any proxy properties. */
159   //  if (GTK_IS_MENU_ITEM (proxy)) {
160   //  }
162   GTK_ACTION_CLASS (ink_comboboxentry_action_parent_class)->connect_proxy (action, proxy);
166 static void ink_comboboxentry_action_class_init (Ink_ComboBoxEntry_ActionClass *klass)
169   GObjectClass     *gobject_class = G_OBJECT_CLASS (klass);
170   GtkActionClass *gtkaction_class = GTK_ACTION_CLASS (klass);
172   gtkaction_class->connect_proxy  = ink_comboboxentry_action_connect_proxy;
174   gobject_class->finalize      = ink_comboboxentry_action_finalize;
175   gobject_class->set_property  = ink_comboboxentry_action_set_property;
176   gobject_class->get_property  = ink_comboboxentry_action_get_property;
178   gDataName = g_quark_from_string("ink_comboboxentry-action");
180   klass->parent_class.create_tool_item = create_tool_item;
181   klass->parent_class.create_menu_item = create_menu_item;
183   ink_comboboxentry_action_parent_class = GTK_ACTION_CLASS(g_type_class_peek_parent (klass) );
185   g_object_class_install_property (
186                                    gobject_class,
187                                    PROP_MODEL,
188                                    g_param_spec_object ("model",
189                                                         "Tree Model",
190                                                         "Tree Model",
191                                                         GTK_TYPE_TREE_MODEL,
192                                                         (GParamFlags)G_PARAM_READWRITE));
193   g_object_class_install_property (
194                                    gobject_class,
195                                    PROP_COMBOBOX,
196                                    g_param_spec_object ("combobox",
197                                                         "GtkComboBoxEntry",
198                                                         "GtkComboBoxEntry",
199                                                         GTK_TYPE_WIDGET,
200                                                         (GParamFlags)G_PARAM_READABLE));
201   g_object_class_install_property (
202                                    gobject_class,
203                                    PROP_ENTRY,
204                                    g_param_spec_object ("entry",
205                                                         "GtkEntry",
206                                                         "GtkEntry",
207                                                         GTK_TYPE_WIDGET,
208                                                         (GParamFlags)G_PARAM_READABLE));
209   g_object_class_install_property (
210                                    gobject_class,
211                                    PROP_ENTRY_WIDTH,
212                                    g_param_spec_int ("entry_width",
213                                                      "EntryBox width",
214                                                      "EntryBox width (characters)",
215                                                      -1.0, 100, -1.0,
216                                                      (GParamFlags)G_PARAM_READWRITE));
218   g_object_class_install_property (
219                                    gobject_class,
220                                    PROP_EXTRA_WIDTH,
221                                    g_param_spec_int ("extra_width",
222                                                      "Extra width",
223                                                      "Extra width (px)",
224                                                      -1.0, 500, -1.0,
225                                                      (GParamFlags)G_PARAM_READWRITE));
227   g_object_class_install_property (
228                                    gobject_class,
229                                    PROP_CELL_DATA_FUNC,
230                                    g_param_spec_pointer ("cell_data_func",
231                                                          "Cell Data Func",
232                                                          "Cell Deta Function",
233                                                          (GParamFlags)G_PARAM_READWRITE));
235   g_object_class_install_property (
236                                    gobject_class,
237                                    PROP_POPUP,
238                                    g_param_spec_boolean ("popup",
239                                                          "Entry Popup",
240                                                          "Entry Popup",
241                                                          false,
242                                                          (GParamFlags)G_PARAM_READWRITE));
244   // We need to know when GtkComboBoxEvent or Menu ready for reading
245   signals[CHANGED] = g_signal_new( "changed",
246                                    G_TYPE_FROM_CLASS(klass),
247                                    G_SIGNAL_RUN_FIRST,
248                                    G_STRUCT_OFFSET(Ink_ComboBoxEntry_ActionClass, changed),
249                                    NULL, NULL,
250                                    g_cclosure_marshal_VOID__VOID,
251                                    G_TYPE_NONE, 0);
253   // Probably not needed... originally to keep track of key-presses.
254   signals[ACTIVATED] = g_signal_new( "activated",
255                                    G_TYPE_FROM_CLASS(klass),
256                                    G_SIGNAL_RUN_FIRST,
257                                    G_STRUCT_OFFSET(Ink_ComboBoxEntry_ActionClass, activated),
258                                    NULL, NULL,
259                                    g_cclosure_marshal_VOID__VOID,
260                                    G_TYPE_NONE, 0);
264 static void ink_comboboxentry_action_init (Ink_ComboBoxEntry_Action *action)
266   action->active = -1;
267   action->text = NULL;
268   action->entry_completion = NULL;
269 #if !GTK_CHECK_VERSION(2,16,0)
270   action->indicator = NULL;
271 #endif
272   action->popup = false;
273   action->warning = NULL;
274   action->altx_name = NULL;
277 GType ink_comboboxentry_action_get_type ()
279   static GType ink_comboboxentry_action_type = 0;
281   if (!ink_comboboxentry_action_type) {
282     static const GTypeInfo ink_comboboxentry_action_info = {
283       sizeof(Ink_ComboBoxEntry_ActionClass),
284       NULL, /* base_init */
285       NULL, /* base_finalize */
286       (GClassInitFunc) ink_comboboxentry_action_class_init,
287       NULL, /* class_finalize */
288       NULL, /* class_data */
289       sizeof(Ink_ComboBoxEntry_Action),
290       0,    /* n_preallocs */
291       (GInstanceInitFunc)ink_comboboxentry_action_init, /* instance_init */
292       NULL  /* value_table */
293     };
295     ink_comboboxentry_action_type = g_type_register_static (GTK_TYPE_ACTION,
296                                                             "Ink_ComboBoxEntry_Action",
297                                                             &ink_comboboxentry_action_info,
298                                                             (GTypeFlags)0 );
299   }
301   return ink_comboboxentry_action_type;
305 Ink_ComboBoxEntry_Action *ink_comboboxentry_action_new (const gchar   *name,
306                                                         const gchar   *label,
307                                                         const gchar   *tooltip,
308                                                         const gchar   *stock_id,
309                                                         GtkTreeModel  *model,
310                                                         gint           entry_width,
311                                                         gint           extra_width,
312                                                         void          *cell_data_func )
314   g_return_val_if_fail (name != NULL, NULL);
316   return (Ink_ComboBoxEntry_Action*)g_object_new (INK_COMBOBOXENTRY_TYPE_ACTION,
317                                                   "name",           name,
318                                                   "label",          label,
319                                                   "tooltip",        tooltip,
320                                                   "stock-id",       stock_id,
321                                                   "model",          model,
322                                                   "entry_width",    entry_width,
323                                                   "extra_width",    extra_width,
324                                                   "cell_data_func", cell_data_func,
325                                                   NULL);
328 // Create a widget for a toolbar.
329 GtkWidget* create_tool_item( GtkAction* action )
331   GtkWidget* item = 0;
333   if ( INK_COMBOBOXENTRY_IS_ACTION( action ) && INK_COMBOBOXENTRY_ACTION(action)->model ) {
335     Ink_ComboBoxEntry_Action* ink_comboboxentry_action = INK_COMBOBOXENTRY_ACTION( action );
337     gchar *action_name = g_strdup( gtk_action_get_name( action ) );
338     gchar *combobox_name = g_strjoin( NULL, action_name, "_combobox", NULL );
339     gchar *entry_name =    g_strjoin( NULL, action_name, "_entry", NULL );
340     g_free( action_name );
342     item = GTK_WIDGET( gtk_tool_item_new() );
344     GtkWidget* comboBoxEntry = gtk_combo_box_entry_new_with_model( ink_comboboxentry_action->model, 0 );
345     // Name it so we can muck with it using an RC file
346     gtk_widget_set_name( comboBoxEntry, combobox_name );
347     g_free( combobox_name );
349     {
350         GtkWidget *align = gtk_alignment_new(0, 0.5, 0, 0);
351 #if GTK_CHECK_VERSION(2,16,0)
352         gtk_container_add( GTK_CONTAINER(align), comboBoxEntry );
353 #else // GTK_CHECK_VERSION(2,16,0)
354         GtkWidget *hbox = gtk_hbox_new( FALSE, 0 );
355         ink_comboboxentry_action->indicator = gtk_image_new_from_stock(GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
356         gtk_box_pack_start( GTK_BOX(hbox), comboBoxEntry, TRUE, TRUE, 0 );
357         gtk_box_pack_start( GTK_BOX(hbox), ink_comboboxentry_action->indicator, FALSE, FALSE, 0 );
358         gtk_container_add( GTK_CONTAINER(align), hbox );
359 #endif // GTK_CHECK_VERSION(2,16,0)
360         gtk_container_add( GTK_CONTAINER(item), align );
361     }
363     ink_comboboxentry_action->combobox = GTK_COMBO_BOX_ENTRY(comboBoxEntry);
365     gtk_combo_box_set_active( GTK_COMBO_BOX( comboBoxEntry ), ink_comboboxentry_action->active );
367     g_signal_connect( G_OBJECT(comboBoxEntry), "changed", G_CALLBACK(combo_box_changed_cb), action );
369     // Optionally add formatting...
370     if( ink_comboboxentry_action->cell_data_func != NULL ) {
371       GtkCellRenderer *cell = gtk_cell_renderer_text_new();
372       gtk_cell_layout_clear( GTK_CELL_LAYOUT( comboBoxEntry ) );
373       gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( comboBoxEntry ), cell, true );
374       gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT( comboBoxEntry ), cell,
375                                           GtkCellLayoutDataFunc (ink_comboboxentry_action->cell_data_func),
376                                           NULL, NULL );
377     }
379     // Optionally widen the combobox width... which widens the drop-down list in list mode.
380     if( ink_comboboxentry_action->extra_width > 0 ) {
381       GtkRequisition req;
382       gtk_widget_size_request( GTK_WIDGET( ink_comboboxentry_action->combobox ), &req );
383       gtk_widget_set_size_request( GTK_WIDGET( ink_comboboxentry_action->combobox ),
384                                    req.width + ink_comboboxentry_action->extra_width, -1 );
385     }
387     // Get reference to GtkEntry and fiddle a bit with it.
388     GtkWidget *child = gtk_bin_get_child( GTK_BIN(comboBoxEntry) );
390     // Name it so we can muck with it using an RC file
391     gtk_widget_set_name( child, entry_name );
392     g_free( entry_name );
394     if( child && GTK_IS_ENTRY( child ) ) {
396       ink_comboboxentry_action->entry = GTK_ENTRY(child);
398       // Change width
399       if( ink_comboboxentry_action->entry_width > 0 ) {
400           gtk_entry_set_width_chars (GTK_ENTRY (child), ink_comboboxentry_action->entry_width );
401       }
403       // Add pop-up entry completion if required
404       if( ink_comboboxentry_action->popup ) {
405           ink_comboboxentry_action_popup_enable( ink_comboboxentry_action );
406       }
408       // Add altx_name if required
409       if( ink_comboboxentry_action->altx_name ) {
410           g_object_set_data( G_OBJECT( child ), ink_comboboxentry_action->altx_name, ink_comboboxentry_action->entry );
411       }
413       // Add signal for GtkEntry to check if finished typing.
414       g_signal_connect( G_OBJECT(child), "activate", G_CALLBACK(entry_activate_cb), action );
416     }
418 #if GTK_CHECK_VERSION(2,16,0)
419     gtk_action_connect_proxy( GTK_ACTION( action ), item );
420 #endif
422     gtk_widget_show_all( item );
424   } else {
426     item = ink_comboboxentry_action_parent_class->create_tool_item( action );
428   }
430   return item;
433 // Create a drop-down menu.
434 GtkWidget* create_menu_item( GtkAction* action )
436   GtkWidget* item = 0;
438     item = ink_comboboxentry_action_parent_class->create_menu_item( action );
439     g_warning( "ink_comboboxentry_action: create_menu_item not implemented" );
440     // One can easily modify ege-select-one-action routine to implement this.
441   return item;
444 // Setters/Getters ---------------------------------------------------
446 GtkTreeModel *ink_comboboxentry_action_get_model( Ink_ComboBoxEntry_Action* action ) {
448   return action->model;
451 GtkComboBoxEntry *ink_comboboxentry_action_get_comboboxentry( Ink_ComboBoxEntry_Action* action ) {
453   return action->combobox;
456 gchar* ink_comboboxentry_action_get_active_text( Ink_ComboBoxEntry_Action* action ) {
458   gchar* text = g_strdup( action->text );
459   return text;
462 gboolean ink_comboboxentry_action_set_active_text( Ink_ComboBoxEntry_Action* ink_comboboxentry_action, const gchar* text ) {
464   g_free( ink_comboboxentry_action->text );
465   ink_comboboxentry_action->text = g_strdup( text );
467   // Get active row or -1 if none
468   ink_comboboxentry_action->active = get_active_row_from_text( ink_comboboxentry_action, ink_comboboxentry_action->text );
470   // Set active row, check that combobox has been created.
471   if( ink_comboboxentry_action->combobox ) {
472     gtk_combo_box_set_active( GTK_COMBO_BOX( ink_comboboxentry_action->combobox ), ink_comboboxentry_action->active );
473   }
475   // Fiddle with entry
476   if( ink_comboboxentry_action->entry ) {
478     // Explicitly set text in GtkEntry box (won't be set if text not in list).
479     gtk_entry_set_text( ink_comboboxentry_action->entry, text );
481     // Show or hide warning
482     if( ink_comboboxentry_action->active == -1 && ink_comboboxentry_action->warning != NULL ) {
483 #if GTK_CHECK_VERSION(2,16,0)
484       {
485           GtkStockItem item;
486           gboolean isStock = gtk_stock_lookup( GTK_STOCK_DIALOG_WARNING, &item );
487           if (isStock) {        
488               gtk_entry_set_icon_from_stock( ink_comboboxentry_action->entry,
489                                              GTK_ENTRY_ICON_SECONDARY,
490                                              GTK_STOCK_DIALOG_WARNING );
491           } else {
492               gtk_entry_set_icon_from_icon_name( ink_comboboxentry_action->entry,
493                                                  GTK_ENTRY_ICON_SECONDARY,
494                                                  GTK_STOCK_DIALOG_WARNING );
495           }
496       }
497       // Can't add tooltip until icon set
498       gtk_entry_set_icon_tooltip_text( ink_comboboxentry_action->entry,
499                                        GTK_ENTRY_ICON_SECONDARY,
500                                        ink_comboboxentry_action->warning );
501 #else // GTK_CHECK_VERSION(2,16,0)
502       gtk_image_set_from_stock( GTK_IMAGE(ink_comboboxentry_action->indicator), GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
503       gtk_widget_set_tooltip_text( ink_comboboxentry_action->indicator, ink_comboboxentry_action->warning );
504 #endif // GTK_CHECK_VERSION(2,16,0)
505     } else {
506 #if GTK_CHECK_VERSION(2,16,0)
507       gtk_entry_set_icon_from_icon_name( GTK_ENTRY(ink_comboboxentry_action->entry),
508                                          GTK_ENTRY_ICON_SECONDARY,
509                                          NULL );
510       gtk_entry_set_icon_from_stock( GTK_ENTRY(ink_comboboxentry_action->entry),
511                                      GTK_ENTRY_ICON_SECONDARY,
512                                      NULL );
513 #else // GTK_CHECK_VERSION(2,16,0)
514       gtk_image_set_from_stock( GTK_IMAGE(ink_comboboxentry_action->indicator), NULL, GTK_ICON_SIZE_SMALL_TOOLBAR);
515       gtk_widget_set_tooltip_text( ink_comboboxentry_action->indicator, NULL );
516 #endif // GTK_CHECK_VERSION(2,16,0)
517     }
518   }
520   // Return if active text in list
521   gboolean found = ( ink_comboboxentry_action->active != -1 );
522   return found;
525 void ink_comboboxentry_action_set_entry_width( Ink_ComboBoxEntry_Action* action, gint entry_width ) {
527   action->entry_width = entry_width;
529   // Widget may not have been created....
530   if( action->entry ) {
531     gtk_entry_set_width_chars( GTK_ENTRY(action->entry), entry_width );
532   }
535 void ink_comboboxentry_action_set_extra_width( Ink_ComboBoxEntry_Action* action, gint extra_width ) {
537   action->extra_width = extra_width;
539   // Widget may not have been created....
540   if( action->combobox ) {
541     GtkRequisition req;
542     gtk_widget_size_request( GTK_WIDGET( action->combobox ), &req );
543     gtk_widget_set_size_request( GTK_WIDGET( action->combobox ), req.width + action->extra_width, -1 );
544   }
547 void ink_comboboxentry_action_popup_enable( Ink_ComboBoxEntry_Action* action ) {
549   action->popup = true;
551   // Widget may not have been created....
552   if( action->entry ) {
554     // Check we don't already have a GtkEntryCompletion
555     if( action->entry_completion ) return;
557     action->entry_completion = gtk_entry_completion_new();
559     gtk_entry_set_completion( action->entry, action->entry_completion );
560     gtk_entry_completion_set_model( action->entry_completion, action->model );
561     gtk_entry_completion_set_text_column( action->entry_completion, 0 );
562     gtk_entry_completion_set_popup_completion( action->entry_completion, true );
563     gtk_entry_completion_set_inline_completion( action->entry_completion, false );
564     gtk_entry_completion_set_inline_selection( action->entry_completion, true );
566     g_signal_connect (G_OBJECT (action->entry_completion),  "match-selected", G_CALLBACK (match_selected_cb), action );
568   }
571 void ink_comboboxentry_action_popup_disable( Ink_ComboBoxEntry_Action* action ) {
573   action->popup = false;
575   if( action->entry_completion ) {
576     gtk_object_destroy( GTK_OBJECT( action->entry_completion ) );
577     action->entry_completion = 0;
578   }
581 void     ink_comboboxentry_action_set_warning( Ink_ComboBoxEntry_Action* action, const gchar* warning ) {
583   g_free( action->warning );
584   action->warning = g_strdup( warning );
586   // Widget may not have been created....
587   if( action->entry ) {
588 #if GTK_CHECK_VERSION(2,16,0)
589     gtk_entry_set_icon_tooltip_text( GTK_ENTRY(action->entry),
590                                      GTK_ENTRY_ICON_SECONDARY,
591                                      action->warning );
592 #else // GTK_CHECK_VERSION(2,16,0)
593     gtk_image_set_from_stock( GTK_IMAGE(action->indicator), action->warning ? GTK_STOCK_DIALOG_WARNING : 0, GTK_ICON_SIZE_SMALL_TOOLBAR );
594 #endif // GTK_CHECK_VERSION(2,16,0)
595   }
598 void     ink_comboboxentry_action_set_altx_name( Ink_ComboBoxEntry_Action* action, const gchar* altx_name ) {
600   g_free( action->altx_name );
601   action->altx_name = g_strdup( altx_name );
603   // Widget may not have been created....
604   if( action->entry ) {
605     g_object_set_data( G_OBJECT(action->entry), action->altx_name, action->entry );
606   }
609 // Internal ---------------------------------------------------
611 // Return row of active text or -1 if not found.
612 gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text ) {
614   // Check if text in list
615   gint row = 0;
616   gboolean found = false;
617   GtkTreeIter iter;
618   gboolean valid = gtk_tree_model_get_iter_first( action->model, &iter );
619   while ( valid ) {
621     // Get text from list entry
622     gchar* text = 0;
623     gtk_tree_model_get( action->model, &iter, 0, &text, -1 ); // Column 0
625     // Check for match
626     if( strcmp( target_text, text ) == 0 ){
627       found = true;
628       break;
629     }
630     ++row;
631     valid = gtk_tree_model_iter_next( action->model, &iter );
632   }
634   if( !found ) row = -1;
636   return row;
641 // Callbacks ---------------------------------------------------
643 static void combo_box_changed_cb( GtkComboBoxEntry* widget, gpointer data ) {
645   // Two things can happen to get here:
646   //   An item is selected in the drop-down menu.
647   //   Text is typed.
648   // We only react here if an item is selected.
650   // Get action
651   Ink_ComboBoxEntry_Action *act = INK_COMBOBOXENTRY_ACTION( data );
653   // Check if item selected:
654   gint newActive = gtk_combo_box_get_active( GTK_COMBO_BOX( widget ));
655   if( newActive >= 0 ) {
657     if( newActive != act->active ) {
658       act->active = newActive;
659       g_free( act->text );
660       act->text = gtk_combo_box_get_active_text( GTK_COMBO_BOX( widget ));
662       // Now let the world know
663       g_signal_emit( G_OBJECT(act), signals[CHANGED], 0 );
665     }
666   }
669 static void entry_activate_cb( GtkEntry* widget, gpointer data ) {
671   // Get text from entry box.. check if it matches a menu entry.
673   // Get action
674   Ink_ComboBoxEntry_Action *ink_comboboxentry_action = INK_COMBOBOXENTRY_ACTION( data );
676   // Get text
677   g_free( ink_comboboxentry_action->text );
678   ink_comboboxentry_action->text = g_strdup( gtk_entry_get_text( widget ) );
680   // Get row
681   ink_comboboxentry_action->active =
682     get_active_row_from_text( ink_comboboxentry_action, ink_comboboxentry_action->text );
684   // Set active row
685   gtk_combo_box_set_active( GTK_COMBO_BOX( ink_comboboxentry_action->combobox), ink_comboboxentry_action->active );
687   // Now let the world know
688   g_signal_emit( G_OBJECT(ink_comboboxentry_action), signals[CHANGED], 0 );
692 static gboolean match_selected_cb( GtkEntryCompletion* /*widget*/, GtkTreeModel* model, GtkTreeIter* iter, gpointer data )
694   // Get action
695   Ink_ComboBoxEntry_Action *ink_comboboxentry_action = INK_COMBOBOXENTRY_ACTION( data );
696   GtkEntry *entry = ink_comboboxentry_action->entry;
698   if( entry) {
699     gchar *family = 0;
700     gtk_tree_model_get(model, iter, 0, &family, -1);
702     // Set text in GtkEntry
703     gtk_entry_set_text (GTK_ENTRY (entry), family );
705     // Set text in GtkAction
706     g_free( ink_comboboxentry_action->text );
707     ink_comboboxentry_action->text = family;
709     // Get row
710     ink_comboboxentry_action->active =
711       get_active_row_from_text( ink_comboboxentry_action, ink_comboboxentry_action->text );
713     // Set active row
714     gtk_combo_box_set_active( GTK_COMBO_BOX( ink_comboboxentry_action->combobox), ink_comboboxentry_action->active );
716     // Now let the world know
717     g_signal_emit( G_OBJECT(ink_comboboxentry_action), signals[CHANGED], 0 );
719     return true;
720   }
721   return false;