Mercurial > pidgin
annotate src/gtkcombobox.c @ 14043:88ccffe0875c
[gaim-migrate @ 16650]
Remove "Napster" from a few more places
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sat, 05 Aug 2006 17:40:38 +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 */ |