gtkcombobox.c 171 KB
Newer Older
1 2 3
/* gtkcombobox.c
 * Copyright (C) 2002, 2003  Kristian Rietveld <kris@gtk.org>
 *
4 5 6 7
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * 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 11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Library General Public License for more details.
13
 *
14 15
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
16 17 18 19
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

20
#include "config.h"
Kristian Rietveld's avatar
Kristian Rietveld committed
21
#include "gtkcombobox.h"
22 23 24

#include "gtkarrow.h"
#include "gtkbindings.h"
Kristian Rietveld's avatar
Kristian Rietveld committed
25
#include "gtkcelllayout.h"
26
#include "gtkcellrenderertext.h"
Kristian Rietveld's avatar
Kristian Rietveld committed
27
#include "gtkcellview.h"
28
#include "gtkeventbox.h"
Kristian Rietveld's avatar
Kristian Rietveld committed
29
#include "gtkframe.h"
30
#include "gtkhbox.h"
31 32 33
#include "gtkliststore.h"
#include "gtkmain.h"
#include "gtkmenu.h"
34
#include "gtkscrolledwindow.h"
35
#include "gtkseparatormenuitem.h"
36
#include "gtktearoffmenuitem.h"
Kristian Rietveld's avatar
Kristian Rietveld committed
37
#include "gtktogglebutton.h"
38
#include "gtktreeselection.h"
Kristian Rietveld's avatar
Kristian Rietveld committed
39 40
#include "gtkvseparator.h"
#include "gtkwindow.h"
41
#include "gtkprivate.h"
42 43 44 45 46

#include <gdk/gdkkeysyms.h>

#include <gobject/gvaluecollector.h>

Kristian Rietveld's avatar
Kristian Rietveld committed
47
#include <string.h>
48 49 50 51 52
#include <stdarg.h>

#include "gtkmarshalers.h"
#include "gtkintl.h"

53
#include "gtktreeprivate.h"
54
#include "gtkalias.h"
55 56 57 58 59 60 61 62 63

/* WELCOME, to THE house of evil code */

typedef struct _ComboCellInfo ComboCellInfo;
struct _ComboCellInfo
{
  GtkCellRenderer *cell;
  GSList *attributes;

64 65 66 67
  GtkCellLayoutDataFunc func;
  gpointer func_data;
  GDestroyNotify destroy;

68 69 70 71
  guint expand : 1;
  guint pack : 1;
};

72 73
#define GTK_COMBO_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COMBO_BOX, GtkComboBoxPrivate))

74 75 76 77 78 79 80 81
struct _GtkComboBoxPrivate
{
  GtkTreeModel *model;

  gint col_column;
  gint row_column;

  gint wrap_width;
82
  GtkShadowType shadow_type;
83

84
  gint active; /* Only temporary */
85
  GtkTreeRowReference *active_row;
86 87 88 89 90 91 92 93

  GtkWidget *tree_view;
  GtkTreeViewColumn *column;

  GtkWidget *cell_view;
  GtkWidget *cell_view_frame;

  GtkWidget *button;
94
  GtkWidget *box;
95 96 97 98 99
  GtkWidget *arrow;
  GtkWidget *separator;

  GtkWidget *popup_widget;
  GtkWidget *popup_window;
100
  GtkWidget *scrolled_window;
101 102 103

  guint inserted_id;
  guint deleted_id;
104 105
  guint reordered_id;
  guint changed_id;
106
  guint popup_idle_id;
107 108
  guint activate_button;
  guint32 activate_time;
109
  guint scroll_timer;
110
  guint resize_idle_id;
111 112

  gint width;
113
  gint height;
114 115 116
  GSList *cells;

  guint popup_in_progress : 1;
117
  guint popup_shown : 1;
118
  guint add_tearoffs : 1;
119 120 121
  guint has_frame : 1;
  guint is_cell_renderer : 1;
  guint editing_canceled : 1;
122
  guint auto_scroll : 1;
Matthias Clasen's avatar
Matthias Clasen committed
123
  guint focus_on_click : 1;
124
  guint button_sensitivity : 2;
125 126 127

  GtkTreeViewRowSeparatorFunc row_separator_func;
  gpointer                    row_separator_data;
128
  GDestroyNotify              row_separator_destroy;
129 130

  gchar *tearoff_title;
131 132
};

133 134 135 136 137 138 139 140 141 142 143 144 145 146
/* While debugging this evil code, I have learned that
 * there are actually 4 modes to this widget, which can
 * be characterized as follows
 * 
 * 1) menu mode, no child added
 *
 * tree_view -> NULL
 * cell_view -> GtkCellView, regular child
 * cell_view_frame -> NULL
 * button -> GtkToggleButton set_parent to combo
 * arrow -> GtkArrow set_parent to button
 * separator -> GtkVSepator set_parent to button
 * popup_widget -> GtkMenu
 * popup_window -> NULL
147
 * scrolled_window -> NULL
148 149 150 151 152 153 154 155 156 157 158
 *
 * 2) menu mode, child added
 * 
 * tree_view -> NULL
 * cell_view -> NULL 
 * cell_view_frame -> NULL
 * button -> GtkToggleButton set_parent to combo
 * arrow -> GtkArrow, child of button
 * separator -> NULL
 * popup_widget -> GtkMenu
 * popup_window -> NULL
159
 * scrolled_window -> NULL
160 161 162
 *
 * 3) list mode, no child added
 * 
163
 * tree_view -> GtkTreeView, child of scrolled_window
164 165 166 167 168 169 170
 * cell_view -> GtkCellView, regular child
 * cell_view_frame -> GtkFrame, set parent to combo
 * button -> GtkToggleButton, set_parent to combo
 * arrow -> GtkArrow, child of button
 * separator -> NULL
 * popup_widget -> tree_view
 * popup_window -> GtkWindow
171
 * scrolled_window -> GtkScrolledWindow, child of popup_window
172 173 174
 *
 * 4) list mode, child added
 *
175
 * tree_view -> GtkTreeView, child of scrolled_window
176 177 178 179 180 181 182
 * cell_view -> NULL
 * cell_view_frame -> NULL
 * button -> GtkToggleButton, set_parent to combo
 * arrow -> GtkArrow, child of button
 * separator -> NULL
 * popup_widget -> tree_view
 * popup_window -> GtkWindow
183
 * scrolled_window -> GtkScrolledWindow, child of popup_window
184 185 186
 * 
 */

187 188
enum {
  CHANGED,
189 190
  MOVE_ACTIVE,
  POPUP,
191
  POPDOWN,
192 193 194 195 196 197 198 199 200
  LAST_SIGNAL
};

