comparison gtk/gaimcombobox.c @ 14191:009db0b357b5

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