src/gtkcombobox.c

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

mercurial