enum {
  PROP_0,
  PROP_MODEL,
  PROP_WRAP_WIDTH,
  PROP_ROW_SPAN_COLUMN,
  PROP_COLUMN_SPAN_COLUMN,
201
  PROP_ACTIVE,
202
  PROP_ADD_TEAROFFS,
203
  PROP_TEAROFF_TITLE,
Matthias Clasen's avatar
Matthias Clasen committed
204
  PROP_HAS_FRAME,
205
  PROP_FOCUS_ON_CLICK,
206 207
  PROP_POPUP_SHOWN,
  PROP_BUTTON_SENSITIVITY
208 209 210 211 212
};

static guint combo_box_signals[LAST_SIGNAL] = {0,};

#define BONUS_PADDING 4
213
#define SCROLL_TIME  100
214 215

/* common */
216

217
static void     gtk_combo_box_cell_layout_init     (GtkCellLayoutIface *iface);
218
static void     gtk_combo_box_cell_editable_init   (GtkCellEditableIface *iface);
219
static void     gtk_combo_box_dispose              (GObject          *object);
220 221
static void     gtk_combo_box_finalize             (GObject          *object);
static void     gtk_combo_box_destroy              (GtkObject        *object);
222 223 224 225 226 227 228 229 230 231

static void     gtk_combo_box_set_property         (GObject         *object,
                                                    guint            prop_id,
                                                    const GValue    *value,
                                                    GParamSpec      *spec);
static void     gtk_combo_box_get_property         (GObject         *object,
                                                    guint            prop_id,
                                                    GValue          *value,
                                                    GParamSpec      *spec);

232
static void     gtk_combo_box_state_changed        (GtkWidget        *widget,
233 234
			                            GtkStateType      previous);
static void     gtk_combo_box_grab_focus           (GtkWidget       *widget);
235
static void     gtk_combo_box_style_set            (GtkWidget       *widget,
236
                                                    GtkStyle        *previous);
237 238
static void     gtk_combo_box_button_toggled       (GtkWidget       *widget,
                                                    gpointer         data);
239 240 241
static void     gtk_combo_box_button_state_changed (GtkWidget       *widget,
			                            GtkStateType     previous,
						    gpointer         data);
242 243
static void     gtk_combo_box_add                  (GtkContainer    *container,
                                                    GtkWidget       *widget);
244 245
static void     gtk_combo_box_remove               (GtkContainer    *container,
                                                    GtkWidget       *widget);
246 247 248 249 250 251 252 253 254 255 256

static ComboCellInfo *gtk_combo_box_get_cell_info  (GtkComboBox      *combo_box,
                                                    GtkCellRenderer  *cell);

static void     gtk_combo_box_menu_show            (GtkWidget        *menu,
                                                    gpointer          user_data);
static void     gtk_combo_box_menu_hide            (GtkWidget        *menu,
                                                    gpointer          user_data);

static void     gtk_combo_box_set_popup_widget     (GtkComboBox      *combo_box,
                                                    GtkWidget        *popup);
257 258 259 260 261 262 263 264 265 266
static void     gtk_combo_box_menu_position_below  (GtkMenu          *menu,
                                                    gint             *x,
                                                    gint             *y,
                                                    gint             *push_in,
                                                    gpointer          user_data);
static void     gtk_combo_box_menu_position_over   (GtkMenu          *menu,
                                                    gint             *x,
                                                    gint             *y,
                                                    gint             *push_in,
                                                    gpointer          user_data);
267 268 269 270 271 272 273 274
static void     gtk_combo_box_menu_position        (GtkMenu          *menu,
                                                    gint             *x,
                                                    gint             *y,
                                                    gint             *push_in,
                                                    gpointer          user_data);

static gint     gtk_combo_box_calc_requested_width (GtkComboBox      *combo_box,
                                                    GtkTreePath      *path);
275
static void     gtk_combo_box_remeasure            (GtkComboBox      *combo_box);
276

277 278
static void     gtk_combo_box_unset_model          (GtkComboBox      *combo_box);

279 280 281 282 283 284 285 286 287 288
static void     gtk_combo_box_size_request         (GtkWidget        *widget,
                                                    GtkRequisition   *requisition);
static void     gtk_combo_box_size_allocate        (GtkWidget        *widget,
                                                    GtkAllocation    *allocation);
static void     gtk_combo_box_forall               (GtkContainer     *container,
                                                    gboolean          include_internals,
                                                    GtkCallback       callback,
                                                    gpointer          callback_data);
static gboolean gtk_combo_box_expose_event         (GtkWidget        *widget,
                                                    GdkEventExpose   *event);
289 290
static gboolean gtk_combo_box_scroll_event         (GtkWidget        *widget,
                                                    GdkEventScroll   *event);
291
static void     gtk_combo_box_set_active_internal  (GtkComboBox      *combo_box,
292
						    GtkTreePath      *path);
293

294
static void     gtk_combo_box_check_appearance     (GtkComboBox      *combo_box);
295
static gchar *  gtk_combo_box_real_get_active_text (GtkComboBox      *combo_box);
296 297 298
static void     gtk_combo_box_real_move_active     (GtkComboBox      *combo_box,
                                                    GtkScrollType     scroll);
static void     gtk_combo_box_real_popup           (GtkComboBox      *combo_box);
299
static gboolean gtk_combo_box_real_popdown         (GtkComboBox      *combo_box);
300

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
/* listening to the model */
static void     gtk_combo_box_model_row_inserted   (GtkTreeModel     *model,
						    GtkTreePath      *path,
						    GtkTreeIter      *iter,
						    gpointer          user_data);
static void     gtk_combo_box_model_row_deleted    (GtkTreeModel     *model,
						    GtkTreePath      *path,
						    gpointer          user_data);
static void     gtk_combo_box_model_rows_reordered (GtkTreeModel     *model,
						    GtkTreePath      *path,
						    GtkTreeIter      *iter,
						    gint             *new_order,
						    gpointer          user_data);
static void     gtk_combo_box_model_row_changed    (GtkTreeModel     *model,
						    GtkTreePath      *path,
						    GtkTreeIter      *iter,
						    gpointer          data);
318 319 320 321
static void     gtk_combo_box_model_row_expanded   (GtkTreeModel     *model,
						    GtkTreePath      *path,
						    GtkTreeIter      *iter,
						    gpointer          data);
322

323
/* list */
324 325 326 327 328
static void     gtk_combo_box_list_position        (GtkComboBox      *combo_box, 
						    gint             *x, 
						    gint             *y, 
						    gint             *width,
						    gint             *height);
329 330 331 332 333 334 335 336 337
static void     gtk_combo_box_list_setup           (GtkComboBox      *combo_box);
static void     gtk_combo_box_list_destroy         (GtkComboBox      *combo_box);

