comparison src/gtkcombobox.c @ 10708:d0d1d631ed49

[gaim-migrate @ 12297] Add some files I missed that are necessary for Gtk 2.4 support, actually commit the makefile changes to compile on Gtk 2.4, fix building with Gtk 2.6 which I broke. And some Gtk 2.2 support too, which should be considered experimental (where experimental means "almost completely broken"). Also a couple of tweaks which might make Gaim continue working after upgrading Gtk without rebuilding Gaim, but I haven't actually tested that. committer: Tailor Script <tailor@pidgin.im>
author Stu Tomlinson <stu@nosnilmot.com>
date Sun, 20 Mar 2005 17:37:31 +0000
parents
children 8cb75ba77f9d
comparison
equal deleted inserted replaced
10707:7672a83c04da 10708:d0d1d631ed49
1 /* gtkcombobox.c
2 * Copyright (C) 2002, 2003 Kristian Rietveld <kris@gtk.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20 /*
21 #include <config.h>
22 */
23 #include <gtk/gtkversion.h>
24 #if !GTK_CHECK_VERSION(2,4,0)
25 #include "gtkcombobox.h"
26
27 #include <gtk/gtkarrow.h>
28 #include <gtk/gtkbindings.h>
29 #include "gtkcelllayout.h"
30 #include <gtk/gtkcellrenderertext.h>
31 #include "gtkcellview.h"
32 #include "gtkcellviewmenuitem.h"
33 #include <gtk/gtkeventbox.h>
34 #include <gtk/gtkframe.h>
35 #include <gtk/gtkhbox.h>
36 #include <gtk/gtkliststore.h>
37 #include <gtk/gtkmain.h>
38 #include <gtk/gtkmenu.h>
39 #include <gtk/gtktogglebutton.h>
40 #include <gtk/gtktreeselection.h>
41 /*
42 #include <gtk/gtktreeprivate.h>
43 */
44 #include <gtk/gtkvseparator.h>
45 #include <gtk/gtkwindow.h>
46
47 #include <gdk/gdkkeysyms.h>
48
49 #include <gobject/gvaluecollector.h>
50
51 #include <string.h>
52 #include <stdarg.h>
53
54 #ifdef ENABLE_NLS
55 # include <libintl.h>
56 # define _(x) gettext(x)
57 # ifdef gettext_noop
58 # define N_(String) gettext_noop (String)
59 # else
60 # define N_(String) (String)
61 # endif
62 #else
63 # define N_(String) (String)
64 # define _(x) (x)
65 #endif
66
67 /* WELCOME, to THE house of evil code */
68
69 typedef struct _ComboCellInfo ComboCellInfo;
70 struct _ComboCellInfo
71 {
72 GtkCellRenderer *cell;
73 GSList *attributes;
74
75 GtkCellLayoutDataFunc func;
76 gpointer func_data;
77 GDestroyNotify destroy;
78
79 guint expand : 1;
80 guint pack : 1;
81 };
82
83 struct _GtkComboBoxPrivate
84 {
85 GtkTreeModel *model;
86
87 gint col_column;
88 gint row_column;
89
90 gint wrap_width;
91
92 gint active_item;
93
94 GtkWidget *tree_view;
95 GtkTreeViewColumn *column;
96
97 GtkWidget *cell_view;
98 GtkWidget *cell_view_frame;
99
100 GtkWidget *button;
101 GtkWidget *box;
102 GtkWidget *arrow;
103 GtkWidget *separator;
104
105 GtkWidget *popup_widget;
106 GtkWidget *popup_window;
107 GtkWidget *popup_frame;
108
109 guint inserted_id;
110 guint deleted_id;
111 guint reordered_id;
112 guint changed_id;
113
114 gint width;
115 GSList *cells;
116
117 guint popup_in_progress : 1;
118 guint destroying : 1;
119 };
120
121 /* While debugging this evil code, I have learned that
122 * there are actually 4 modes to this widget, which can
123 * be characterized as follows
124 *
125 * 1) menu mode, no child added
126 *
127 * tree_view -> NULL
128 * cell_view -> GtkCellView, regular child
129 * cell_view_frame -> NULL
130 * button -> GtkToggleButton set_parent to combo
131 * box -> child of button
132 * arrow -> child of box
133 * separator -> child of box
134 * popup_widget -> GtkMenu
135 * popup_window -> NULL
136 * popup_frame -> NULL
137 *
138 * 2) menu mode, child added
139 *
140 * tree_view -> NULL
141 * cell_view -> NULL
142 * cell_view_frame -> NULL
143 * button -> GtkToggleButton set_parent to combo
144 * box -> NULL
145 * arrow -> GtkArrow, child of button
146 * separator -> NULL
147 * popup_widget -> GtkMenu
148 * popup_window -> NULL
149 * popup_frame -> NULL
150 *
151 * 3) list mode, no child added
152 *
153 * tree_view -> GtkTreeView, child of popup_frame
154 * cell_view -> GtkCellView, regular child
155 * cell_view_frame -> GtkFrame, set parent to combo
156 * button -> GtkToggleButton, set_parent to combo
157 * box -> NULL
158 * arrow -> GtkArrow, child of button
159 * separator -> NULL
160 * popup_widget -> tree_view
161 * popup_window -> GtkWindow
162 * popup_frame -> GtkFrame, child of popup_window
163 *
164 * 4) list mode, child added
165 *
166 * tree_view -> GtkTreeView, child of popup_frame
167 * cell_view -> NULL
168 * cell_view_frame -> NULL
169 * button -> GtkToggleButton, set_parent to combo
170 * box -> NULL
171 * arrow -> GtkArrow, child of button
172 * separator -> NULL
173 * popup_widget -> tree_view
174 * popup_window -> GtkWindow
175 * popup_frame -> GtkFrame, child of popup_window
176 *
177 */
178
179 enum {
180 CHANGED,
181 LAST_SIGNAL
182 };
183
184 enum {
185 PROP_0,
186 PROP_MODEL,
187 PROP_WRAP_WIDTH,
188 PROP_ROW_SPAN_COLUMN,
189 PROP_COLUMN_SPAN_COLUMN,
190 PROP_ACTIVE
191 };
192
193 static GtkBinClass *parent_class = NULL;
194 static guint combo_box_signals[LAST_SIGNAL] = {0,};
195
196 #define BONUS_PADDING 4
197
198
199 /* common */
200 static void gtk_combo_box_class_init (GtkComboBoxClass *klass);
201 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
202 static void gtk_combo_box_init (GtkComboBox *combo_box);
203 static void gtk_combo_box_finalize (GObject *object);
204 static void gtk_combo_box_destroy (GtkObject *object);
205
206 static void gtk_combo_box_set_property (GObject *object,
207 guint prop_id,
208 const GValue *value,
209 GParamSpec *spec);
210 static void gtk_combo_box_get_property (GObject *object,
211 guint prop_id,
212 GValue *value,
213 GParamSpec *spec);
214
215 static void gtk_combo_box_state_changed (GtkWidget *widget,
216 GtkStateType previous);
217 static void gtk_combo_box_style_set (GtkWidget *widget,
218 GtkStyle *previous);
219 static void gtk_combo_box_button_toggled (GtkWidget *widget,
220 gpointer data);
221 static void gtk_combo_box_add (GtkContainer *container,
222 GtkWidget *widget);
223 static void gtk_combo_box_remove (GtkContainer *container,
224 GtkWidget *widget);
225
226 static ComboCellInfo *gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
227 GtkCellRenderer *cell);
228
229 static void gtk_combo_box_menu_show (GtkWidget *menu,
230 gpointer user_data);
231 static void gtk_combo_box_menu_hide (GtkWidget *menu,
232 gpointer user_data);
233
234 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
235 GtkWidget *popup);
236 static void gtk_combo_box_menu_position_below (GtkMenu *menu,
237 gint *x,
238 gint *y,
239 gint *push_in,
240 gpointer user_data);
241 static void gtk_combo_box_menu_position_over (GtkMenu *menu,
242 gint *x,
243 gint *y,
244 gint *push_in,
245 gpointer user_data);
246 static void gtk_combo_box_menu_position (GtkMenu *menu,
247 gint *x,
248 gint *y,
249 gint *push_in,
250 gpointer user_data);
251
252 static gint gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
253 GtkTreePath *path);
254 static void gtk_combo_box_remeasure (GtkComboBox *combo_box);
255
256 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
257
258 static void gtk_combo_box_size_request (GtkWidget *widget,
259 GtkRequisition *requisition);
260 static void gtk_combo_box_size_allocate (GtkWidget *widget,
261 GtkAllocation *allocation);
262 static void gtk_combo_box_forall (GtkContainer *container,
263 gboolean include_internals,
264 GtkCallback callback,
265 gpointer callback_data);
266 static gboolean gtk_combo_box_expose_event (GtkWidget *widget,
267 GdkEventExpose *event);
268 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
269 GdkEventScroll *event);
270 static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
271 gint index);
272 static gboolean gtk_combo_box_key_press (GtkWidget *widget,
273 GdkEventKey *event,
274 gpointer data);
275
276 /* listening to the model */
277 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
278 GtkTreePath *path,
279 GtkTreeIter *iter,
280 gpointer user_data);
281 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
282 GtkTreePath *path,
283 gpointer user_data);
284 static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
285 GtkTreePath *path,
286 GtkTreeIter *iter,
287 gint *new_order,
288 gpointer user_data);
289 static void gtk_combo_box_model_row_changed (GtkTreeModel *model,
290 GtkTreePath *path,
291 GtkTreeIter *iter,
292 gpointer data);
293
294 /* list */
295 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
296 gint *x,
297 gint *y,
298 gint *width,
299 gint *height);
300 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
301 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
302
303 static void gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box);
304
305 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
306 GdkEventButton *event,
307 gpointer data);
308 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
309 GdkEventKey *event,
310 gpointer data);
311 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
312 GdkEventButton *event,
313 gpointer data);
314
315 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
316 GtkTreePath *path,
317 GtkTreeIter *iter,
318 gpointer data);
319
320 /* menu */
321 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
322 gboolean add_children);
323 static void gtk_combo_box_menu_fill (GtkComboBox *combo_box);
324 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
325
326 static void gtk_combo_box_item_get_size (GtkComboBox *combo_box,
327 gint index,
328 gint *cols,
329 gint *rows);
330 static void gtk_combo_box_relayout_item (GtkComboBox *combo_box,
331 gint index);
332 static void gtk_combo_box_relayout (GtkComboBox *combo_box);
333
334 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
335 GdkEventButton *event,
336 gpointer user_data);
337 static void gtk_combo_box_menu_item_activate (GtkWidget *item,
338 gpointer user_data);
339 static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
340 GtkTreePath *path,
341 GtkTreeIter *iter,
342 gpointer user_data);
343 static void gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
344 GtkTreePath *path,
345 gpointer user_data);
346 static void gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
347 GtkTreePath *path,
348 GtkTreeIter *iter,
349 gint *new_order,
350 gpointer user_data);
351 static void gtk_combo_box_menu_row_changed (GtkTreeModel *model,
352 GtkTreePath *path,
353 GtkTreeIter *iter,
354 gpointer data);
355 static gboolean gtk_combo_box_menu_key_press (GtkWidget *widget,
356 GdkEventKey *event,
357 gpointer data);
358
359 /* cell layout */
360 static void gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
361 GtkCellRenderer *cell,
362 gboolean expand);
363 static void gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
364 GtkCellRenderer *cell,
365 gboolean expand);
366 static void gtk_combo_box_cell_layout_clear (GtkCellLayout *layout);
367 static void gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
368 GtkCellRenderer *cell,
369 const gchar *attribute,
370 gint column);
371 static void gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
372 GtkCellRenderer *cell,
373 GtkCellLayoutDataFunc func,
374 gpointer func_data,
375 GDestroyNotify destroy);
376 static void gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
377 GtkCellRenderer *cell);
378 static void gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
379 GtkCellRenderer *cell,
380 gint position);
381 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
382 gboolean group_cycling);
383
384 static void cell_view_sync_cells (GtkComboBox *combo_box,
385 GtkCellView *cell_view);
386
387 #if !GTK_CHECK_VERSION(2,4,0)
388 static void gtk_menu_attach (GtkMenu *menu,
389 GtkWidget *child,
390 guint left_attach,
391 guint right_attach,
392 guint top_attach,
393 guint bottom_attach);
394 #endif
395
396 GType
397 gtk_combo_box_get_type (void)
398 {
399 static GType combo_box_type = 0;
400
401 if (!combo_box_type)
402 {
403 static const GTypeInfo combo_box_info =
404 {
405 sizeof (GtkComboBoxClass),
406 NULL, /* base_init */
407 NULL, /* base_finalize */
408 (GClassInitFunc) gtk_combo_box_class_init,
409 NULL, /* class_finalize */
410 NULL, /* class_data */
411 sizeof (GtkComboBox),
412 0,
413 (GInstanceInitFunc) gtk_combo_box_init
414 };
415
416 static const GInterfaceInfo cell_layout_info =
417 {
418 (GInterfaceInitFunc) gtk_combo_box_cell_layout_init,
419 NULL,
420 NULL
421 };
422
423 combo_box_type = g_type_register_static (GTK_TYPE_BIN,
424 "GaimGtkComboBox",
425 &combo_box_info,
426 0);
427
428 g_type_add_interface_static (combo_box_type,
429 GTK_TYPE_CELL_LAYOUT,
430 &cell_layout_info);
431 }
432
433 return combo_box_type;
434 }
435
436 /* common */
437 static void
438 gtk_combo_box_class_init (GtkComboBoxClass *klass)
439 {
440 GObjectClass *object_class;
441 GtkBindingSet *binding_set;
442 GtkObjectClass *gtk_object_class;
443 GtkContainerClass *container_class;
444 GtkWidgetClass *widget_class;
445
446 binding_set = gtk_binding_set_by_class (klass);
447
448 container_class = (GtkContainerClass *)klass;
449 container_class->forall = gtk_combo_box_forall;
450 container_class->add = gtk_combo_box_add;
451 container_class->remove = gtk_combo_box_remove;
452
453 widget_class = (GtkWidgetClass *)klass;
454 widget_class->size_allocate = gtk_combo_box_size_allocate;
455 widget_class->size_request = gtk_combo_box_size_request;
456 widget_class->expose_event = gtk_combo_box_expose_event;
457 widget_class->scroll_event = gtk_combo_box_scroll_event;
458 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
459 widget_class->style_set = gtk_combo_box_style_set;
460 widget_class->state_changed = gtk_combo_box_state_changed;
461
462 gtk_object_class = (GtkObjectClass *)klass;
463 gtk_object_class->destroy = gtk_combo_box_destroy;
464
465 object_class = (GObjectClass *)klass;
466 object_class->finalize = gtk_combo_box_finalize;
467 object_class->set_property = gtk_combo_box_set_property;
468 object_class->get_property = gtk_combo_box_get_property;
469
470 parent_class = g_type_class_peek_parent (klass);
471
472 /* signals */
473 combo_box_signals[CHANGED] =
474 g_signal_new ("changed",
475 G_OBJECT_CLASS_TYPE (klass),
476 G_SIGNAL_RUN_LAST,
477 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
478 NULL, NULL,
479 g_cclosure_marshal_VOID__VOID,
480 G_TYPE_NONE, 0);
481
482 /* properties */
483 g_object_class_install_property (object_class,
484 PROP_MODEL,
485 g_param_spec_object ("model",
486 _("ComboBox model"),
487 _("The model for the combo box"),
488 GTK_TYPE_TREE_MODEL,
489 G_PARAM_READWRITE));
490
491 g_object_class_install_property (object_class,
492 PROP_WRAP_WIDTH,
493 g_param_spec_int ("wrap_width",
494 _("Wrap width"),
495 _("Wrap width for layouting the items in a grid"),
496 0,
497 G_MAXINT,
498 0,
499 G_PARAM_READWRITE));
500
501 g_object_class_install_property (object_class,
502 PROP_ROW_SPAN_COLUMN,
503 g_param_spec_int ("row_span_column",
504 _("Row span column"),
505 _("TreeModel column containing the row span values"),
506 0,
507 G_MAXINT,
508 0,
509 G_PARAM_READWRITE));
510
511 g_object_class_install_property (object_class,
512 PROP_COLUMN_SPAN_COLUMN,
513 g_param_spec_int ("column_span_column",
514 _("Column span column"),
515
516 _("TreeModel column containing the column span values"),
517 0,
518 G_MAXINT,
519 0,
520 G_PARAM_READWRITE));
521
522 g_object_class_install_property (object_class,
523 PROP_ACTIVE,
524 g_param_spec_int ("active",
525 _("Active item"),
526 _("The item which is currently active"),
527 -1,
528 G_MAXINT,
529 -1,
530 G_PARAM_READWRITE));
531
532 gtk_widget_class_install_style_property (widget_class,
533 g_param_spec_boolean ("appears-as-list",
534 _("Appears as list"),
535 _("Whether combobox dropdowns should look like lists rather than menus"),
536 FALSE,
537 G_PARAM_READABLE));
538 }
539
540 static void
541 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
542 {
543 iface->pack_start = gtk_combo_box_cell_layout_pack_start;
544 iface->pack_end = gtk_combo_box_cell_layout_pack_end;
545 iface->clear = gtk_combo_box_cell_layout_clear;
546 iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
547 iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
548 iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
549 iface->reorder = gtk_combo_box_cell_layout_reorder;
550 }
551
552 static void
553 gtk_combo_box_init (GtkComboBox *combo_box)
554 {
555 combo_box->priv = g_new0(GtkComboBoxPrivate,1);
556
557 combo_box->priv->cell_view = gtk_cell_view_new ();
558 gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (combo_box));
559 GTK_BIN (combo_box)->child = combo_box->priv->cell_view;
560 gtk_widget_show (combo_box->priv->cell_view);
561
562 combo_box->priv->width = 0;
563 combo_box->priv->wrap_width = 0;
564
565 combo_box->priv->active_item = -1;
566 combo_box->priv->col_column = -1;
567 combo_box->priv->row_column = -1;
568 }
569
570 static void
571 gtk_combo_box_set_property (GObject *object,
572 guint prop_id,
573 const GValue *value,
574 GParamSpec *pspec)
575 {
576 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
577
578 switch (prop_id)
579 {
580 case PROP_MODEL:
581 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
582 break;
583
584 case PROP_WRAP_WIDTH:
585 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
586 break;
587
588 case PROP_ROW_SPAN_COLUMN:
589 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
590 break;
591
592 case PROP_COLUMN_SPAN_COLUMN:
593 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
594 break;
595
596 case PROP_ACTIVE:
597 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
598 break;
599
600 default:
601 break;
602 }
603 }
604
605 static void
606 gtk_combo_box_get_property (GObject *object,
607 guint prop_id,
608 GValue *value,
609 GParamSpec *pspec)
610 {
611 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
612
613 switch (prop_id)
614 {
615 case PROP_MODEL:
616 g_value_set_object (value, combo_box->priv->model);
617 break;
618
619 case PROP_WRAP_WIDTH:
620 g_value_set_int (value, combo_box->priv->wrap_width);
621 break;
622
623 case PROP_ROW_SPAN_COLUMN:
624 g_value_set_int (value, combo_box->priv->row_column);
625 break;
626
627 case PROP_COLUMN_SPAN_COLUMN:
628 g_value_set_int (value, combo_box->priv->col_column);
629 break;
630
631 case PROP_ACTIVE:
632 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
633 break;
634
635 default:
636 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
637 break;
638 }
639 }
640
641 static void
642 gtk_combo_box_state_changed (GtkWidget *widget,
643 GtkStateType previous)
644 {
645 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
646
647 if (GTK_WIDGET_REALIZED (widget))
648 {
649 if (combo_box->priv->tree_view && combo_box->priv->cell_view)
650 gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
651 &widget->style->base[GTK_WIDGET_STATE (widget)]);
652 }
653
654 gtk_widget_queue_draw (widget);
655 }
656
657 static void
658 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
659 {
660 gboolean appears_as_list;
661
662 /* if wrap_width > 0, then we are in grid-mode and forced to use
663 * unix style
664 */
665 if (combo_box->priv->wrap_width)
666 appears_as_list = FALSE;
667 else
668 gtk_widget_style_get (GTK_WIDGET (combo_box),
669 "appears-as-list", &appears_as_list,
670 NULL);
671
672 if (appears_as_list)
673 {
674 /* Destroy all the menu mode widgets, if they exist. */
675 if (GTK_IS_MENU (combo_box->priv->popup_widget))
676 gtk_combo_box_menu_destroy (combo_box);
677
678 /* Create the list mode widgets, if they don't already exist. */
679 if (!GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
680 gtk_combo_box_list_setup (combo_box);
681 }
682 else
683 {
684 /* Destroy all the list mode widgets, if they exist. */
685 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
686 gtk_combo_box_list_destroy (combo_box);
687
688 /* Create the menu mode widgets, if they don't already exist. */
689 if (!GTK_IS_MENU (combo_box->priv->popup_widget))
690 gtk_combo_box_menu_setup (combo_box, TRUE);
691 }
692 }
693
694 static void
695 gtk_combo_box_style_set (GtkWidget *widget,
696 GtkStyle *previous)
697 {
698 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
699
700 gtk_combo_box_check_appearance (combo_box);
701
702 if (combo_box->priv->tree_view && combo_box->priv->cell_view)
703 gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
704 &widget->style->base[GTK_WIDGET_STATE (widget)]);
705 }
706
707 static void
708 gtk_combo_box_button_toggled (GtkWidget *widget,
709 gpointer data)
710 {
711 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
712
713 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
714 {
715 if (!combo_box->priv->popup_in_progress)
716 gtk_combo_box_popup (combo_box);
717 }
718 else
719 gtk_combo_box_popdown (combo_box);
720 }
721
722 static void
723 gtk_combo_box_add (GtkContainer *container,
724 GtkWidget *widget)
725 {
726 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
727
728 if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent)
729 {
730 gtk_widget_unparent (combo_box->priv->cell_view);
731 GTK_BIN (container)->child = NULL;
732 gtk_widget_queue_resize (GTK_WIDGET (container));
733 }
734
735 gtk_widget_set_parent (widget, GTK_WIDGET (container));
736 GTK_BIN (container)->child = widget;
737
738 if (combo_box->priv->cell_view &&
739 widget != combo_box->priv->cell_view)
740 {
741 /* since the cell_view was unparented, it's gone now */
742 combo_box->priv->cell_view = NULL;
743
744 if (!combo_box->priv->tree_view && combo_box->priv->separator)
745 {
746 gtk_container_remove (GTK_CONTAINER (combo_box->priv->separator->parent),
747 combo_box->priv->separator);
748 combo_box->priv->separator = NULL;
749
750 gtk_widget_queue_resize (GTK_WIDGET (container));
751 }
752 else if (combo_box->priv->cell_view_frame)
753 {
754 gtk_widget_unparent (combo_box->priv->cell_view_frame);
755 combo_box->priv->cell_view_frame = NULL;
756 }
757 }
758 }
759
760 static void
761 gtk_combo_box_remove (GtkContainer *container,
762 GtkWidget *widget)
763 {
764 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
765 gboolean appears_as_list;
766
767 gtk_widget_unparent (widget);
768 GTK_BIN (container)->child = NULL;
769
770 if (combo_box->priv->destroying)
771 return;
772
773 gtk_widget_queue_resize (GTK_WIDGET (container));
774
775 if (!combo_box->priv->tree_view)
776 appears_as_list = FALSE;
777 else
778 appears_as_list = TRUE;
779
780 if (appears_as_list)
781 gtk_combo_box_list_destroy (combo_box);
782 else if (GTK_IS_MENU (combo_box->priv->popup_widget))
783 {
784 gtk_combo_box_menu_destroy (combo_box);
785 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
786 combo_box->priv->popup_widget = NULL;
787 }
788
789 if (!combo_box->priv->cell_view)
790 {
791 combo_box->priv->cell_view = gtk_cell_view_new ();
792 gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (container));
793 GTK_BIN (container)->child = combo_box->priv->cell_view;
794
795 gtk_widget_show (combo_box->priv->cell_view);
796 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
797 combo_box->priv->model);
798 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (combo_box->priv->cell_view));
799 }
800
801
802 if (appears_as_list)
803 gtk_combo_box_list_setup (combo_box);
804 else
805 gtk_combo_box_menu_setup (combo_box, TRUE);
806
807 gtk_combo_box_set_active_internal (combo_box, combo_box->priv->active_item);
808 }
809
810 static ComboCellInfo *
811 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
812 GtkCellRenderer *cell)
813 {
814 GSList *i;
815
816 for (i = combo_box->priv->cells; i; i = i->next)
817 {
818 ComboCellInfo *info = (ComboCellInfo *)i->data;
819
820 if (info && info->cell == cell)
821 return info;
822 }
823
824 return NULL;
825 }
826
827 static void
828 gtk_combo_box_menu_show (GtkWidget *menu,
829 gpointer user_data)
830 {
831 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
832
833 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
834 TRUE);
835 combo_box->priv->popup_in_progress = FALSE;
836 }
837
838 static void
839 gtk_combo_box_menu_hide (GtkWidget *menu,
840 gpointer user_data)
841 {
842 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
843
844 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
845 FALSE);
846 }
847
848 static void
849 gtk_combo_box_detacher (GtkWidget *widget,
850 GtkMenu *menu)
851 {
852 GtkComboBox *combo_box;
853
854 g_return_if_fail (GTK_IS_COMBO_BOX (widget));
855
856 combo_box = GTK_COMBO_BOX (widget);
857 g_return_if_fail (combo_box->priv->popup_widget == (GtkWidget*) menu);
858
859 g_signal_handlers_disconnect_by_func (menu,
860 gtk_combo_box_menu_show,
861 combo_box);
862 g_signal_handlers_disconnect_by_func (menu,
863 gtk_combo_box_menu_hide,
864 combo_box);
865
866 combo_box->priv->popup_widget = NULL;
867 }
868
869 static void
870 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
871 GtkWidget *popup)
872 {
873 if (GTK_IS_MENU (combo_box->priv->popup_widget))
874 {
875 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
876 combo_box->priv->popup_widget = NULL;
877 }
878 else if (combo_box->priv->popup_widget)
879 {
880 gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame),
881 combo_box->priv->popup_widget);
882 g_object_unref (G_OBJECT (combo_box->priv->popup_widget));
883 combo_box->priv->popup_widget = NULL;
884 }
885
886 if (GTK_IS_MENU (popup))
887 {
888 if (combo_box->priv->popup_window)
889 {
890 gtk_widget_destroy (combo_box->priv->popup_window);
891 combo_box->priv->popup_window = NULL;
892 combo_box->priv->popup_frame = NULL;
893 }
894
895 combo_box->priv->popup_widget = popup;
896
897 g_signal_connect (popup, "show",
898 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
899 g_signal_connect (popup, "hide",
900 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
901
902 gtk_menu_attach_to_widget (GTK_MENU (popup),
903 GTK_WIDGET (combo_box),
904 gtk_combo_box_detacher);
905 }
906 else
907 {
908 if (!combo_box->priv->popup_window)
909 {
910 combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
911 gtk_window_set_resizable (GTK_WINDOW (combo_box->priv->popup_window), FALSE);
912 gtk_window_set_screen (GTK_WINDOW (combo_box->priv->popup_window),
913 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
914
915 combo_box->priv->popup_frame = gtk_frame_new (NULL);
916 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame),
917 GTK_SHADOW_ETCHED_IN);
918 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window),
919 combo_box->priv->popup_frame);
920
921 gtk_widget_show (combo_box->priv->popup_frame);
922 }
923
924 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
925 popup);
926 gtk_widget_show (popup);
927 g_object_ref (G_OBJECT (popup));
928 combo_box->priv->popup_widget = popup;
929 }
930 }
931
932 static void
933 gtk_combo_box_menu_position_below (GtkMenu *menu,
934 gint *x,
935 gint *y,
936 gint *push_in,
937 gpointer user_data)
938 {
939 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
940 gint sx, sy;
941 GtkWidget *child;
942 GtkRequisition req;
943 GdkScreen *screen;
944 gint monitor_num;
945 GdkRectangle monitor;
946
947 /* FIXME: is using the size request here broken? */
948 child = GTK_BIN (combo_box)->child;
949
950 gdk_window_get_origin (child->window, &sx, &sy);
951
952 if (GTK_WIDGET_NO_WINDOW (child))
953 {
954 sx += child->allocation.x;
955 sy += child->allocation.y;
956 }
957
958 gtk_widget_size_request (GTK_WIDGET (menu), &req);
959
960 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
961 *x = sx;
962 else
963 *x = sx + child->allocation.width - req.width;
964 *y = sy;
965
966 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
967 monitor_num = gdk_screen_get_monitor_at_window (screen,
968 GTK_WIDGET (combo_box)->window);
969 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
970
971 if (*x < monitor.x)
972 *x = monitor.x;
973 else if (*x + req.width > monitor.x + monitor.width)
974 *x = monitor.x + monitor.width - req.width;
975
976 if (monitor.y + monitor.height - *y - child->allocation.height >= req.height)
977 *y += child->allocation.height;
978 else if (*y - monitor.y >= req.height)
979 *y -= req.height;
980 else if (monitor.y + monitor.height - *y - child->allocation.height > *y - monitor.y)
981 *y += child->allocation.height;
982 else
983 *y -= req.height;
984
985 *push_in = FALSE;
986 }
987
988 static void
989 gtk_combo_box_menu_position_over (GtkMenu *menu,
990 gint *x,
991 gint *y,
992 gboolean *push_in,
993 gpointer user_data)
994 {
995 GtkComboBox *combo_box;
996 GtkWidget *active;
997 GtkWidget *child;
998 GtkWidget *widget;
999 GtkRequisition requisition;
1000 GList *children;
1001 gint screen_width;
1002 gint menu_xpos;
1003 gint menu_ypos;
1004 gint menu_width;
1005
1006 g_return_if_fail (GTK_IS_COMBO_BOX (user_data));
1007
1008 combo_box = GTK_COMBO_BOX (user_data);
1009 widget = GTK_WIDGET (combo_box);
1010
1011 gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
1012 menu_width = requisition.width;
1013
1014 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1015 gdk_window_get_origin (widget->window, &menu_xpos, &menu_ypos);
1016
1017 menu_xpos += widget->allocation.x;
1018 menu_ypos += widget->allocation.y + widget->allocation.height / 2 - 2;
1019
1020 if (active != NULL)
1021 {
1022 gtk_widget_get_child_requisition (active, &requisition);
1023 menu_ypos -= requisition.height / 2;
1024 }
1025
1026 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1027 while (children)
1028 {
1029 child = children->data;
1030
1031 if (active == child)
1032 break;
1033
1034 if (GTK_WIDGET_VISIBLE (child))
1035 {
1036 gtk_widget_get_child_requisition (child, &requisition);
1037 menu_ypos -= requisition.height;
1038 }
1039
1040 children = children->next;
1041 }
1042
1043 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1044 menu_xpos = menu_xpos + widget->allocation.width - menu_width;
1045
1046 /* Clamp the position on screen */
1047 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1048
1049 if (menu_xpos < 0)
1050 menu_xpos = 0;
1051 else if ((menu_xpos + menu_width) > screen_width)
1052 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1053
1054 *x = menu_xpos;
1055 *y = menu_ypos;
1056
1057 *push_in = TRUE;
1058 }
1059
1060 static void
1061 gtk_combo_box_menu_position (GtkMenu *menu,
1062 gint *x,
1063 gint *y,
1064 gint *push_in,
1065 gpointer user_data)
1066 {
1067 GtkComboBox *combo_box;
1068 GtkWidget *menu_item;
1069
1070 combo_box = GTK_COMBO_BOX (user_data);
1071
1072 if (combo_box->priv->wrap_width > 0 || combo_box->priv->cell_view == NULL)
1073 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1074 else
1075 {
1076 menu_item = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1077 if (menu_item)
1078 gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget),
1079 menu_item);
1080
1081 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1082 }
1083
1084 }
1085
1086 static void
1087 gtk_combo_box_list_position (GtkComboBox *combo_box,
1088 gint *x,
1089 gint *y,
1090 gint *width,
1091 gint *height)
1092 {
1093 GtkWidget *sample;
1094 GdkScreen *screen;
1095 gint monitor_num;
1096 GdkRectangle monitor;
1097 GtkRequisition popup_req;
1098
1099 sample = GTK_BIN (combo_box)->child;
1100
1101 *width = sample->allocation.width;
1102 gtk_widget_size_request (combo_box->priv->popup_window, &popup_req);
1103 *height = popup_req.height;
1104
1105 gdk_window_get_origin (sample->window, x, y);
1106
1107 if (combo_box->priv->cell_view_frame)
1108 {
1109 *x -= GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1110 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1111 *width += 2 * (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1112 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1113 }
1114
1115 if (GTK_WIDGET_NO_WINDOW (sample))
1116 {
1117 *x += sample->allocation.x;
1118 *y += sample->allocation.y;
1119 }
1120
1121 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1122 monitor_num = gdk_screen_get_monitor_at_window (screen,
1123 GTK_WIDGET (combo_box)->window);
1124 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1125
1126 if (*x < monitor.x)
1127 *x = monitor.x;
1128 else if (*x + *width > monitor.x + monitor.width)
1129 *x = monitor.x + monitor.width - *width;
1130
1131 if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
1132 *y += sample->allocation.height;
1133 else
1134 *y -= *height;
1135 }
1136
1137 /**
1138 * gtk_combo_box_popup:
1139 * @combo_box: a #GtkComboBox
1140 *
1141 * Pops up the menu or dropdown list of @combo_box.
1142 *
1143 * This function is mostly intended for use by accessibility technologies;
1144 * applications should have little use for it.
1145 *
1146 * Since: 2.4
1147 **/
1148 void
1149 gtk_combo_box_popup (GtkComboBox *combo_box)
1150 {
1151 gint x, y, width, height;
1152
1153 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1154
1155 if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
1156 return;
1157
1158 if (GTK_IS_MENU (combo_box->priv->popup_widget))
1159 {
1160 gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget),
1161 combo_box->priv->active_item);
1162
1163 if (combo_box->priv->wrap_width == 0)
1164 {
1165 GtkRequisition requisition;
1166
1167 width = GTK_WIDGET (combo_box)->allocation.width;
1168 gtk_widget_size_request (combo_box->priv->popup_widget, &requisition);
1169
1170 gtk_widget_set_size_request (combo_box->priv->popup_widget,
1171 MAX (width, requisition.width), -1);
1172 }
1173
1174 gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
1175 NULL, NULL,
1176 gtk_combo_box_menu_position, combo_box,
1177 0, 0);
1178 return;
1179 }
1180
1181 gtk_widget_show_all (combo_box->priv->popup_frame);
1182 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
1183
1184 gtk_widget_set_size_request (combo_box->priv->popup_window, width, -1);
1185 gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
1186
1187 /* popup */
1188 gtk_widget_show (combo_box->priv->popup_window);
1189
1190 gtk_widget_grab_focus (combo_box->priv->popup_window);
1191 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1192 TRUE);
1193
1194 if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view))
1195 {
1196 gdk_keyboard_grab (combo_box->priv->popup_window->window,
1197 FALSE, GDK_CURRENT_TIME);
1198 gtk_widget_grab_focus (combo_box->priv->tree_view);
1199 }
1200
1201 gtk_grab_add (combo_box->priv->popup_window);
1202 gdk_pointer_grab (combo_box->priv->popup_window->window, TRUE,
1203 GDK_BUTTON_PRESS_MASK |
1204 GDK_BUTTON_RELEASE_MASK |
1205 GDK_POINTER_MOTION_MASK,
1206 NULL, NULL, GDK_CURRENT_TIME);
1207
1208 gtk_grab_add (combo_box->priv->tree_view);
1209 }
1210
1211 /**
1212 * gtk_combo_box_popdown:
1213 * @combo_box: a #GtkComboBox
1214 *
1215 * Hides the menu or dropdown list of @combo_box.
1216 *
1217 * This function is mostly intended for use by accessibility technologies;
1218 * applications should have little use for it.
1219 *
1220 * Since: 2.4
1221 **/
1222 void
1223 gtk_combo_box_popdown (GtkComboBox *combo_box)
1224 {
1225 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1226
1227 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (combo_box)))
1228 return;
1229
1230 if (GTK_IS_MENU (combo_box->priv->popup_widget))
1231 {
1232 gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget));
1233 return;
1234 }
1235
1236 gtk_combo_box_list_remove_grabs (combo_box);
1237 gtk_widget_hide_all (combo_box->priv->popup_window);
1238 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1239 FALSE);
1240 }
1241
1242 static gint
1243 gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
1244 GtkTreePath *path)
1245 {
1246 gint padding;
1247 GtkRequisition req;
1248
1249 if (combo_box->priv->cell_view)
1250 gtk_widget_style_get (combo_box->priv->cell_view,
1251 "focus-line-width", &padding,
1252 NULL);
1253 else
1254 padding = 0;
1255
1256 /* add some pixels for good measure */
1257 padding += BONUS_PADDING;
1258
1259 if (combo_box->priv->cell_view)
1260 gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
1261 path, &req);
1262 else
1263 req.width = 0;
1264
1265 return req.width + padding;
1266 }
1267
1268 static void
1269 gtk_combo_box_remeasure (GtkComboBox *combo_box)
1270 {
1271 GtkTreeIter iter;
1272 GtkTreePath *path;
1273 gint padding = 0;
1274
1275 if (!combo_box->priv->model ||
1276 !gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
1277 return;
1278
1279 combo_box->priv->width = 0;
1280
1281 path = gtk_tree_path_new_from_indices (0, -1);
1282
1283 if (combo_box->priv->cell_view)
1284 gtk_widget_style_get (combo_box->priv->cell_view,
1285 "focus-line-width", &padding,
1286 NULL);
1287 else
1288 padding = 0;
1289
1290 /* add some pixels for good measure */
1291 padding += BONUS_PADDING;
1292
1293 do
1294 {
1295 GtkRequisition req;
1296
1297 if (combo_box->priv->cell_view)
1298 gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
1299 path, &req);
1300 else
1301 req.width = 0;
1302
1303 combo_box->priv->width = MAX (combo_box->priv->width,
1304 req.width + padding);
1305
1306 gtk_tree_path_next (path);
1307 }
1308 while (gtk_tree_model_iter_next (combo_box->priv->model, &iter));
1309
1310 gtk_tree_path_free (path);
1311 }
1312
1313 static void
1314 gtk_combo_box_size_request (GtkWidget *widget,
1315 GtkRequisition *requisition)
1316 {
1317 gint width, height;
1318 GtkRequisition bin_req;
1319
1320 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1321
1322 /* common */
1323 gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
1324 gtk_combo_box_remeasure (combo_box);
1325 bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1326
1327 gtk_combo_box_check_appearance (combo_box);
1328
1329 if (!combo_box->priv->tree_view)
1330 {
1331 /* menu mode */
1332
1333 if (combo_box->priv->cell_view)
1334 {
1335 GtkRequisition button_req, sep_req, arrow_req;
1336 gint border_width, xthickness, ythickness;
1337
1338 gtk_widget_size_request (combo_box->priv->button, &button_req);
1339 border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
1340 xthickness = combo_box->priv->button->style->xthickness;
1341 ythickness = combo_box->priv->button->style->ythickness;
1342
1343 bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1344
1345 gtk_widget_size_request (combo_box->priv->separator, &sep_req);
1346 gtk_widget_size_request (combo_box->priv->arrow, &arrow_req);
1347
1348 height = MAX (sep_req.height, arrow_req.height);
1349 height = MAX (height, bin_req.height);
1350
1351 width = bin_req.width + sep_req.width + arrow_req.width;
1352
1353 height += border_width + 1 + ythickness * 2 + 4;
1354 width += border_width + 1 + xthickness * 2 + 4;
1355
1356 requisition->width = width;
1357 requisition->height = height;
1358 }
1359 else
1360 {
1361 GtkRequisition but_req;
1362
1363 gtk_widget_size_request (combo_box->priv->button, &but_req);
1364
1365 requisition->width = bin_req.width + but_req.width;
1366 requisition->height = MAX (bin_req.height, but_req.height);
1367 }
1368 }
1369 else
1370 {
1371 /* list mode */
1372 GtkRequisition button_req, frame_req;
1373
1374 /* sample + frame */
1375 *requisition = bin_req;
1376
1377 if (combo_box->priv->cell_view_frame)
1378 {
1379 gtk_widget_size_request (combo_box->priv->cell_view_frame, &frame_req);
1380 requisition->width += 2 *
1381 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1382 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1383 requisition->height += 2 *
1384 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1385 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
1386 }
1387
1388 /* the button */
1389 gtk_widget_size_request (combo_box->priv->button, &button_req);
1390
1391 requisition->height = MAX (requisition->height, button_req.height);
1392 requisition->width += button_req.width;
1393 }
1394 }
1395
1396 static void
1397 gtk_combo_box_size_allocate (GtkWidget *widget,
1398 GtkAllocation *allocation)
1399 {
1400 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1401 GtkAllocation child;
1402 GtkRequisition req;
1403 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1404
1405 widget->allocation = *allocation;
1406
1407 gtk_combo_box_check_appearance (combo_box);
1408
1409 if (!combo_box->priv->tree_view)
1410 {
1411 if (combo_box->priv->cell_view)
1412 {
1413 gint border_width, xthickness, ythickness;
1414 gint width;
1415
1416 /* menu mode */
1417 gtk_widget_size_allocate (combo_box->priv->button, allocation);
1418
1419 /* set some things ready */
1420 border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
1421 xthickness = combo_box->priv->button->style->xthickness;
1422 ythickness = combo_box->priv->button->style->ythickness;
1423
1424 child.x = allocation->x + border_width + 1 + xthickness + 2;
1425 child.y = allocation->y + border_width + 1 + ythickness + 2;
1426
1427 width = MAX(1, allocation->width - (border_width + 1 + xthickness * 2 + 4));
1428
1429 /* handle the children */
1430 gtk_widget_size_request (combo_box->priv->arrow, &req);
1431 child.width = req.width;
1432 child.height = MAX(1, allocation->height - 2 * (child.y - allocation->y));
1433 if (!is_rtl)
1434 child.x += width - req.width;
1435 gtk_widget_size_allocate (combo_box->priv->arrow, &child);
1436 if (is_rtl)
1437 child.x += req.width;
1438 gtk_widget_size_request (combo_box->priv->separator, &req);
1439 child.width = req.width;
1440 if (!is_rtl)
1441 child.x -= req.width;
1442 gtk_widget_size_allocate (combo_box->priv->separator, &child);
1443
1444 if (is_rtl)
1445 {
1446 child.x += req.width;
1447 child.width = MAX(1, allocation->x + allocation->width
1448 - (border_width + 1 + xthickness + 2) - child.x);
1449 }
1450 else
1451 {
1452 child.width = child.x;
1453 child.x = allocation->x + border_width + 1 + xthickness + 2;
1454 child.width = MAX(1, child.width - child.x);
1455 }
1456
1457 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1458 }
1459 else
1460 {
1461 gtk_widget_size_request (combo_box->priv->button, &req);
1462 if (is_rtl)
1463 child.x = allocation->x;
1464 else
1465 child.x = allocation->x + allocation->width - req.width;
1466 child.y = allocation->y;
1467 child.width = req.width;
1468 child.height = allocation->height;
1469 gtk_widget_size_allocate (combo_box->priv->button, &child);
1470
1471 if (is_rtl)
1472 child.x = allocation->x + req.width;
1473 else
1474 child.x = allocation->x;
1475 child.y = allocation->y;
1476 child.width = MAX(1, allocation->width - req.width);
1477 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1478 }
1479 }
1480 else
1481 {
1482 /* list mode */
1483
1484 /* button */
1485 gtk_widget_size_request (combo_box->priv->button, &req);
1486 if (is_rtl)
1487 child.x = allocation->x;
1488 else
1489 child.x = allocation->x + allocation->width - req.width;
1490 child.y = allocation->y;
1491 child.width = req.width;
1492 child.height = allocation->height;
1493 gtk_widget_size_allocate (combo_box->priv->button, &child);
1494
1495 /* frame */
1496 if (is_rtl)
1497 child.x = allocation->x + req.width;
1498 else
1499 child.x = allocation->x;
1500 child.y = allocation->y;
1501 child.width = MAX (1, allocation->width - req.width);
1502 child.height = allocation->height;
1503
1504 if (combo_box->priv->cell_view_frame)
1505 {
1506 gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
1507
1508 /* the sample */
1509 child.x +=
1510 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1511 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1512 child.y +=
1513 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1514 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
1515 child.width -= 2 * (
1516 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1517 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1518 child.width = MAX(1,child.width);
1519 child.height -= 2 * (
1520 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1521 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
1522 child.height = MAX(1,child.height);
1523 }
1524
1525 gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
1526 }
1527 }
1528
1529 static void
1530 gtk_combo_box_unset_model (GtkComboBox *combo_box)
1531 {
1532 if (combo_box->priv->model)
1533 {
1534 g_signal_handler_disconnect (combo_box->priv->model,
1535 combo_box->priv->inserted_id);
1536 g_signal_handler_disconnect (combo_box->priv->model,
1537 combo_box->priv->deleted_id);
1538 g_signal_handler_disconnect (combo_box->priv->model,
1539 combo_box->priv->reordered_id);
1540 g_signal_handler_disconnect (combo_box->priv->model,
1541 combo_box->priv->changed_id);
1542 }
1543
1544 /* menu mode */
1545 if (!combo_box->priv->tree_view)
1546 {
1547 if (combo_box->priv->popup_widget)
1548 gtk_container_foreach (GTK_CONTAINER (combo_box->priv->popup_widget),
1549 (GtkCallback)gtk_widget_destroy, NULL);
1550 }
1551
1552 if (combo_box->priv->model)
1553 {
1554 g_object_unref (G_OBJECT (combo_box->priv->model));
1555 combo_box->priv->model = NULL;
1556 }
1557
1558 if (combo_box->priv->cell_view)
1559 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
1560 }
1561
1562 static void
1563 gtk_combo_box_forall (GtkContainer *container,
1564 gboolean include_internals,
1565 GtkCallback callback,
1566 gpointer callback_data)
1567 {
1568 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1569
1570 if (include_internals)
1571 {
1572 if (combo_box->priv->button)
1573 (* callback) (combo_box->priv->button, callback_data);
1574 if (combo_box->priv->cell_view_frame)
1575 (* callback) (combo_box->priv->cell_view_frame, callback_data);
1576 }
1577
1578 if (GTK_BIN (container)->child)
1579 (* callback) (GTK_BIN (container)->child, callback_data);
1580 }
1581
1582 static gboolean
1583 gtk_combo_box_expose_event (GtkWidget *widget,
1584 GdkEventExpose *event)
1585 {
1586 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1587
1588 if (!combo_box->priv->tree_view)
1589 {
1590 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1591 combo_box->priv->button, event);
1592 }
1593 else
1594 {
1595 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1596 combo_box->priv->button, event);
1597
1598 if (combo_box->priv->cell_view_frame)
1599 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1600 combo_box->priv->cell_view_frame, event);
1601 }
1602
1603 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1604 GTK_BIN (widget)->child, event);
1605
1606 return FALSE;
1607 }
1608
1609 static gboolean
1610 gtk_combo_box_scroll_event (GtkWidget *widget,
1611 GdkEventScroll *event)
1612 {
1613 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1614 gint index;
1615 gint items;
1616
1617 index = gtk_combo_box_get_active (combo_box);
1618
1619 if (index != -1)
1620 {
1621 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1622
1623 if (event->direction == GDK_SCROLL_UP)
1624 index--;
1625 else
1626 index++;
1627
1628 gtk_combo_box_set_active (combo_box, CLAMP (index, 0, items - 1));
1629 }
1630
1631 return TRUE;
1632 }
1633
1634 /*
1635 * menu style
1636 */
1637
1638 static void
1639 cell_view_sync_cells (GtkComboBox *combo_box,
1640 GtkCellView *cell_view)
1641 {
1642 GSList *k;
1643
1644 for (k = combo_box->priv->cells; k; k = k->next)
1645 {
1646 GSList *j;
1647 ComboCellInfo *info = (ComboCellInfo *)k->data;
1648
1649 if (info->pack == GTK_PACK_START)
1650 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view),
1651 info->cell, info->expand);
1652 else if (info->pack == GTK_PACK_END)
1653 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (cell_view),
1654 info->cell, info->expand);
1655
1656 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
1657 info->cell,
1658 info->func, info->func_data, NULL);
1659
1660 for (j = info->attributes; j; j = j->next->next)
1661 {
1662 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cell_view),
1663 info->cell,
1664 j->data,
1665 GPOINTER_TO_INT (j->next->data));
1666 }
1667 }
1668 }
1669
1670 static void
1671 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
1672 gboolean add_children)
1673 {
1674 GtkWidget *menu;
1675
1676 if (combo_box->priv->cell_view)
1677 {
1678 combo_box->priv->button = gtk_toggle_button_new ();
1679 g_signal_connect (combo_box->priv->button, "toggled",
1680 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1681 g_signal_connect_after (combo_box->priv->button, "key_press_event",
1682 G_CALLBACK (gtk_combo_box_key_press), combo_box);
1683 gtk_widget_set_parent (combo_box->priv->button,
1684 GTK_BIN (combo_box)->child->parent);
1685
1686 combo_box->priv->box = gtk_hbox_new (FALSE, 0);
1687 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
1688 combo_box->priv->box);
1689
1690 combo_box->priv->separator = gtk_vseparator_new ();
1691 gtk_container_add (GTK_CONTAINER (combo_box->priv->box),
1692 combo_box->priv->separator);
1693
1694 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1695 gtk_container_add (GTK_CONTAINER (combo_box->priv->box),
1696 combo_box->priv->arrow);
1697
1698 gtk_widget_show_all (combo_box->priv->button);
1699 }
1700 else
1701 {
1702 combo_box->priv->button = gtk_toggle_button_new ();
1703 g_signal_connect (combo_box->priv->button, "toggled",
1704 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1705 g_signal_connect_after (combo_box, "key_press_event",
1706 G_CALLBACK (gtk_combo_box_key_press), combo_box);
1707 gtk_widget_set_parent (combo_box->priv->button,
1708 GTK_BIN (combo_box)->child->parent);
1709
1710 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1711 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
1712 combo_box->priv->arrow);
1713 gtk_widget_show_all (combo_box->priv->button);
1714 }
1715
1716 g_signal_connect (combo_box->priv->button, "button_press_event",
1717 G_CALLBACK (gtk_combo_box_menu_button_press),
1718 combo_box);
1719
1720 /* create our funky menu */
1721 menu = gtk_menu_new ();
1722 g_signal_connect (menu, "key_press_event",
1723 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
1724 gtk_combo_box_set_popup_widget (combo_box, menu);
1725
1726 /* add items */
1727 if (add_children)
1728 gtk_combo_box_menu_fill (combo_box);
1729
1730 }
1731
1732 static void
1733 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
1734 {
1735 gint i, items;
1736 GtkWidget *menu;
1737 GtkWidget *tmp;
1738
1739 if (!combo_box->priv->model)
1740 return;
1741
1742 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1743 menu = combo_box->priv->popup_widget;
1744
1745 for (i = 0; i < items; i++)
1746 {
1747 GtkTreePath *path;
1748
1749 path = gtk_tree_path_new_from_indices (i, -1);
1750 tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
1751 path);
1752 g_signal_connect (tmp, "activate",
1753 G_CALLBACK (gtk_combo_box_menu_item_activate),
1754 combo_box);
1755
1756 cell_view_sync_cells (combo_box,
1757 GTK_CELL_VIEW (GTK_BIN (tmp)->child));
1758
1759 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tmp);
1760
1761 if (combo_box->priv->wrap_width)
1762 gtk_combo_box_relayout_item (combo_box, i);
1763
1764 gtk_widget_show (tmp);
1765
1766 gtk_tree_path_free (path);
1767 }
1768 }
1769
1770 static void
1771 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
1772 {
1773 g_signal_handlers_disconnect_matched (combo_box->priv->button,
1774 G_SIGNAL_MATCH_DATA,
1775 0, 0, NULL,
1776 gtk_combo_box_menu_button_press, NULL);
1777
1778 /* unparent will remove our latest ref */
1779 gtk_widget_unparent (combo_box->priv->button);
1780
1781 combo_box->priv->box = NULL;
1782 combo_box->priv->button = NULL;
1783 combo_box->priv->arrow = NULL;
1784 combo_box->priv->separator = NULL;
1785
1786 /* changing the popup window will unref the menu and the children */
1787 }
1788
1789 /*
1790 * grid
1791 */
1792
1793 static void
1794 gtk_combo_box_item_get_size (GtkComboBox *combo_box,
1795 gint index_,
1796 gint *cols,
1797 gint *rows)
1798 {
1799 GtkTreeIter iter;
1800
1801 gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter, NULL, index_);
1802
1803 if (cols)
1804 {
1805 if (combo_box->priv->col_column == -1)
1806 *cols = 1;
1807 else
1808 gtk_tree_model_get (combo_box->priv->model, &iter,
1809 combo_box->priv->col_column, cols,
1810 -1);
1811 }
1812
1813 if (rows)
1814 {
1815 if (combo_box->priv->row_column == -1)
1816 *rows = 1;
1817 else
1818 gtk_tree_model_get (combo_box->priv->model, &iter,
1819 combo_box->priv->row_column, rows,
1820 -1);
1821 }
1822 }
1823
1824 static gboolean
1825 menu_occupied (GtkMenu *menu,
1826 guint left_attach,
1827 guint right_attach,
1828 guint top_attach,
1829 guint bottom_attach)
1830 {
1831 GList *i;
1832
1833 g_return_val_if_fail (GTK_IS_MENU (menu), TRUE);
1834 g_return_val_if_fail (left_attach < right_attach, TRUE);
1835 g_return_val_if_fail (top_attach < bottom_attach, TRUE);
1836
1837 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
1838 {
1839 guint l, r, b, t;
1840 gboolean h_intersect = FALSE;
1841 gboolean v_intersect = FALSE;
1842
1843 gtk_container_child_get (GTK_CONTAINER (menu), i->data,
1844 "left_attach", &l,
1845 "right_attach", &r,
1846 "bottom_attach", &b,
1847 "top_attach", &t,
1848 NULL);
1849
1850 /* look if this item intersects with the given coordinates */
1851 h_intersect = left_attach <= l && l <= right_attach;
1852 h_intersect &= left_attach <= r && r <= right_attach;
1853
1854 v_intersect = top_attach <= t && t <= bottom_attach;
1855 v_intersect &= top_attach <= b && b <= bottom_attach;
1856
1857 if (h_intersect && v_intersect)
1858 return TRUE;
1859 }
1860
1861 return FALSE;
1862 }
1863
1864 static void
1865 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
1866 gint index)
1867 {
1868 gint current_col = 0, current_row = 0;
1869 gint rows, cols;
1870 GList *list, *nth;
1871 GtkWidget *item, *last;
1872 GtkWidget *menu;
1873
1874 menu = combo_box->priv->popup_widget;
1875 if (!GTK_IS_MENU_SHELL (menu))
1876 return;
1877
1878 list = gtk_container_get_children (GTK_CONTAINER (menu));
1879 nth = g_list_nth (list, index);
1880 item = nth->data;
1881 if (nth->prev)
1882 last = nth->prev->data;
1883 else
1884 last = NULL;
1885 g_list_free (list);
1886
1887 gtk_combo_box_item_get_size (combo_box, index, &cols, &rows);
1888
1889 if (combo_box->priv->col_column == -1 &&
1890 combo_box->priv->row_column == -1 &&
1891 last)
1892 {
1893 gtk_container_child_get (GTK_CONTAINER (menu),
1894 last,
1895 "right_attach", &current_col,
1896 "top_attach", &current_row,
1897 NULL);
1898 if (current_col + cols > combo_box->priv->wrap_width)
1899 {
1900 current_col = 0;
1901 current_row++;
1902 }
1903 }
1904 else
1905 {
1906 /* look for a good spot */
1907 while (1)
1908 {
1909 if (current_col + cols > combo_box->priv->wrap_width)
1910 {
1911 current_col = 0;
1912 current_row++;
1913 }
1914
1915 if (!menu_occupied (GTK_MENU (menu),
1916 current_col, current_col + cols,
1917 current_row, current_row + rows))
1918 break;
1919
1920 current_col++;
1921 }
1922 }
1923
1924 /* set attach props */
1925 gtk_menu_attach (GTK_MENU (menu), item,
1926 current_col, current_col + cols,
1927 current_row, current_row + rows);
1928 }
1929
1930 static void
1931 gtk_combo_box_relayout (GtkComboBox *combo_box)
1932 {
1933 GList *list, *j;
1934 GtkWidget *menu;
1935
1936 menu = combo_box->priv->popup_widget;
1937
1938 /* do nothing unless we are in menu style and realized */
1939 if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
1940 return;
1941
1942 /* get rid of all children */
1943 list = gtk_container_get_children (GTK_CONTAINER (menu));
1944
1945 for (j = g_list_last (list); j; j = j->prev)
1946 gtk_container_remove (GTK_CONTAINER (menu), j->data);
1947
1948 g_list_free (list);
1949
1950 /* and relayout */
1951 gtk_combo_box_menu_fill (combo_box);
1952 }
1953
1954 /* callbacks */
1955 static gboolean
1956 gtk_combo_box_menu_button_press (GtkWidget *widget,
1957 GdkEventButton *event,
1958 gpointer user_data)
1959 {
1960 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1961
1962 if (! GTK_IS_MENU (combo_box->priv->popup_widget))
1963 return FALSE;
1964
1965 if (event->type == GDK_BUTTON_PRESS && event->button == 1)
1966 {
1967 combo_box->priv->popup_in_progress = TRUE;
1968
1969 gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget),
1970 combo_box->priv->active_item);
1971
1972 if (combo_box->priv->wrap_width == 0)
1973 {
1974 GtkRequisition requisition;
1975 gint width;
1976
1977 width = GTK_WIDGET (combo_box)->allocation.width;
1978 gtk_widget_size_request (combo_box->priv->popup_widget, &requisition);
1979
1980 gtk_widget_set_size_request (combo_box->priv->popup_widget,
1981 MAX (width, requisition.width), -1);
1982 }
1983
1984 gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
1985 NULL, NULL,
1986 gtk_combo_box_menu_position, combo_box,
1987 event->button, event->time);
1988
1989 return TRUE;
1990 }
1991
1992 return FALSE;
1993 }
1994
1995 static void
1996 gtk_combo_box_menu_item_activate (GtkWidget *item,
1997 gpointer user_data)
1998 {
1999 gint index;
2000 GtkWidget *menu;
2001 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2002
2003 menu = combo_box->priv->popup_widget;
2004 g_return_if_fail (GTK_IS_MENU (menu));
2005
2006 index = g_list_index (GTK_MENU_SHELL (menu)->children, item);
2007
2008 gtk_combo_box_set_active (combo_box, index);
2009 }
2010
2011 static void
2012 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
2013 GtkTreePath *path,
2014 GtkTreeIter *iter,
2015 gpointer user_data)
2016 {
2017 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2018 gint index = gtk_tree_path_get_indices (path)[0];
2019
2020 if (combo_box->priv->active_item >= index)
2021 combo_box->priv->active_item++;
2022
2023 if (!combo_box->priv->tree_view)
2024 gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
2025 }
2026
2027 static void
2028 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
2029 GtkTreePath *path,
2030 gpointer user_data)
2031 {
2032 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2033 gint index = gtk_tree_path_get_indices (path)[0];
2034
2035 if (!combo_box->priv->tree_view)
2036 gtk_combo_box_menu_row_deleted (model, path, user_data);
2037
2038 if (index == combo_box->priv->active_item)
2039 {
2040 gint items = gtk_tree_model_iter_n_children (model, NULL);
2041
2042 if (items == 0)
2043 gtk_combo_box_set_active_internal (combo_box, -1);
2044 else if (index == items)
2045 gtk_combo_box_set_active_internal (combo_box, index - 1);
2046 else
2047 gtk_combo_box_set_active_internal (combo_box, index);
2048 }
2049 else if (combo_box->priv->active_item > index)
2050 combo_box->priv->active_item--;
2051 }
2052
2053 static void
2054 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
2055 GtkTreePath *path,
2056 GtkTreeIter *iter,
2057 gint *new_order,
2058 gpointer user_data)
2059 {
2060 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2061 gint items = gtk_tree_model_iter_n_children (model, NULL);
2062 gint i;
2063
2064 for (i = 0; i < items; i++)
2065 if (new_order[i] == combo_box->priv->active_item)
2066 {
2067 combo_box->priv->active_item = i;
2068 break;
2069 }
2070
2071 if (!combo_box->priv->tree_view)
2072 gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
2073 }
2074
2075 static void
2076 gtk_combo_box_model_row_changed (GtkTreeModel *model,
2077 GtkTreePath *path,
2078 GtkTreeIter *iter,
2079 gpointer user_data)
2080 {
2081 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2082 gint index = gtk_tree_path_get_indices (path)[0];
2083
2084 if (index == combo_box->priv->active_item &&
2085 combo_box->priv->cell_view)
2086 gtk_widget_queue_resize (GTK_WIDGET (combo_box->priv->cell_view));
2087
2088 if (combo_box->priv->tree_view)
2089 gtk_combo_box_list_row_changed (model, path, iter, user_data);
2090 else
2091 gtk_combo_box_menu_row_changed (model, path, iter, user_data);
2092 }
2093
2094
2095 static void
2096 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
2097 GtkTreePath *path,
2098 GtkTreeIter *iter,
2099 gpointer user_data)
2100 {
2101 GtkWidget *menu;
2102 GtkWidget *item;
2103 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2104
2105 if (!combo_box->priv->popup_widget)
2106 return;
2107
2108 menu = combo_box->priv->popup_widget;
2109 g_return_if_fail (GTK_IS_MENU (menu));
2110
2111 item = gtk_cell_view_menu_item_new_from_model (model, path);
2112 g_signal_connect (item, "activate",
2113 G_CALLBACK (gtk_combo_box_menu_item_activate),
2114 combo_box);
2115
2116 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (item)->child));
2117
2118 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item,
2119 gtk_tree_path_get_indices (path)[0]);
2120 gtk_widget_show (item);
2121 }
2122
2123 static void
2124 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
2125 GtkTreePath *path,
2126 gpointer user_data)
2127 {
2128 gint index;
2129 GtkWidget *menu;
2130 GtkWidget *item;
2131 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2132
2133 if (!combo_box->priv->popup_widget)
2134 return;
2135
2136 index = gtk_tree_path_get_indices (path)[0];
2137
2138 menu = combo_box->priv->popup_widget;
2139 g_return_if_fail (GTK_IS_MENU (menu));
2140
2141 item = g_list_nth_data (GTK_MENU_SHELL (menu)->children, index);
2142 g_return_if_fail (GTK_IS_MENU_ITEM (item));
2143
2144 gtk_container_remove (GTK_CONTAINER (menu), item);
2145 }
2146
2147 static void
2148 gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
2149 GtkTreePath *path,
2150 GtkTreeIter *iter,
2151 gint *new_order,
2152 gpointer user_data)
2153 {
2154 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2155
2156 gtk_combo_box_relayout (combo_box);
2157 }
2158
2159 static void
2160 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
2161 GtkTreePath *path,
2162 GtkTreeIter *iter,
2163 gpointer user_data)
2164 {
2165 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2166 gint width;
2167
2168 if (!combo_box->priv->popup_widget)
2169 return;
2170
2171 if (combo_box->priv->wrap_width)
2172 gtk_combo_box_relayout_item (combo_box,
2173 gtk_tree_path_get_indices (path)[0]);
2174
2175 width = gtk_combo_box_calc_requested_width (combo_box, path);
2176
2177 if (width > combo_box->priv->width)
2178 {
2179 if (combo_box->priv->cell_view)
2180 {
2181 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
2182 gtk_widget_queue_resize (combo_box->priv->cell_view);
2183 }
2184 combo_box->priv->width = width;
2185 }
2186 }
2187
2188 /*
2189 * list style
2190 */
2191
2192 static void
2193 gtk_combo_box_list_setup (GtkComboBox *combo_box)
2194 {
2195 GSList *i;
2196 GtkTreeSelection *sel;
2197
2198 combo_box->priv->button = gtk_toggle_button_new ();
2199 gtk_widget_set_parent (combo_box->priv->button,
2200 GTK_BIN (combo_box)->child->parent);
2201 g_signal_connect (combo_box->priv->button, "button_press_event",
2202 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
2203 g_signal_connect (combo_box->priv->button, "toggled",
2204 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2205 g_signal_connect_after (combo_box, "key_press_event",
2206 G_CALLBACK (gtk_combo_box_key_press), combo_box);
2207
2208 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2209 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
2210 combo_box->priv->arrow);
2211 combo_box->priv->separator = NULL;
2212 gtk_widget_show_all (combo_box->priv->button);
2213
2214 if (combo_box->priv->cell_view)
2215 {
2216 combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
2217 gtk_widget_set_parent (combo_box->priv->cell_view_frame,
2218 GTK_BIN (combo_box)->child->parent);
2219 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
2220 GTK_SHADOW_IN);
2221
2222 gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
2223 &GTK_WIDGET (combo_box)->style->base[GTK_WIDGET_STATE (combo_box)]);
2224
2225 combo_box->priv->box = gtk_event_box_new ();
2226 /*
2227 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->box),
2228 FALSE);
2229 */
2230
2231 gtk_container_add (GTK_CONTAINER (combo_box->priv->cell_view_frame),
2232 combo_box->priv->box);
2233
2234 gtk_widget_show_all (combo_box->priv->cell_view_frame);
2235
2236 g_signal_connect (combo_box->priv->box, "button_press_event",
2237 G_CALLBACK (gtk_combo_box_list_button_pressed),
2238 combo_box);
2239 }
2240
2241 combo_box->priv->tree_view = gtk_tree_view_new ();
2242 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
2243 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
2244 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view),
2245 FALSE);
2246 /*
2247 _gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (combo_box->priv->tree_view),
2248 TRUE);
2249 */
2250 if (combo_box->priv->model)
2251 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
2252 combo_box->priv->model);
2253
2254 g_signal_connect (combo_box->priv->tree_view, "button_press_event",
2255 G_CALLBACK (gtk_combo_box_list_button_pressed),
2256 combo_box);
2257 g_signal_connect (combo_box->priv->tree_view, "button_release_event",
2258 G_CALLBACK (gtk_combo_box_list_button_released),
2259 combo_box);
2260 g_signal_connect (combo_box->priv->tree_view, "key_press_event",
2261 G_CALLBACK (gtk_combo_box_list_key_press),
2262 combo_box);
2263
2264 combo_box->priv->column = gtk_tree_view_column_new ();
2265 gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
2266 combo_box->priv->column);
2267
2268 /* sync up */
2269 for (i = combo_box->priv->cells; i; i = i->next)
2270 {
2271 GSList *j;
2272 ComboCellInfo *info = (ComboCellInfo *)i->data;
2273
2274 if (info->pack == GTK_PACK_START)
2275 gtk_tree_view_column_pack_start (combo_box->priv->column,
2276 info->cell, info->expand);
2277 else if (info->pack == GTK_PACK_END)
2278 gtk_tree_view_column_pack_end (combo_box->priv->column,
2279 info->cell, info->expand);
2280
2281 for (j = info->attributes; j; j = j->next->next)
2282 {
2283 gtk_tree_view_column_add_attribute (combo_box->priv->column,
2284 info->cell,
2285 j->data,
2286 GPOINTER_TO_INT (j->next->data));
2287 }
2288 }
2289
2290 if (combo_box->priv->active_item != -1)
2291 {
2292 GtkTreePath *path;
2293
2294 path = gtk_tree_path_new_from_indices (combo_box->priv->active_item, -1);
2295 if (path)
2296 {
2297 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
2298 path, NULL, FALSE);
2299 gtk_tree_path_free (path);
2300 }
2301 }
2302
2303 /* set sample/popup widgets */
2304 gtk_combo_box_set_popup_widget (combo_box, combo_box->priv->tree_view);
2305
2306 gtk_widget_show (combo_box->priv->tree_view);
2307 }
2308
2309 static void
2310 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
2311 {
2312 /* disconnect signals */
2313 g_signal_handlers_disconnect_matched (combo_box->priv->tree_view,
2314 G_SIGNAL_MATCH_DATA,
2315 0, 0, NULL, NULL, combo_box);
2316 g_signal_handlers_disconnect_matched (combo_box->priv->button,
2317 G_SIGNAL_MATCH_DATA,
2318 0, 0, NULL,
2319 gtk_combo_box_list_button_pressed,
2320 NULL);
2321 if (combo_box->priv->box)
2322 g_signal_handlers_disconnect_matched (combo_box->priv->box,
2323 G_SIGNAL_MATCH_DATA,
2324 0, 0, NULL,
2325 gtk_combo_box_list_button_pressed,
2326 NULL);
2327
2328 /* destroy things (unparent will kill the latest ref from us)
2329 * last unref on button will destroy the arrow
2330 */
2331 gtk_widget_unparent (combo_box->priv->button);
2332 combo_box->priv->button = NULL;
2333 combo_box->priv->arrow = NULL;
2334
2335 if (combo_box->priv->cell_view)
2336 {
2337 g_object_set (G_OBJECT (combo_box->priv->cell_view),
2338 "background_set", FALSE,
2339 NULL);
2340 }
2341
2342 if (combo_box->priv->cell_view_frame)
2343 {
2344 gtk_widget_unparent (combo_box->priv->cell_view_frame);
2345 combo_box->priv->cell_view_frame = NULL;
2346 combo_box->priv->box = NULL;
2347 }
2348
2349 gtk_widget_destroy (combo_box->priv->tree_view);
2350
2351 combo_box->priv->tree_view = NULL;
2352 combo_box->priv->popup_widget = NULL;
2353 }
2354
2355 /* callbacks */
2356 static void
2357 gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box)
2358 {
2359 if (combo_box->priv->tree_view &&
2360 GTK_WIDGET_HAS_GRAB (combo_box->priv->tree_view))
2361 {
2362 gtk_grab_remove (combo_box->priv->tree_view);
2363 }
2364
2365 if (combo_box->priv->popup_window &&
2366 GTK_WIDGET_HAS_GRAB (combo_box->priv->popup_window))
2367 {
2368 gtk_grab_remove (combo_box->priv->popup_window);
2369 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
2370 gdk_pointer_ungrab (GDK_CURRENT_TIME);
2371 }
2372 }
2373
2374 static gboolean
2375 gtk_combo_box_list_button_pressed (GtkWidget *widget,
2376 GdkEventButton *event,
2377 gpointer data)
2378 {
2379 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2380
2381 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
2382
2383 if (ewidget == combo_box->priv->tree_view)
2384 return TRUE;
2385
2386 if ((ewidget != combo_box->priv->button && ewidget != combo_box->priv->box) ||
2387 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
2388 return FALSE;
2389
2390 gtk_combo_box_popup (combo_box);
2391
2392 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
2393 TRUE);
2394
2395 combo_box->priv->popup_in_progress = TRUE;
2396
2397 return TRUE;
2398 }
2399
2400 static gboolean
2401 gtk_combo_box_list_button_released (GtkWidget *widget,
2402 GdkEventButton *event,
2403 gpointer data)
2404 {
2405 gboolean ret;
2406 GtkTreePath *path = NULL;
2407
2408 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2409
2410 gboolean popup_in_progress = FALSE;
2411
2412 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
2413
2414 if (combo_box->priv->popup_in_progress)
2415 {
2416 popup_in_progress = TRUE;
2417 combo_box->priv->popup_in_progress = FALSE;
2418 }
2419
2420 if (ewidget != combo_box->priv->tree_view)
2421 {
2422 if (ewidget == combo_box->priv->button &&
2423 !popup_in_progress &&
2424 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
2425 {
2426 gtk_combo_box_popdown (combo_box);
2427 return TRUE;
2428 }
2429
2430 /* released outside treeview */
2431 if (ewidget != combo_box->priv->button)
2432 {
2433 gtk_combo_box_popdown (combo_box);
2434
2435 return TRUE;
2436 }
2437
2438 return FALSE;
2439 }
2440
2441 /* select something cool */
2442 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
2443 event->x, event->y,
2444 &path,
2445 NULL, NULL, NULL);
2446
2447 if (!ret)
2448 return TRUE; /* clicked outside window? */
2449
2450 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
2451 gtk_combo_box_popdown (combo_box);
2452
2453 gtk_tree_path_free (path);
2454
2455 return TRUE;
2456 }
2457
2458 static gboolean
2459 gtk_combo_box_key_press (GtkWidget *widget,
2460 GdkEventKey *event,
2461 gpointer data)
2462 {
2463 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2464 guint state = event->state & gtk_accelerator_get_default_mod_mask ();
2465 gint items = 0;
2466 gint index = gtk_combo_box_get_active (combo_box);
2467 gint new_index;
2468
2469 if (combo_box->priv->model)
2470 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
2471
2472 if ((event->keyval == GDK_Down || event->keyval == GDK_KP_Down) &&
2473 state == GDK_MOD1_MASK)
2474 {
2475 gtk_combo_box_popup (combo_box);
2476
2477 return TRUE;
2478 }
2479
2480 switch (event->keyval)
2481 {
2482 case GDK_Down:
2483 case GDK_KP_Down:
2484 new_index = index + 1;
2485 break;
2486 case GDK_Up:
2487 case GDK_KP_Up:
2488 new_index = index - 1;
2489 break;
2490 case GDK_Page_Up:
2491 case GDK_KP_Page_Up:
2492 case GDK_Home:
2493 case GDK_KP_Home:
2494 new_index = 0;
2495 break;
2496 case GDK_Page_Down:
2497 case GDK_KP_Page_Down:
2498 case GDK_End:
2499 case GDK_KP_End:
2500 new_index = items - 1;
2501 break;
2502 default:
2503 return FALSE;
2504 }
2505
2506 if (items > 0)
2507 gtk_combo_box_set_active (combo_box, CLAMP (new_index, 0, items - 1));
2508
2509 return TRUE;
2510 }
2511
2512 static gboolean
2513 gtk_combo_box_menu_key_press (GtkWidget *widget,
2514 GdkEventKey *event,
2515 gpointer data)
2516 {
2517 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2518 guint state = event->state & gtk_accelerator_get_default_mod_mask ();
2519
2520 if ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) &&
2521 state == GDK_MOD1_MASK)
2522 {
2523 gtk_combo_box_popdown (combo_box);
2524
2525 return TRUE;
2526 }
2527
2528 return FALSE;
2529 }
2530
2531 static gboolean
2532 gtk_combo_box_list_key_press (GtkWidget *widget,
2533 GdkEventKey *event,
2534 gpointer data)
2535 {
2536 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2537 guint state = event->state & gtk_accelerator_get_default_mod_mask ();
2538
2539 if (event->keyval == GDK_Escape ||
2540 ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) &&
2541 state == GDK_MOD1_MASK))
2542 {
2543 /* reset active item -- this is incredibly lame and ugly */
2544 gtk_combo_box_set_active (combo_box,
2545 gtk_combo_box_get_active (combo_box));
2546
2547 gtk_combo_box_popdown (combo_box);
2548
2549 return TRUE;
2550 }
2551
2552 if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter ||
2553 event->keyval == GDK_space || event->keyval == GDK_KP_Space)
2554 {
2555 gboolean ret = FALSE;
2556 GtkTreeIter iter;
2557 GtkTreeModel *model = NULL;
2558
2559 if (combo_box->priv->model)
2560 {
2561 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
2562
2563 ret = gtk_tree_selection_get_selected (sel, &model, &iter);
2564 }
2565 if (ret)
2566 {
2567 GtkTreePath *path;
2568
2569 path = gtk_tree_model_get_path (model, &iter);
2570 if (path)
2571 {
2572 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
2573 gtk_tree_path_free (path);
2574 }
2575 }
2576
2577 gtk_combo_box_popdown (combo_box);
2578
2579 return TRUE;
2580 }
2581
2582 return FALSE;
2583 }
2584
2585 static void
2586 gtk_combo_box_list_row_changed (GtkTreeModel *model,
2587 GtkTreePath *path,
2588 GtkTreeIter *iter,
2589 gpointer data)
2590 {
2591 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2592 gint width;
2593
2594 width = gtk_combo_box_calc_requested_width (combo_box, path);
2595
2596 if (width > combo_box->priv->width)
2597 {
2598 if (combo_box->priv->cell_view)
2599 {
2600 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
2601 gtk_widget_queue_resize (combo_box->priv->cell_view);
2602 }
2603 combo_box->priv->width = width;
2604 }
2605 }
2606
2607 /*
2608 * GtkCellLayout implementation
2609 */
2610 static void
2611 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
2612 GtkCellRenderer *cell,
2613 gboolean expand)
2614 {
2615 ComboCellInfo *info;
2616 GtkComboBox *combo_box;
2617 GtkWidget *menu;
2618
2619 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2620 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2621
2622 combo_box = GTK_COMBO_BOX (layout);
2623
2624 g_object_ref (G_OBJECT (cell));
2625 gtk_object_sink (GTK_OBJECT (cell));
2626
2627 info = g_new0 (ComboCellInfo, 1);
2628 info->cell = cell;
2629 info->expand = expand;
2630 info->pack = GTK_PACK_START;
2631
2632 combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
2633
2634 if (combo_box->priv->cell_view)
2635 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2636 cell, expand);
2637
2638 if (combo_box->priv->column)
2639 gtk_tree_view_column_pack_start (combo_box->priv->column, cell, expand);
2640
2641 menu = combo_box->priv->popup_widget;
2642 if (GTK_IS_MENU (menu))
2643 {
2644 GList *i, *list;
2645
2646 list = gtk_container_get_children (GTK_CONTAINER (menu));
2647 for (i = list; i; i = i->next)
2648 {
2649 GtkCellView *view;
2650
2651 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2652 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2653 else
2654 view = GTK_CELL_VIEW (i->data);
2655
2656 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), cell, expand);
2657 }
2658 g_list_free (list);
2659 }
2660 }
2661
2662 static void
2663 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
2664 GtkCellRenderer *cell,
2665 gboolean expand)
2666 {
2667 ComboCellInfo *info;
2668 GtkComboBox *combo_box;
2669 GtkWidget *menu;
2670
2671 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2672 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2673
2674 combo_box = GTK_COMBO_BOX (layout);
2675
2676 g_object_ref (G_OBJECT (cell));
2677 gtk_object_sink (GTK_OBJECT (cell));
2678
2679 info = g_new0 (ComboCellInfo, 1);
2680 info->cell = cell;
2681 info->expand = expand;
2682 info->pack = GTK_PACK_END;
2683
2684 combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
2685
2686 if (combo_box->priv->cell_view)
2687 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2688 cell, expand);
2689
2690 if (combo_box->priv->column)
2691 gtk_tree_view_column_pack_end (combo_box->priv->column, cell, expand);
2692
2693 menu = combo_box->priv->popup_widget;
2694 if (GTK_IS_MENU (menu))
2695 {
2696 GList *i, *list;
2697
2698 list = gtk_container_get_children (GTK_CONTAINER (menu));
2699 for (i = list; i; i = i->next)
2700 {
2701 GtkCellView *view;
2702
2703 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2704 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2705 else
2706 view = GTK_CELL_VIEW (i->data);
2707
2708 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (view), cell, expand);
2709 }
2710 g_list_free (list);
2711 }
2712 }
2713
2714 static void
2715 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
2716 {
2717 GtkWidget *menu;
2718 GtkComboBox *combo_box;
2719 GSList *i;
2720
2721 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2722
2723 combo_box = GTK_COMBO_BOX (layout);
2724
2725 if (combo_box->priv->cell_view)
2726 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->cell_view));
2727
2728 if (combo_box->priv->column)
2729 gtk_tree_view_column_clear (combo_box->priv->column);
2730
2731 for (i = combo_box->priv->cells; i; i = i->next)
2732 {
2733 ComboCellInfo *info = (ComboCellInfo *)i->data;
2734
2735 gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
2736 g_object_unref (G_OBJECT (info->cell));
2737 g_free (info);
2738 i->data = NULL;
2739 }
2740 g_slist_free (combo_box->priv->cells);
2741 combo_box->priv->cells = NULL;
2742
2743 menu = combo_box->priv->popup_widget;
2744 if (GTK_IS_MENU (menu))
2745 {
2746 GList *i, *list;
2747
2748 list = gtk_container_get_children (GTK_CONTAINER (menu));
2749 for (i = list; i; i = i->next)
2750 {
2751 GtkCellView *view;
2752
2753 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2754 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2755 else
2756 view = GTK_CELL_VIEW (i->data);
2757
2758 gtk_cell_layout_clear (GTK_CELL_LAYOUT (view));
2759 }
2760 g_list_free (list);
2761 }
2762 }
2763
2764 static void
2765 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
2766 GtkCellRenderer *cell,
2767 const gchar *attribute,
2768 gint column)
2769 {
2770 ComboCellInfo *info;
2771 GtkComboBox *combo_box;
2772 GtkWidget *menu;
2773
2774 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2775 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2776
2777 combo_box = GTK_COMBO_BOX (layout);
2778
2779 info = gtk_combo_box_get_cell_info (combo_box, cell);
2780
2781 info->attributes = g_slist_prepend (info->attributes,
2782 GINT_TO_POINTER (column));
2783 info->attributes = g_slist_prepend (info->attributes,
2784 g_strdup (attribute));
2785
2786 if (combo_box->priv->cell_view)
2787 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2788 cell, attribute, column);
2789
2790 if (combo_box->priv->column)
2791 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
2792 cell, attribute, column);
2793
2794 menu = combo_box->priv->popup_widget;
2795 if (GTK_IS_MENU (menu))
2796 {
2797 GList *i, *list;
2798
2799 list = gtk_container_get_children (GTK_CONTAINER (menu));
2800 for (i = list; i; i = i->next)
2801 {
2802 GtkCellView *view;
2803
2804 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2805 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2806 else
2807 view = GTK_CELL_VIEW (i->data);
2808
2809 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), cell,
2810 attribute, column);
2811 }
2812 g_list_free (list);
2813 }
2814
2815 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
2816 }
2817
2818 static void
2819 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
2820 GtkCellRenderer *cell,
2821 GtkCellLayoutDataFunc func,
2822 gpointer func_data,
2823 GDestroyNotify destroy)
2824 {
2825 ComboCellInfo *info;
2826 GtkComboBox *combo_box;
2827 GtkWidget *menu;
2828
2829 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2830
2831 combo_box = GTK_COMBO_BOX (layout);
2832
2833 info = gtk_combo_box_get_cell_info (combo_box, cell);
2834 g_return_if_fail (info != NULL);
2835
2836 if (info->destroy)
2837 {
2838 GDestroyNotify d = info->destroy;
2839
2840 info->destroy = NULL;
2841 d (info->func_data);
2842 }
2843
2844 info->func = func;
2845 info->func_data = func_data;
2846 info->destroy = destroy;
2847
2848 if (combo_box->priv->cell_view)
2849 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell, func, func_data, NULL);
2850
2851 if (combo_box->priv->column)
2852 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->column), cell, func, func_data, NULL);
2853
2854 menu = combo_box->priv->popup_widget;
2855 if (GTK_IS_MENU (menu))
2856 {
2857 GList *i, *list;
2858
2859 list = gtk_container_get_children (GTK_CONTAINER (menu));
2860 for (i = list; i; i = i->next)
2861 {
2862 GtkCellView *view;
2863
2864 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2865 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2866 else
2867 view = GTK_CELL_VIEW (i->data);
2868
2869 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (view), cell,
2870 func, func_data, NULL);
2871 }
2872 g_list_free (list);
2873 }
2874
2875 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
2876 }
2877
2878 static void
2879 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
2880 GtkCellRenderer *cell)
2881 {
2882 ComboCellInfo *info;
2883 GtkComboBox *combo_box;
2884 GtkWidget *menu;
2885 GSList *list;
2886
2887 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2888 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2889
2890 combo_box = GTK_COMBO_BOX (layout);
2891
2892 info = gtk_combo_box_get_cell_info (combo_box, cell);
2893 if (info)
2894 {
2895 list = info->attributes;
2896 while (list && list->next)
2897 {
2898 g_free (list->data);
2899 list = list->next->next;
2900 }
2901 g_slist_free (info->attributes);
2902 info->attributes = NULL;
2903 }
2904
2905 if (combo_box->priv->cell_view)
2906 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell);
2907
2908 if (combo_box->priv->column)
2909 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->column), cell);
2910
2911 menu = combo_box->priv->popup_widget;
2912 if (GTK_IS_MENU (menu))
2913 {
2914 GList *i, *list;
2915
2916 list = gtk_container_get_children (GTK_CONTAINER (menu));
2917 for (i = list; i; i = i->next)
2918 {
2919 GtkCellView *view;
2920
2921 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2922 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2923 else
2924 view = GTK_CELL_VIEW (i->data);
2925
2926 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (view), cell);
2927 }
2928 g_list_free (list);
2929 }
2930
2931 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
2932 }
2933
2934 static void
2935 gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
2936 GtkCellRenderer *cell,
2937 gint position)
2938 {
2939 ComboCellInfo *info;
2940 GtkComboBox *combo_box;
2941 GtkWidget *menu;
2942 GSList *link;
2943
2944 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2945 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2946
2947 combo_box = GTK_COMBO_BOX (layout);
2948
2949 info = gtk_combo_box_get_cell_info (combo_box, cell);
2950
2951 g_return_if_fail (info != NULL);
2952 g_return_if_fail (position >= 0);
2953
2954 link = g_slist_find (combo_box->priv->cells, info);
2955
2956 g_return_if_fail (link != NULL);
2957
2958 combo_box->priv->cells = g_slist_remove_link (combo_box->priv->cells, link);
2959 combo_box->priv->cells = g_slist_insert (combo_box->priv->cells, info,
2960 position);
2961
2962 if (combo_box->priv->cell_view)
2963 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2964 cell, position);
2965
2966 if (combo_box->priv->column)
2967 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->column),
2968 cell, position);
2969
2970 menu = combo_box->priv->popup_widget;
2971 if (GTK_IS_MENU (menu))
2972 {
2973 GList *i, *list;
2974
2975 list = gtk_container_get_children (GTK_CONTAINER (menu));
2976 for (i = list; i; i = i->next)
2977 {
2978 GtkCellView *view;
2979
2980 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2981 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2982 else
2983 view = GTK_CELL_VIEW (i->data);
2984
2985 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (view), cell, position);
2986 }
2987 g_list_free (list);
2988 }
2989
2990 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
2991 }
2992
2993 /*
2994 * public API
2995 */
2996
2997 /**
2998 * gtk_combo_box_new:
2999 *
3000 * Creates a new empty #GtkComboBox.
3001 *
3002 * Return value: A new #GtkComboBox.
3003 *
3004 * Since: 2.4
3005 */
3006 GtkWidget *
3007 gtk_combo_box_new (void)
3008 {
3009 return GTK_WIDGET (g_object_new (GTK_TYPE_COMBO_BOX, NULL));
3010 }
3011
3012 /**
3013 * gtk_combo_box_new_with_model:
3014 * @model: A #GtkTreeModel.
3015 *
3016 * Creates a new #GtkComboBox with the model initialized to @model.
3017 *
3018 * Return value: A new #GtkComboBox.
3019 *
3020 * Since: 2.4
3021 */
3022 GtkWidget *
3023 gtk_combo_box_new_with_model (GtkTreeModel *model)
3024 {
3025 GtkComboBox *combo_box;
3026
3027 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
3028
3029 combo_box = GTK_COMBO_BOX (g_object_new (GTK_TYPE_COMBO_BOX,
3030 "model", model,
3031 NULL));
3032
3033 return GTK_WIDGET (combo_box);
3034 }
3035
3036 /**
3037 * gtk_combo_box_set_wrap_width:
3038 * @combo_box: A #GtkComboBox.
3039 * @width: Preferred number of columns.
3040 *
3041 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
3042 * the preferred number of columns when you want to the popup to be layed out
3043 * in a table.
3044 *
3045 * Since: 2.4
3046 */
3047 void
3048 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
3049 gint width)
3050 {
3051 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3052 g_return_if_fail (width >= 0);
3053
3054 if (width != combo_box->priv->wrap_width)
3055 {
3056 combo_box->priv->wrap_width = width;
3057
3058 gtk_combo_box_check_appearance (combo_box);
3059 gtk_combo_box_relayout (combo_box);
3060
3061 g_object_notify (G_OBJECT (combo_box), "wrap_width");
3062 }
3063 }
3064
3065 /**
3066 * gtk_combo_box_set_row_span_column:
3067 * @combo_box: A #GtkComboBox.
3068 * @row_span: A column in the model passed during construction.
3069 *
3070 * Sets the column with row span information for @combo_box to be @row_span.
3071 * The row span column contains integers which indicate how many rows
3072 * an item should span.
3073 *
3074 * Since: 2.4
3075 */
3076 void
3077 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
3078 gint row_span)
3079 {
3080 gint col;
3081
3082 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3083
3084 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
3085 g_return_if_fail (row_span >= 0 && row_span < col);
3086
3087 if (row_span != combo_box->priv->row_column)
3088 {
3089 combo_box->priv->row_column = row_span;
3090
3091 gtk_combo_box_relayout (combo_box);
3092
3093 g_object_notify (G_OBJECT (combo_box), "row_span_column");
3094 }
3095 }
3096
3097 /**
3098 * gtk_combo_box_set_column_span_column:
3099 * @combo_box: A #GtkComboBox.
3100 * @column_span: A column in the model passed during construction.
3101 *
3102 * Sets the column with column span information for @combo_box to be
3103 * @column_span. The column span column contains integers which indicate
3104 * how many columns an item should span.
3105 *
3106 * Since: 2.4
3107 */
3108 void
3109 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
3110 gint column_span)
3111 {
3112 gint col;
3113
3114 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3115
3116 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
3117 g_return_if_fail (column_span >= 0 && column_span < col);
3118
3119 if (column_span != combo_box->priv->col_column)
3120 {
3121 combo_box->priv->col_column = column_span;
3122
3123 gtk_combo_box_relayout (combo_box);
3124
3125 g_object_notify (G_OBJECT (combo_box), "column_span_column");
3126 }
3127 }
3128
3129 /**
3130 * gtk_combo_box_get_active:
3131 * @combo_box: A #GtkComboBox.
3132 *
3133 * Returns the index of the currently active item, or -1 if there's no
3134 * active item.
3135 *
3136 * Return value: An integer which is the index of the currently active item, or
3137 * -1 if there's no active item.
3138 *
3139 * Since: 2.4
3140 */
3141 gint
3142 gtk_combo_box_get_active (GtkComboBox *combo_box)
3143 {
3144 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
3145
3146 return combo_box->priv->active_item;
3147 }
3148
3149 /**
3150 * gtk_combo_box_set_active:
3151 * @combo_box: A #GtkComboBox.
3152 * @index_: An index in the model passed during construction, or -1 to have
3153 * no active item.
3154 *
3155 * Sets the active item of @combo_box to be the item at @index.
3156 *
3157 * Since: 2.4
3158 */
3159 void
3160 gtk_combo_box_set_active (GtkComboBox *combo_box,
3161 gint index_)
3162 {
3163 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3164 /* -1 means "no item selected" */
3165 g_return_if_fail (index_ >= -1);
3166
3167 if (combo_box->priv->active_item == index_)
3168 return;
3169
3170 gtk_combo_box_set_active_internal (combo_box, index_);
3171 }
3172
3173 static void
3174 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
3175 gint index)
3176 {
3177 GtkTreePath *path;
3178
3179 combo_box->priv->active_item = index;
3180
3181 if (index == -1)
3182 {
3183 if (combo_box->priv->tree_view)
3184 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)));
3185 else
3186 {
3187 GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
3188
3189 if (GTK_IS_MENU (menu))
3190 gtk_menu_set_active (menu, -1);
3191 }
3192
3193 if (combo_box->priv->cell_view)
3194 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
3195 }
3196 else
3197 {
3198 path = gtk_tree_path_new_from_indices (index, -1);
3199
3200 if (combo_box->priv->tree_view)
3201 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view), path, NULL, FALSE);
3202 else
3203 {
3204 GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
3205
3206 if (GTK_IS_MENU (menu))
3207 gtk_menu_set_active (GTK_MENU (menu), index);
3208 }
3209
3210 if (combo_box->priv->cell_view)
3211 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), path);
3212
3213 gtk_tree_path_free (path);
3214 }
3215
3216 g_signal_emit_by_name (combo_box, "changed", NULL, NULL);
3217 }
3218
3219
3220 /**
3221 * gtk_combo_box_get_active_iter:
3222 * @combo_box: A #GtkComboBox
3223 * @iter: The uninitialized #GtkTreeIter.
3224 *
3225 * Sets @iter to point to the current active item, if it exists.
3226 *
3227 * Return value: %TRUE, if @iter was set
3228 *
3229 * Since: 2.4
3230 **/
3231 gboolean
3232 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
3233 GtkTreeIter *iter)
3234 {
3235 GtkTreePath *path;
3236 gint active;
3237 gboolean retval;
3238
3239 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
3240
3241 active = gtk_combo_box_get_active (combo_box);
3242 if (active < 0)
3243 return FALSE;
3244
3245 path = gtk_tree_path_new_from_indices (active, -1);
3246 retval = gtk_tree_model_get_iter (gtk_combo_box_get_model (combo_box),
3247 iter, path);
3248 gtk_tree_path_free (path);
3249
3250 return retval;
3251 }
3252
3253 /**
3254 * gtk_combo_box_set_active_iter:
3255 * @combo_box: A #GtkComboBox
3256 * @iter: The #GtkTreeIter.
3257 *
3258 * Sets the current active item to be the one referenced by @iter.
3259 * @iter must correspond to a path of depth one.
3260 *
3261 * Since: 2.4
3262 **/
3263 void
3264 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
3265 GtkTreeIter *iter)
3266 {
3267 GtkTreePath *path;
3268
3269 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3270
3271 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
3272 g_return_if_fail (path != NULL);
3273 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
3274
3275 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
3276 gtk_tree_path_free (path);
3277 }
3278
3279 /**
3280 * gtk_combo_box_set_model:
3281 * @combo_box: A #GtkComboBox.
3282 * @model: A #GtkTreeModel.
3283 *
3284 * Sets the model used by @combo_box to be @model. Will unset a previously set
3285 * model (if applicable). If @model is %NULL, then it will unset the model.
3286 *
3287 * Note that this function does not clear the cell renderers, you have to
3288 * call gtk_combo_box_cell_layout_clear() yourself if you need to set up
3289 * different cell renderers for the new model.
3290 *
3291 * Since: 2.4
3292 */
3293 void
3294 gtk_combo_box_set_model (GtkComboBox *combo_box,
3295 GtkTreeModel *model)
3296 {
3297 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3298
3299 if (!model)
3300 {
3301 gtk_combo_box_unset_model (combo_box);
3302 return;
3303 }
3304
3305 g_return_if_fail (GTK_IS_TREE_MODEL (model));
3306
3307 if (model == combo_box->priv->model)
3308 return;
3309
3310 if (combo_box->priv->model)
3311 gtk_combo_box_unset_model (combo_box);
3312
3313 combo_box->priv->model = model;
3314 g_object_ref (G_OBJECT (combo_box->priv->model));
3315
3316 combo_box->priv->inserted_id =
3317 g_signal_connect (combo_box->priv->model, "row_inserted",
3318 G_CALLBACK (gtk_combo_box_model_row_inserted),
3319 combo_box);
3320 combo_box->priv->deleted_id =
3321 g_signal_connect (combo_box->priv->model, "row_deleted",
3322 G_CALLBACK (gtk_combo_box_model_row_deleted),
3323 combo_box);
3324 combo_box->priv->reordered_id =
3325 g_signal_connect (combo_box->priv->model, "rows_reordered",
3326 G_CALLBACK (gtk_combo_box_model_rows_reordered),
3327 combo_box);
3328 combo_box->priv->changed_id =
3329 g_signal_connect (combo_box->priv->model, "row_changed",
3330 G_CALLBACK (gtk_combo_box_model_row_changed),
3331 combo_box);
3332
3333 if (combo_box->priv->tree_view)
3334 {
3335 /* list mode */
3336 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
3337 combo_box->priv->model);
3338 }
3339 else
3340 {
3341 /* menu mode */
3342 if (combo_box->priv->popup_widget)
3343 gtk_combo_box_menu_fill (combo_box);
3344
3345 }
3346
3347 if (combo_box->priv->cell_view)
3348 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
3349 combo_box->priv->model);
3350 }
3351
3352 /**
3353 * gtk_combo_box_get_model
3354 * @combo_box: A #GtkComboBox.
3355 *
3356 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
3357 *
3358 * Return value: A #GtkTreeModel which was passed during construction.
3359 *
3360 * Since: 2.4
3361 */
3362 GtkTreeModel *
3363 gtk_combo_box_get_model (GtkComboBox *combo_box)
3364 {
3365 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
3366
3367 return combo_box->priv->model;
3368 }
3369
3370
3371 /* convenience API for simple text combos */
3372
3373 /**
3374 * gtk_combo_box_new_text:
3375 *
3376 * Convenience function which constructs a new text combo box, which is a
3377 * #GtkComboBox just displaying strings. If you use this function to create
3378 * a text combo box, you should only manipulate its data source with the
3379 * following convenience functions: gtk_combo_box_append_text(),
3380 * gtk_combo_box_insert_text(), gtk_combo_box_prepend_text() and
3381 * gtk_combo_box_remove_text().
3382 *
3383 * Return value: A new text combo box.
3384 *
3385 * Since: 2.4
3386 */
3387 GtkWidget *
3388 gtk_combo_box_new_text (void)
3389 {
3390 GtkWidget *combo_box;
3391 GtkCellRenderer *cell;
3392 GtkListStore *store;
3393
3394 store = gtk_list_store_new (1, G_TYPE_STRING);
3395 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
3396 g_object_unref (store);
3397
3398 cell = gtk_cell_renderer_text_new ();
3399 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
3400 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
3401 "text", 0,
3402 NULL);
3403
3404 return combo_box;
3405 }
3406
3407 /**
3408 * gtk_combo_box_append_text:
3409 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
3410 * @text: A string.
3411 *
3412 * Appends @string to the list of strings stored in @combo_box. Note that
3413 * you can only use this function with combo boxes constructed with
3414 * gtk_combo_box_new_text().
3415 *
3416 * Since: 2.4
3417 */
3418 void
3419 gtk_combo_box_append_text (GtkComboBox *combo_box,
3420 const gchar *text)
3421 {
3422 GtkTreeIter iter;
3423 GtkListStore *store;
3424
3425 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3426 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3427 g_return_if_fail (text != NULL);
3428
3429 store = GTK_LIST_STORE (combo_box->priv->model);
3430
3431 gtk_list_store_append (store, &iter);
3432 gtk_list_store_set (store, &iter, 0, text, -1);
3433 }
3434
3435 /**
3436 * gtk_combo_box_insert_text:
3437 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
3438 * @position: An index to insert @text.
3439 * @text: A string.
3440 *
3441 * Inserts @string at @position in the list of strings stored in @combo_box.
3442 * Note that you can only use this function with combo boxes constructed
3443 * with gtk_combo_box_new_text().
3444 *
3445 * Since: 2.4
3446 */
3447 void
3448 gtk_combo_box_insert_text (GtkComboBox *combo_box,
3449 gint position,
3450 const gchar *text)
3451 {
3452 GtkTreeIter iter;
3453 GtkListStore *store;
3454
3455 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3456 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3457 g_return_if_fail (position >= 0);
3458 g_return_if_fail (text != NULL);
3459
3460 store = GTK_LIST_STORE (combo_box->priv->model);
3461
3462 gtk_list_store_insert (store, &iter, position);
3463 gtk_list_store_set (store, &iter, 0, text, -1);
3464 }
3465
3466 /**
3467 * gtk_combo_box_prepend_text:
3468 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
3469 * @text: A string.
3470 *
3471 * Prepends @string to the list of strings stored in @combo_box. Note that
3472 * you can only use this function with combo boxes constructed with
3473 * gtk_combo_box_new_text().
3474 *
3475 * Since: 2.4
3476 */
3477 void
3478 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
3479 const gchar *text)
3480 {
3481 GtkTreeIter iter;
3482 GtkListStore *store;
3483
3484 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3485 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3486 g_return_if_fail (text != NULL);
3487
3488 store = GTK_LIST_STORE (combo_box->priv->model);
3489
3490 gtk_list_store_prepend (store, &iter);
3491 gtk_list_store_set (store, &iter, 0, text, -1);
3492 }
3493
3494 /**
3495 * gtk_combo_box_remove_text:
3496 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
3497 * @position: Index of the item to remove.
3498 *
3499 * Removes the string at @position from @combo_box. Note that you can only use
3500 * this function with combo boxes constructed with gtk_combo_box_new_text().
3501 *
3502 * Since: 2.4
3503 */
3504 void
3505 gtk_combo_box_remove_text (GtkComboBox *combo_box,
3506 gint position)
3507 {
3508 GtkTreeIter iter;
3509 GtkListStore *store;
3510
3511 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3512 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3513 g_return_if_fail (position >= 0);
3514
3515 store = GTK_LIST_STORE (combo_box->priv->model);
3516
3517 if (gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter,
3518 NULL, position))
3519 gtk_list_store_remove (store, &iter);
3520 }
3521
3522
3523 static gboolean
3524 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
3525 gboolean group_cycling)
3526 {
3527 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
3528
3529 gtk_widget_grab_focus (combo_box->priv->button);
3530
3531 return TRUE;
3532 }
3533
3534 static void
3535 gtk_combo_box_destroy (GtkObject *object)
3536 {
3537 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
3538
3539 gtk_combo_box_popdown (combo_box);
3540
3541 combo_box->priv->destroying = 1;
3542
3543 GTK_OBJECT_CLASS (parent_class)->destroy (object);
3544 combo_box->priv->cell_view = NULL;
3545
3546 combo_box->priv->destroying = 0;
3547 }
3548
3549 static void
3550 gtk_combo_box_finalize (GObject *object)
3551 {
3552 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
3553 GSList *i;
3554
3555 if (GTK_IS_MENU (combo_box->priv->popup_widget))
3556 {
3557 gtk_combo_box_menu_destroy (combo_box);
3558 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
3559 combo_box->priv->popup_widget = NULL;
3560 }
3561
3562 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
3563 gtk_combo_box_list_destroy (combo_box);
3564
3565 if (combo_box->priv->popup_window)
3566 gtk_widget_destroy (combo_box->priv->popup_window);
3567
3568 gtk_combo_box_unset_model (combo_box);
3569
3570 for (i = combo_box->priv->cells; i; i = i->next)
3571 {
3572 ComboCellInfo *info = (ComboCellInfo *)i->data;
3573 GSList *list = info->attributes;
3574
3575 if (info->destroy)
3576 info->destroy (info->func_data);
3577
3578 while (list && list->next)
3579 {
3580 g_free (list->data);
3581 list = list->next->next;
3582 }
3583 g_slist_free (info->attributes);
3584
3585 g_object_unref (G_OBJECT (info->cell));
3586 g_free (info);
3587 }
3588 g_slist_free (combo_box->priv->cells);
3589
3590 g_free (combo_box->priv);
3591
3592 G_OBJECT_CLASS (parent_class)->finalize (object);
3593 }
3594
3595
3596 /**
3597 * Code below this point has been pulled in from gtkmenu.c in 2.4.14
3598 * and is needed to provide gtk_menu_attach()
3599 */
3600
3601 typedef struct
3602 {
3603 gint left_attach;
3604 gint right_attach;
3605 gint top_attach;
3606 gint bottom_attach;
3607 gint effective_left_attach;
3608 gint effective_right_attach;
3609 gint effective_top_attach;
3610 gint effective_bottom_attach;
3611 } AttachInfo;
3612
3613 #define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
3614
3615 static AttachInfo *
3616 get_attach_info (GtkWidget *child)
3617 {
3618 GObject *object = G_OBJECT (child);
3619 AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY);
3620
3621 if (!ai)
3622 {
3623 ai = g_new0 (AttachInfo, 1);
3624 g_object_set_data_full (object, ATTACH_INFO_KEY, ai, g_free);
3625 }
3626
3627 return ai;
3628 }
3629
3630 /**
3631 * gtk_menu_attach:
3632 * @menu: a #GtkMenu.
3633 * @child: a #GtkMenuItem.
3634 * @left_attach: The column number to attach the left side of the item to.
3635 * @right_attach: The column number to attach the right side of the item to.
3636 * @top_attach: The row number to attach the top of the item to.
3637 * @bottom_attach: The row number to attach the bottom of the item to.
3638 *
3639 * Adds a new #GtkMenuItem to a (table) menu. The number of 'cells' that
3640 * an item will occupy is specified by @left_attach, @right_attach,
3641 * @top_attach and @bottom_attach. These each represent the leftmost,
3642 * rightmost, uppermost and lower column and row numbers of the table.
3643 * (Columns and rows are indexed from zero).
3644 *
3645 * Note that this function is not related to gtk_menu_detach().
3646 *
3647 * Since: 2.4
3648 **/
3649 static void
3650 gtk_menu_attach (GtkMenu *menu,
3651 GtkWidget *child,
3652 guint left_attach,
3653 guint right_attach,
3654 guint top_attach,
3655 guint bottom_attach)
3656 {
3657 GtkMenuShell *menu_shell;
3658
3659 g_return_if_fail (GTK_IS_MENU (menu));
3660 g_return_if_fail (GTK_IS_MENU_ITEM (child));
3661 g_return_if_fail (child->parent == NULL ||
3662 child->parent == GTK_WIDGET (menu));
3663 g_return_if_fail (left_attach < right_attach);
3664 g_return_if_fail (top_attach < bottom_attach);
3665
3666 menu_shell = GTK_MENU_SHELL (menu);
3667
3668 if (!child->parent)
3669 {
3670 AttachInfo *ai = get_attach_info (child);
3671
3672 ai->left_attach = left_attach;
3673 ai->right_attach = right_attach;
3674 ai->top_attach = top_attach;
3675 ai->bottom_attach = bottom_attach;
3676
3677 menu_shell->children = g_list_append (menu_shell->children, child);
3678
3679 gtk_widget_set_parent (child, GTK_WIDGET (menu));
3680
3681 /*
3682 menu_queue_resize (menu);
3683 */
3684 }
3685 else
3686 {
3687 gtk_container_child_set (GTK_CONTAINER (child->parent), child,
3688 "left_attach", left_attach,
3689 "right_attach", right_attach,
3690 "top_attach", top_attach,
3691 "bottom_attach", bottom_attach,
3692 NULL);
3693 }
3694 }
3695 #endif /* Gtk 2.4 */