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