static gboolean gtk_combo_box_list_button_released (GtkWidget        *widget,
                                                    GdkEventButton   *event,
                                                    gpointer          data);
static gboolean gtk_combo_box_list_key_press       (GtkWidget        *widget,
                                                    GdkEventKey      *event,
                                                    gpointer          data);
338 339 340 341 342 343 344
static gboolean gtk_combo_box_list_enter_notify    (GtkWidget        *widget,
                                                    GdkEventCrossing *event,
                                                    gpointer          data);
static void     gtk_combo_box_list_auto_scroll     (GtkComboBox   *combo,
						    gint           x,
						    gint           y);
static gboolean gtk_combo_box_list_scroll_timeout  (GtkComboBox   *combo);
345 346 347 348
static gboolean gtk_combo_box_list_button_pressed  (GtkWidget        *widget,
                                                    GdkEventButton   *event,
                                                    gpointer          data);

349 350 351 352 353 354
static gboolean gtk_combo_box_list_select_func     (GtkTreeSelection *selection,
						    GtkTreeModel     *model,
						    GtkTreePath      *path,
						    gboolean          path_currently_selected,
						    gpointer          data);

355 356 357 358
static void     gtk_combo_box_list_row_changed     (GtkTreeModel     *model,
                                                    GtkTreePath      *path,
                                                    GtkTreeIter      *iter,
                                                    gpointer          data);
359
static void     gtk_combo_box_list_popup_resize    (GtkComboBox      *combo_box);
360 361 362

/* menu */
static void     gtk_combo_box_menu_setup           (GtkComboBox      *combo_box,
363
                                                    gboolean          add_children);
364
static void     gtk_combo_box_menu_fill            (GtkComboBox      *combo_box);
365 366 367
static void     gtk_combo_box_menu_fill_level      (GtkComboBox      *combo_box,
						    GtkWidget        *menu,
						    GtkTreeIter      *iter);
368
static void     gtk_combo_box_update_title         (GtkComboBox      *combo_box);
369 370 371
static void     gtk_combo_box_menu_destroy         (GtkComboBox      *combo_box);

static void     gtk_combo_box_relayout_item        (GtkComboBox      *combo_box,
372 373 374
						    GtkWidget        *item,
                                                    GtkTreeIter      *iter,
						    GtkWidget        *last);
375 376 377 378 379 380 381 382 383 384 385 386 387 388
static void     gtk_combo_box_relayout             (GtkComboBox      *combo_box);

static gboolean gtk_combo_box_menu_button_press    (GtkWidget        *widget,
                                                    GdkEventButton   *event,
                                                    gpointer          user_data);
static void     gtk_combo_box_menu_item_activate   (GtkWidget        *item,
                                                    gpointer          user_data);
static void     gtk_combo_box_menu_row_inserted    (GtkTreeModel     *model,
                                                    GtkTreePath      *path,
                                                    GtkTreeIter      *iter,
                                                    gpointer          user_data);
static void     gtk_combo_box_menu_row_deleted     (GtkTreeModel     *model,
                                                    GtkTreePath      *path,
                                                    gpointer          user_data);
389 390 391 392 393
static void     gtk_combo_box_menu_rows_reordered  (GtkTreeModel     *model,
						    GtkTreePath      *path,
						    GtkTreeIter      *iter,
						    gint             *new_order,
						    gpointer          user_data);
394 395 396 397
static void     gtk_combo_box_menu_row_changed     (GtkTreeModel     *model,
                                                    GtkTreePath      *path,
                                                    GtkTreeIter      *iter,
                                                    gpointer          data);
398 399 400
static gboolean gtk_combo_box_menu_key_press       (GtkWidget        *widget,
						    GdkEventKey      *event,
						    gpointer          data);
401 402 403 404 405 406
static void     gtk_combo_box_menu_popup           (GtkComboBox      *combo_box,
						    guint             button, 
						    guint32           activate_time);
static GtkWidget *gtk_cell_view_menu_item_new      (GtkComboBox      *combo_box,
						    GtkTreeModel     *model,
						    GtkTreeIter      *iter);
407

408 409 410 411 412 413 414
/* cell layout */
static void     gtk_combo_box_cell_layout_pack_start         (GtkCellLayout         *layout,
                                                              GtkCellRenderer       *cell,
                                                              gboolean               expand);
static void     gtk_combo_box_cell_layout_pack_end           (GtkCellLayout         *layout,
                                                              GtkCellRenderer       *cell,
                                                              gboolean               expand);
415
static GList   *gtk_combo_box_cell_layout_get_cells          (GtkCellLayout         *layout);
416 417 418 419 420 421 422 423 424 425 426 427
static void     gtk_combo_box_cell_layout_clear              (GtkCellLayout         *layout);
static void     gtk_combo_box_cell_layout_add_attribute      (GtkCellLayout         *layout,
                                                              GtkCellRenderer       *cell,
                                                              const gchar           *attribute,
                                                              gint                   column);
static void     gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
                                                              GtkCellRenderer       *cell,
                                                              GtkCellLayoutDataFunc  func,
                                                              gpointer               func_data,
                                                              GDestroyNotify         destroy);
static void     gtk_combo_box_cell_layout_clear_attributes   (GtkCellLayout         *layout,
                                                              GtkCellRenderer       *cell);
Kristian Rietveld's avatar
Kristian Rietveld committed
428 429 430
static void     gtk_combo_box_cell_layout_reorder            (GtkCellLayout         *layout,
                                                              GtkCellRenderer       *cell,
                                                              gint                   position);
431 432
static gboolean gtk_combo_box_mnemonic_activate              (GtkWidget    *widget,
							      gboolean      group_cycling);
433

434 435 436 437 438 439 440
static void     gtk_combo_box_sync_cells                     (GtkComboBox   *combo_box,
					                      GtkCellLayout *cell_layout);
static void     combo_cell_data_func                         (GtkCellLayout   *cell_layout,
							      GtkCellRenderer *cell,
							      GtkTreeModel    *tree_model,
							      GtkTreeIter     *iter,
							      gpointer         data);
441
static void     gtk_combo_box_child_show                     (GtkWidget       *widget,
442
							      GtkComboBox     *combo_box);
443
static void     gtk_combo_box_child_hide                     (GtkWidget       *widget,
444
							      GtkComboBox     *combo_box);
445

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
/* GtkBuildable method implementation */
static GtkBuildableIface *parent_buildable_iface;

static void     gtk_combo_box_buildable_init                 (GtkBuildableIface *iface);
static gboolean gtk_combo_box_buildable_custom_tag_start     (GtkBuildable  *buildable,
							      GtkBuilder    *builder,
							      GObject       *child,
							      const gchar   *tagname,
							      GMarkupParser *parser,
							      gpointer      *data);
