comparison pidgin/pidgincombobox.c @ 15822:84b0f9b23ede

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