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