static void     gtk_combo_box_buildable_custom_tag_end       (GtkBuildable  *buildable,
							      GtkBuilder    *builder,
							      GObject       *child,
							      const gchar   *tagname,
							      gpointer      *data);
461

462 463 464 465 466
/* GtkCellEditable method implementations */
static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
					 GdkEvent        *event);


Matthias Clasen's avatar
Matthias Clasen committed
467 468 469 470
G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
						gtk_combo_box_cell_layout_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
471 472 473 474
						gtk_combo_box_cell_editable_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
						gtk_combo_box_buildable_init))

475 476 477 478 479 480

/* common */
static void
gtk_combo_box_class_init (GtkComboBoxClass *klass)
{
  GObjectClass *object_class;
481
  GtkObjectClass *gtk_object_class;
482 483
  GtkContainerClass *container_class;
  GtkWidgetClass *widget_class;
484
  GtkBindingSet *binding_set;
485

486 487
  klass->get_active_text = gtk_combo_box_real_get_active_text;

488 489 490
  container_class = (GtkContainerClass *)klass;
  container_class->forall = gtk_combo_box_forall;
  container_class->add = gtk_combo_box_add;
491
  container_class->remove = gtk_combo_box_remove;
492 493 494 495 496

  widget_class = (GtkWidgetClass *)klass;
  widget_class->size_allocate = gtk_combo_box_size_allocate;
  widget_class->size_request = gtk_combo_box_size_request;
  widget_class->expose_event = gtk_combo_box_expose_event;
497
  widget_class->scroll_event = gtk_combo_box_scroll_event;
498
  widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
499
  widget_class->grab_focus = gtk_combo_box_grab_focus;
500
  widget_class->style_set = gtk_combo_box_style_set;
501
  widget_class->state_changed = gtk_combo_box_state_changed;
502

503 504 505
  gtk_object_class = (GtkObjectClass *)klass;
  gtk_object_class->destroy = gtk_combo_box_destroy;

506
  object_class = (GObjectClass *)klass;
507
  object_class->dispose = gtk_combo_box_dispose;
508
  object_class->finalize = gtk_combo_box_finalize;
509 510 511 512
  object_class->set_property = gtk_combo_box_set_property;
  object_class->get_property = gtk_combo_box_get_property;

  /* signals */
Matthias Clasen's avatar
Matthias Clasen committed
513 514 515 516
  /**
   * GtkComboBox::changed:
   * @widget: the object which received the signal
   * 
517
   * The changed signal is emitted when the active
Matthias Clasen's avatar
Matthias Clasen committed
518 519 520
   * item is changed. The can be due to the user selecting
   * a different item from the list, or due to a 
   * call to gtk_combo_box_set_active_iter().
521 522
   * It will also be emitted while typing into a GtkComboBoxEntry, 
   * as well as when selecting an item from the GtkComboBoxEntry's list.
Matthias Clasen's avatar
Matthias Clasen committed
523 524 525
   *
   * Since: 2.4
   */
526
  combo_box_signals[CHANGED] =
527
    g_signal_new (I_("changed"),
528 529 530 531 532 533
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkComboBoxClass, changed),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
534 535 536 537 538 539 540 541 542 543 544
  /**
   * GtkComboBox::move-active:
   * @widget: the object that received the signal
   * @scroll_type: a #GtkScrollType
   *
   * The ::move-active signal is a 
   * <link linkend="keybinding-signals">keybinding signal</link>
   * which gets emitted to move the active selection.
   *
   * Since: 2.12
   */
545
  combo_box_signals[MOVE_ACTIVE] =
546 547 548 549 550 551 552 553
    g_signal_new_class_handler (I_("move-active"),
                                G_OBJECT_CLASS_TYPE (klass),
                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                                G_CALLBACK (gtk_combo_box_real_move_active),
                                NULL, NULL,
                                g_cclosure_marshal_VOID__ENUM,
                                G_TYPE_NONE, 1,
                                GTK_TYPE_SCROLL_TYPE);
554

555 556 557 558 559 560 561 562 563 564 565 566
  /**
   * GtkComboBox::popup:
   * @widget: the object that received the signal
   *
   * The ::popup signal is a 
   * <link linkend="keybinding-signals">keybinding signal</link>
   * which gets emitted to popup the combo box list.
   *
   * The default binding for this signal is Alt+Down.
   *
   * Since: 2.12
   */
567
  combo_box_signals[POPUP] =
568 569 570 571 572 573 574
    g_signal_new_class_handler (I_("popup"),
                                G_OBJECT_CLASS_TYPE (klass),
                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                                G_CALLBACK (gtk_combo_box_real_popup),
                                NULL, NULL,
                                g_cclosure_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);
575 576 577 578 579 580 581 582 583 584 585 586
  /**
   * GtkComboBox::popdown:
   * @button: the object which received the signal
   *
   * The ::popdown signal is a 
   * <link linkend="keybinding-signals">keybinding signal</link> 
   * which gets emitted to popdown the combo box list.
   *
   * The default bindings for this signal are Alt+Up and Escape.
   *
   * Since: 2.12
   */
587
  combo_box_signals[POPDOWN] =
588 589 590 591 592 593 594
    g_signal_new_class_handler (I_("popdown"),
                                G_OBJECT_CLASS_TYPE (klass),
                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                                G_CALLBACK (gtk_combo_box_real_popdown),
                                NULL, NULL,
                                _gtk_marshal_BOOLEAN__VOID,
                                G_TYPE_BOOLEAN, 0);
595

596 597 598 599 600
  /* key bindings */
  binding_set = gtk_binding_set_by_class (widget_class);

  gtk_binding_entry_add_signal (binding_set, GDK_Down, GDK_MOD1_MASK,
				"popup", 0);
601 602 603 604 605 606 607 608 609
  gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, GDK_MOD1_MASK,
				"popup", 0);

  gtk_binding_entry_add_signal (binding_set, GDK_Up, GDK_MOD1_MASK,
				"popdown", 0);
  gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, GDK_MOD1_MASK,
				"popdown", 0);
  gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
				"popdown", 0);
610 611

  gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
612
				"move-active", 1,
613 614
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
  gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, 0,
615
				"move-active", 1,
616 617
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
  gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
618
				"move-active", 1,
619 620
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
  gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, 0,
621
				"move-active", 1,
