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