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