622 623
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
  gtk_binding_entry_add_signal (binding_set, GDK_Home, 0,
624
				"move-active", 1,
625 626
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
  gtk_binding_entry_add_signal (binding_set, GDK_KP_Home, 0,
627
				"move-active", 1,
628 629 630
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);

  gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
631
				"move-active", 1,
632 633
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
  gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, 0,
634
				"move-active", 1,
635 636
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
  gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
637
				"move-active", 1,
638 639
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
  gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, 0,
640
				"move-active", 1,
641 642
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
  gtk_binding_entry_add_signal (binding_set, GDK_End, 0,
643
				"move-active", 1,
644 645
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
  gtk_binding_entry_add_signal (binding_set, GDK_KP_End, 0,
646
				"move-active", 1,
647 648
				GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);

649
  /* properties */
Matthias Clasen's avatar
Matthias Clasen committed
650 651 652 653 654 655 656 657
  /**
   * GtkComboBox:model:
   *
   * The model from which the combo box takes the values shown
   * in the list. 
   *
   * Since: 2.4
   */
658 659 660
  g_object_class_install_property (object_class,
                                   PROP_MODEL,
                                   g_param_spec_object ("model",
661 662
                                                        P_("ComboBox model"),
                                                        P_("The model for the combo box"),
663
                                                        GTK_TYPE_TREE_MODEL,
664
                                                        GTK_PARAM_READWRITE));
665

Matthias Clasen's avatar
Matthias Clasen committed
666 667 668 669 670 671 672 673 674
  /**
   * GtkComboBox:wrap-width:
   *
   * If wrap-width is set to a positive value, the list will be
   * displayed in multiple columns, the number of columns is
   * determined by wrap-width.
   *
   * Since: 2.4
   */
675 676
  g_object_class_install_property (object_class,
                                   PROP_WRAP_WIDTH,
677
                                   g_param_spec_int ("wrap-width",
678
                                                     P_("Wrap width"),
679
                                                     P_("Wrap width for laying out the items in a grid"),
680 681 682
                                                     0,
                                                     G_MAXINT,
                                                     0,
683
                                                     GTK_PARAM_READWRITE));
684

Matthias Clasen's avatar
Matthias Clasen committed
685 686 687 688 689 690 691

  /**
   * GtkComboBox:row-span-column:
   *
   * If this is set to a non-negative value, it must be the index of a column 
   * of type %G_TYPE_INT in the model. 
   *
Matthias Clasen's avatar
Matthias Clasen committed
692 693 694
   * The values of that column are used to determine how many rows a value in 
   * the list will span. Therefore, the values in the model column pointed to 
   * by this property must be greater than zero and not larger than wrap-width.
Matthias Clasen's avatar
Matthias Clasen committed
695 696 697
   *
   * Since: 2.4
   */
698 699
  g_object_class_install_property (object_class,
                                   PROP_ROW_SPAN_COLUMN,
700
                                   g_param_spec_int ("row-span-column",
701 702
                                                     P_("Row span column"),
                                                     P_("TreeModel column containing the row span values"),
703
                                                     -1,
704
                                                     G_MAXINT,
705
                                                     -1,
706
                                                     GTK_PARAM_READWRITE));
707

Matthias Clasen's avatar
Matthias Clasen committed
708 709 710 711 712 713 714 715 716 717 718 719

  /**
   * GtkComboBox:column-span-column:
   *
   * If this is set to a non-negative value, it must be the index of a column 
   * of type %G_TYPE_INT in the model. 
   *
   * The values of that column are used to determine how many columns a value 
   * in the list will span. 
   *
   * Since: 2.4
   */
720 721
  g_object_class_install_property (object_class,
                                   PROP_COLUMN_SPAN_COLUMN,
722
                                   g_param_spec_int ("column-span-column",
723 724
                                                     P_("Column span column"),
                                                     P_("TreeModel column containing the column span values"),
725
                                                     -1,
726
                                                     G_MAXINT,
727
                                                     -1,
728
                                                     GTK_PARAM_READWRITE));
729

730

Matthias Clasen's avatar
Matthias Clasen committed
731 732 733 734 735
  /**
   * GtkComboBox:active:
   *
   * The item which is currently active. If the model is a non-flat treemodel,
   * and the active item is not an immediate child of the root of the tree,
Matthias Clasen's avatar
Matthias Clasen committed
736 737
   * this property has the value 
   * <literal>gtk_tree_path_get_indices (path)[0]</literal>,
Matthias Clasen's avatar
Matthias Clasen committed
738 739 740 741
   * where <literal>path</literal> is the #GtkTreePath of the active item.
   *
   * Since: 2.4
   */
742 743 744
  g_object_class_install_property (object_class,
                                   PROP_ACTIVE,
                                   g_param_spec_int ("active",
745 746
                                                     P_("Active item"),
                                                     P_("The item which is currently active"),
747
                                                     -1,
748
                                                     G_MAXINT,
749
                                                     -1,
750
                                                     GTK_PARAM_READWRITE));
751

752 753 754
  /**
   * GtkComboBox:add-tearoffs:
   *
Matthias Clasen's avatar
Matthias Clasen committed
755
   * The add-tearoffs property controls whether generated menus 
756 757 758 759 760 761 762 763 764 765
   * have tearoff menu items. 
   *
   * Note that this only affects menu style combo boxes.
   *
   * Since: 2.6
   */
  g_object_class_install_property (object_class,
                                   PROP_ADD_TEAROFFS,
				   g_param_spec_boolean ("add-tearoffs",
							 P_("Add tearoffs to menus"),
766
							 P_("Whether dropdowns should have a tearoff menu item"),
767
							 FALSE,
768
							 GTK_PARAM_READWRITE));
769
  
770 771 772
  /**
   * GtkComboBox:has-frame:
   *
Matthias Clasen's avatar
Matthias Clasen committed
773
   * The has-frame property controls whether a frame
774 775 776 777 778 779 780 781 782 783
   * is drawn around the entry.
   *
   * Since: 2.6
   */
  g_object_class_install_property (object_class,
                                   PROP_HAS_FRAME,
				   g_param_spec_boolean ("has-frame",
							 P_("Has Frame"),
							 P_("Whether the combo box draws a frame around the child"),
							 TRUE,
784
							 GTK_PARAM_READWRITE));
785
  
Matthias Clasen's avatar
Matthias Clasen committed
786 787
  g_object_class_install_property (object_class,
                                   PROP_FOCUS_ON_CLICK,
788
                                   g_param_spec_boolean ("focus-on-click",
Matthias Clasen's avatar
Matthias Clasen committed
789 790 791
							 P_("Focus on click"),
							 P_("Whether the combo box grabs focus when it is clicked with the mouse"),
							 TRUE,
792
							 GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
793

794 795 796 797 798 799 800 801 802 803 804 805 806
  /**
   * GtkComboBox:tearoff-title:
   *
   * A title that may be displayed by the window manager 
   * when the popup is torn-off.
   *
   * Since: 2.10
   */
  g_object_class_install_property (object_class,
                                   PROP_TEAROFF_TITLE,
                                   g_param_spec_string ("tearoff-title",
                                                        P_("Tearoff Title"),
                                                        P_("A title that may be displayed by the window manager when the popup is torn-off"),
807
                                                        NULL,
808 809 810
                                                        GTK_PARAM_READWRITE));


811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
  /**
   * GtkComboBox:popup-shown:
   *
   * Whether the combo boxes dropdown is popped up. 
   * Note that this property is mainly useful, because
   * it allows you to connect to notify::popup-shown.
   *
   * Since: 2.10
   */
  g_object_class_install_property (object_class,
                                   PROP_POPUP_SHOWN,
                                   g_param_spec_boolean ("popup-shown",
                                                         P_("Popup shown"),
                                                         P_("Whether the combo's dropdown is shown"),
                                                         FALSE,
                                                         GTK_PARAM_READABLE));
827

828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845

   /**
    * GtkComboBox:button-sensitivity:
    *
    * Whether the dropdown button is sensitive when
    * the model is empty.
    *
    * Since: 2.14
    */
   g_object_class_install_property (object_class,
                                    PROP_BUTTON_SENSITIVITY,
                                    g_param_spec_enum ("button-sensitivity",
                                                       P_("Button Sensitivity"),
                                                       P_("Whether the dropdown button is sensitive when the model is empty"),
                                                       GTK_TYPE_SENSITIVITY_TYPE,
                                                       GTK_SENSITIVITY_AUTO,
                                                       GTK_PARAM_READWRITE));

846
  gtk_widget_class_install_style_property (widget_class,
847 848
                                           g_param_spec_boolean ("appears-as-list",
                                                                 P_("Appears as list"),
849
                                                                 P_("Whether dropdowns should look like lists rather than menus"),
850
                                                                 FALSE,
851
                                                                 GTK_PARAM_READABLE));
852

853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
  /**
   * GtkComboBox:arrow-size:
   *
   * Sets the minimum size of the arrow in the combo box.  Note
   * that the arrow size is coupled to the font size, so in case
   * a larger font is used, the arrow will be larger than set
   * by arrow size.
   *
   * Since: 2.12
   */
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("arrow-size",
							     P_("Arrow Size"),
							     P_("The minimum size of the arrow in the combo box"),
							     0,
							     G_MAXINT,
							     15,
							     GTK_PARAM_READABLE));

872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
  /**
   * GtkComboBox:shadow-type:
   *
   * Which kind of shadow to draw around the combo box.
   *
   * Since: 2.12
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_enum ("shadow-type",
                                                              P_("Shadow type"),
                                                              P_("Which kind of shadow to draw around the combo box"),
                                                              GTK_TYPE_SHADOW_TYPE,
                                                              GTK_SHADOW_NONE,
                                                              GTK_PARAM_READABLE));

887 888 889
  g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
}

890 891 892 893
static void
gtk_combo_box_buildable_init (GtkBuildableIface *iface)
{
  parent_buildable_iface = g_type_interface_peek_parent (iface);
894
  iface->add_child = _gtk_cell_layout_buildable_add_child;
895 896 897 898
  iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
  iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
}

899 900 901 902 903
static void
gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
{
  iface->pack_start = gtk_combo_box_cell_layout_pack_start;
  iface->pack_end = gtk_combo_box_cell_layout_pack_end;
904
  iface->get_cells = gtk_combo_box_cell_layout_get_cells;
905 906 907 908
  iface->clear = gtk_combo_box_cell_layout_clear;
  iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
  iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
  iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
Kristian Rietveld's avatar
Kristian Rietveld committed
909
  iface->reorder = gtk_combo_box_cell_layout_reorder;
910 911
}

912 913 914 915 916 917
static void
gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
{
  iface->start_editing = gtk_combo_box_start_editing;
}

918 919 920
static void
gtk_combo_box_init (GtkComboBox *combo_box)
{
921
  GtkComboBoxPrivate *priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box);
922

923 924 925 926
  priv->cell_view = gtk_cell_view_new ();
  gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (combo_box));
  GTK_BIN (combo_box)->child = priv->cell_view;
  gtk_widget_show (priv->cell_view);
927

928 929 930
  priv->width = 0;
  priv->height = 0;
  priv->wrap_width = 0;
931

932
  priv->active = -1;
933 934 935
  priv->active_row = NULL;
  priv->col_column = -1;
  priv->row_column = -1;
936

937 938 939 940 941 942 943
  priv->popup_shown = FALSE;
  priv->add_tearoffs = FALSE;
  priv->has_frame = TRUE;
  priv->is_cell_renderer = FALSE;
  priv->editing_canceled = FALSE;
  priv->auto_scroll = FALSE;
  priv->focus_on_click = TRUE;
944
  priv->button_sensitivity = GTK_SENSITIVITY_AUTO;
945 946

  combo_box->priv = priv;
947 948

  gtk_combo_box_check_appearance (combo_box);
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
}

static void
gtk_combo_box_set_property (GObject      *object,
                            guint         prop_id,
                            const GValue *value,
                            GParamSpec   *pspec)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (object);

  switch (prop_id)
    {
      case PROP_MODEL:
        gtk_combo_box_set_model (combo_box, g_value_get_object (value));
        break;

      case PROP_WRAP_WIDTH:
        gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
        break;

      case PROP_ROW_SPAN_COLUMN:
        gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
        break;

      case PROP_COLUMN_SPAN_COLUMN:
        gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
        break;

      case PROP_ACTIVE:
        gtk_combo_box_set_active (combo_box, g_value_get_int (value));
        break;

981 982 983 984
      case PROP_ADD_TEAROFFS:
        gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
        break;

985 986 987 988
      case PROP_HAS_FRAME:
        combo_box->priv->has_frame = g_value_get_boolean (value);
        break;

Matthias Clasen's avatar
Matthias Clasen committed
989
      case PROP_FOCUS_ON_CLICK:
990 991
	gtk_combo_box_set_focus_on_click (combo_box, 
					  g_value_get_boolean (value));
Matthias Clasen's avatar
Matthias Clasen committed
992 993
        break;

994 995 996 997
      case PROP_TEAROFF_TITLE:
	gtk_combo_box_set_title (combo_box, g_value_get_string (value));
        break;

998 999
      case PROP_POPUP_SHOWN:
        if (g_value_get_boolean (value))
1000
          gtk_combo_box_popup (combo_box);
1001
        else
1002
          gtk_combo_box_popdown (combo_box);
1003 1004
        break;

1005 1006 1007 1008 1009
      case PROP_BUTTON_SENSITIVITY:
        gtk_combo_box_set_button_sensitivity (combo_box,
                                              g_value_get_enum (value));
        break;

1010
      default:
1011
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
        break;
    }
}

static void
gtk_combo_box_get_property (GObject    *object,
                            guint       prop_id,
                            GValue     *value,
                            GParamSpec *pspec)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (object);

  switch (prop_id)
    {
      case PROP_MODEL:
        g_value_set_object (value, combo_box->priv->model);
        break;

      case PROP_WRAP_WIDTH:
        g_value_set_int (value, combo_box->priv->wrap_width);
        break;

      case PROP_ROW_SPAN_COLUMN:
        g_value_set_int (value, combo_box->priv->row_column);
        break;

      case PROP_COLUMN_SPAN_COLUMN:
        g_value_set_int (value, combo_box->priv->col_column);
        break;

      case PROP_ACTIVE:
        g_value_set_int (value, gtk_combo_box_get_active (combo_box));
        break;

1046 1047 1048 1049
      case PROP_ADD_TEAROFFS:
        g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
        break;

1050 1051 1052 1053
      case PROP_HAS_FRAME:
        g_value_set_boolean (value, combo_box->priv->has_frame);
        break;

Matthias Clasen's avatar
Matthias Clasen committed
1054 1055 1056 1057
      case PROP_FOCUS_ON_CLICK:
        g_value_set_boolean (value, combo_box->priv->focus_on_click);
        break;

1058 1059 1060 1061
      case PROP_TEAROFF_TITLE:
        g_value_set_string (value, gtk_combo_box_get_title (combo_box));
        break;

1062 1063 1064 1065
      case PROP_POPUP_SHOWN:
        g_value_set_boolean (value, combo_box->priv->popup_shown);
        break;

1066 1067 1068 1069
      case PROP_BUTTON_SENSITIVITY:
        g_value_set_enum (value, combo_box->priv->button_sensitivity);
        break;

1070 1071 1072 1073 1074 1075
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

1076 1077 1078 1079 1080
static void
gtk_combo_box_state_changed (GtkWidget    *widget,
			     GtkStateType  previous)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1081
  GtkComboBoxPrivate *priv = combo_box->priv;
1082 1083 1084

  if (GTK_WIDGET_REALIZED (widget))
    {
1085 1086
      if (priv->tree_view && priv->cell_view)
	gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view), 
1087 1088 1089 1090 1091 1092
					    &widget->style->base[GTK_WIDGET_STATE (widget)]);
    }

  gtk_widget_queue_draw (widget);
}

1093 1094 1095 1096 1097 1098
static void
gtk_combo_box_button_state_changed (GtkWidget    *widget,
				    GtkStateType  previous,
				    gpointer      data)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1099
  GtkComboBoxPrivate *priv = combo_box->priv;
1100 1101 1102

  if (GTK_WIDGET_REALIZED (widget))
    {
1103
      if (!priv->tree_view && priv->cell_view)
1104 1105
	{
	  if ((GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE) !=
1106 1107
	      (GTK_WIDGET_STATE (priv->cell_view) == GTK_STATE_INSENSITIVE))
	    gtk_widget_set_sensitive (priv->cell_view, GTK_WIDGET_SENSITIVE (widget));
1108
	  
1109
	  gtk_widget_set_state (priv->cell_view, 
1110 1111
				GTK_WIDGET_STATE (widget));
	}
1112 1113 1114 1115 1116
    }

  gtk_widget_queue_draw (widget);
}

1117
static void
1118
gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1119
{
1120
  GtkComboBoxPrivate *priv = combo_box->priv;
1121
  gboolean appears_as_list;
1122 1123 1124 1125

  /* if wrap_width > 0, then we are in grid-mode and forced to use
   * unix style
   */
1126
  if (priv->wrap_width)
1127 1128 1129 1130 1131
    appears_as_list = FALSE;
  else
    gtk_widget_style_get (GTK_WIDGET (combo_box),
			  "appears-as-list", &appears_as_list,
			  NULL);
1132

1133
  if (appears_as_list)
1134
    {
1135
      /* Destroy all the menu mode widgets, if they exist. */
1136
      if (GTK_IS_MENU (priv->popup_widget))
1137 1138 1139
	gtk_combo_box_menu_destroy (combo_box);

      /* Create the list mode widgets, if they don't already exist. */
1140
      if (!GTK_IS_TREE_VIEW (priv->tree_view))
1141
	gtk_combo_box_list_setup (combo_box);
1142 1143 1144
    }
  else
    {
1145
      /* Destroy all the list mode widgets, if they exist. */
1146
      if (GTK_IS_TREE_VIEW (priv->tree_view))
1147 1148 1149
	gtk_combo_box_list_destroy (combo_box);

      /* Create the menu mode widgets, if they don't already exist. */
1150
      if (!GTK_IS_MENU (priv->popup_widget))
1151
	gtk_combo_box_menu_setup (combo_box, TRUE);
1152
    }
1153 1154

  gtk_widget_style_get (GTK_WIDGET (combo_box),
1155
			"shadow-type", &priv->shadow_type,
1156
			NULL);
1157 1158 1159 1160 1161 1162 1163
}

static void
gtk_combo_box_style_set (GtkWidget *widget,
                         GtkStyle  *previous)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1164
  GtkComboBoxPrivate *priv = combo_box->priv;
1165 1166

  gtk_combo_box_check_appearance (combo_box);
1167

1168 1169
  if (priv->tree_view && priv->cell_view)
    gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view), 
1170
					&widget->style->base[GTK_WIDGET_STATE (widget)]);
