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