1171 1172 1173

  if (GTK_IS_ENTRY (GTK_BIN (combo_box)->child))
    g_object_set (GTK_BIN (combo_box)->child, "shadow-type",
1174
                  GTK_SHADOW_NONE == priv->shadow_type ?
1175
                  GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
}

static void
gtk_combo_box_button_toggled (GtkWidget *widget,
                              gpointer   data)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (data);

  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
    {
      if (!combo_box->priv->popup_in_progress)
        gtk_combo_box_popup (combo_box);
    }
  else
    gtk_combo_box_popdown (combo_box);
}

static void
gtk_combo_box_add (GtkContainer *container,
                   GtkWidget    *widget)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1198
  GtkComboBoxPrivate *priv = combo_box->priv;
1199

1200
  if (priv->cell_view && priv->cell_view->parent)
1201
    {
1202
      gtk_widget_unparent (priv->cell_view);
1203 1204 1205 1206 1207 1208
      GTK_BIN (container)->child = NULL;
      gtk_widget_queue_resize (GTK_WIDGET (container));
    }
  
  gtk_widget_set_parent (widget, GTK_WIDGET (container));
  GTK_BIN (container)->child = widget;
1209

1210 1211
  if (priv->cell_view &&
      widget != priv->cell_view)
1212 1213
    {
      /* since the cell_view was unparented, it's gone now */
1214
      priv->cell_view = NULL;
1215

1216
      if (!priv->tree_view && priv->separator)
1217
        {
1218 1219 1220
	  gtk_container_remove (GTK_CONTAINER (priv->separator->parent),
				priv->separator);
	  priv->separator = NULL;
1221 1222 1223

          gtk_widget_queue_resize (GTK_WIDGET (container));
        }
1224
      else if (priv->cell_view_frame)
1225
        {
1226 1227 1228
          gtk_widget_unparent (priv->cell_view_frame);
          priv->cell_view_frame = NULL;
          priv->box = NULL;
1229 1230 1231 1232
        }
    }
}

1233 1234 1235 1236 1237
static void
gtk_combo_box_remove (GtkContainer *container,
		      GtkWidget    *widget)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1238
  GtkComboBoxPrivate *priv = combo_box->priv;
1239
  GtkTreePath *path;
1240 1241
  gboolean appears_as_list;

1242 1243
  if (widget == priv->cell_view)
    priv->cell_view = NULL;
1244

1245 1246 1247
  gtk_widget_unparent (widget);
  GTK_BIN (container)->child = NULL;

1248
  if (GTK_OBJECT_FLAGS (combo_box) & GTK_IN_DESTRUCTION)
1249 1250 1251 1252
    return;

  gtk_widget_queue_resize (GTK_WIDGET (container));

1253
  if (!priv->tree_view)
1254 1255 1256 1257 1258 1259
    appears_as_list = FALSE;
  else
    appears_as_list = TRUE;
  
  if (appears_as_list)
    gtk_combo_box_list_destroy (combo_box);
1260
  else if (GTK_IS_MENU (priv->popup_widget))
1261 1262
    {
      gtk_combo_box_menu_destroy (combo_box);
1263 1264
      gtk_menu_detach (GTK_MENU (priv->popup_widget));
      priv->popup_widget = NULL;
1265 1266
    }

1267
  if (!priv->cell_view)
1268
    {
1269 1270 1271
      priv->cell_view = gtk_cell_view_new ();
      gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (container));
      GTK_BIN (container)->child = priv->cell_view;
1272
      
1273 1274 1275 1276
      gtk_widget_show (priv->cell_view);
      gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view),
			       priv->model);
      gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->cell_view));
1277 1278 1279 1280
    }


  if (appears_as_list)
1281
    gtk_combo_box_list_setup (combo_box); 
1282 1283 1284
  else
    gtk_combo_box_menu_setup (combo_box, TRUE);

1285
  if (gtk_tree_row_reference_valid (priv->active_row))
1286
    {
1287
      path = gtk_tree_row_reference_get_path (priv->active_row);
1288 1289 1290 1291 1292
      gtk_combo_box_set_active_internal (combo_box, path);
      gtk_tree_path_free (path);
    }
  else
    gtk_combo_box_set_active_internal (combo_box, NULL);
1293 1294
}

1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
static ComboCellInfo *
gtk_combo_box_get_cell_info (GtkComboBox     *combo_box,
                             GtkCellRenderer *cell)
{
  GSList *i;

  for (i = combo_box->priv->cells; i; i = i->next)
    {
      ComboCellInfo *info = (ComboCellInfo *)i->data;

1305
      if (info && info->cell == cell)
1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316
        return info;
    }

  return NULL;
}

static void
gtk_combo_box_menu_show (GtkWidget *menu,
                         gpointer   user_data)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1317
  GtkComboBoxPrivate *priv = combo_box->priv;
1318

1319 1320
  gtk_combo_box_child_show (menu, user_data);

1321 1322
  priv->popup_in_progress = TRUE;
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1323
                                TRUE);
1324
  priv->popup_in_progress = FALSE;
1325 1326 1327 1328 1329 1330 1331 1332
}

static void
gtk_combo_box_menu_hide (GtkWidget *menu,
                         gpointer   user_data)
{
  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);

1333
  gtk_combo_box_child_hide (menu,user_data);
1334

1335 1336 1337 1338
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
                                FALSE);
}

1339 1340 1341 1342
static void
gtk_combo_box_detacher (GtkWidget *widget,
			GtkMenu	  *menu)
{
1343 1344
  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
  GtkComboBoxPrivate *priv = combo_box->priv;
1345

1346
  g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
1347

1348
  g_signal_handlers_disconnect_by_func (menu->toplevel,
1349 1350
					gtk_combo_box_menu_show,
					combo_box);
1351
  g_signal_handlers_disconnect_by_func (menu->toplevel,
1352 1353 1354
					gtk_combo_box_menu_hide,
					combo_box);
  
1355
  priv->popup_widget = NULL;
1356 1357
}

1358 1359 1360 1361
static void
gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
                                GtkWidget   *popup)
{
1362 1363 1364
  GtkComboBoxPrivate *priv = combo_box->priv;

  if (GTK_IS_MENU (priv->popup_widget))
1365
    {
1366 1367
      gtk_menu_detach (GTK_MENU (priv->popup_widget));
      priv->popup_widget = NULL;
1368
    }
1369
  else if (priv->popup_widget)
1370
    {
1371 1372 1373 1374
      gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
                            priv->popup_widget);
      g_object_unref (priv->popup_widget);
      priv->popup_widget = NULL;
1375 1376 1377 1378
    }

  if (GTK_IS_MENU (popup))
    {
1379
      if (priv->popup_window)
1380
        {
1381 1382
          gtk_widget_destroy (priv->popup_window);
          priv->popup_window = NULL;
1383 1384
        }

1385
      priv->popup_widget = popup;
1386

1387 1388 1389 1390 1391 1392
      /* 
       * Note that we connect to show/hide on the toplevel, not the
       * menu itself, since the menu is not shown/hidden when it is
       * popped up while torn-off.
       */
      g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1393
                        G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1394
      g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1395 1396
                        G_CALLBACK (gtk_combo_box_menu_hide), combo_box);

1397 1398 1399
      gtk_menu_attach_to_widget (GTK_MENU (popup),
				 GTK_WIDGET (combo_box),
				 gtk_combo_box_detacher);
1400 1401 1402
    }
  else
    {
1403
      if (!priv->popup_window)
1404
        {
1405 1406
	  GtkWidget *toplevel;
	  
1407 1408
          priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
          gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
1409

1410
	  gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
1411 1412
				    GDK_WINDOW_TYPE_HINT_COMBO);

1413
	  g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
1414 1415
			    G_CALLBACK (gtk_combo_box_child_show),
			    combo_box);
1416
	  g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
1417 1418 1419
			    G_CALLBACK (gtk_combo_box_child_hide),
			    combo_box);