gtktreemenu.c 60.8 KB
Newer Older
1 2 3 4 5 6 7
/* gtktreemenu.c
 *
 * Copyright (C) 2010 Openismus GmbH
 *
 * Authors:
 *      Tristan Van Berkom <tristanvb@openismus.com>
 *
8 9
 * Based on some GtkComboBox menu code by Kristian Rietveld <kris@gtk.org>
 *
10 11 12 13 14 15 16 17 18 19 20
 * 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.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardón's avatar
Javier Jardón committed
21
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 23
 */

24
/*
25 26 27 28
 * SECTION:gtktreemenu
 * @Short_Description: A GtkMenu automatically created from a #GtkTreeModel
 * @Title: GtkTreeMenu
 *
29
 * The #GtkTreeMenu is used to display a drop-down menu allowing selection
30 31 32 33
 * of every row in the model and is used by the #GtkComboBox for its drop-down
 * menu.
 */

34 35 36
#include "config.h"
#include "gtkintl.h"
#include "gtktreemenu.h"
37
#include "gtkmarshalers.h"
38 39 40 41 42 43
#include "gtkmenuitem.h"
#include "gtkseparatormenuitem.h"
#include "gtkcellareabox.h"
#include "gtkcellareacontext.h"
#include "gtkcelllayout.h"
#include "gtkcellview.h"
44
#include "gtkmenushellprivate.h"
45 46
#include "gtkprivate.h"

47 48 49 50 51
#undef GDK_DEPRECATED
#undef GDK_DEPRECATED_FOR
#define GDK_DEPRECATED
#define GDK_DEPRECATED_FOR(f)

52
#include "deprecated/gtktearoffmenuitem.h"
53 54

/* GObjectClass */
55
static void      gtk_tree_menu_constructed                    (GObject            *object);
56 57 58
static void      gtk_tree_menu_dispose                        (GObject            *object);
static void      gtk_tree_menu_finalize                       (GObject            *object);
static void      gtk_tree_menu_set_property                   (GObject            *object,
59 60 61
                                                               guint               prop_id,
                                                               const GValue       *value,
                                                               GParamSpec         *pspec);
62
static void      gtk_tree_menu_get_property                   (GObject            *object,
63 64 65
                                                               guint               prop_id,
                                                               GValue             *value,
                                                               GParamSpec         *pspec);
66 67 68

/* GtkWidgetClass */
static void      gtk_tree_menu_get_preferred_width            (GtkWidget           *widget,
69 70
                                                               gint                *minimum_size,
                                                               gint                *natural_size);
71
static void      gtk_tree_menu_get_preferred_height           (GtkWidget           *widget,
72 73
                                                               gint                *minimum_size,
                                                               gint                *natural_size);
74 75 76 77 78 79 80 81
static void      gtk_tree_menu_get_preferred_width_for_height (GtkWidget           *widget,
                                                               gint                 for_height,
                                                               gint                *minimum_size,
                                                               gint                *natural_size);
static void      gtk_tree_menu_get_preferred_height_for_width (GtkWidget           *widget,
                                                               gint                 for_width,
                                                               gint                *minimum_size,
                                                               gint                *natural_size);
82 83 84 85 86 87 88

/* GtkCellLayoutIface */
static void      gtk_tree_menu_cell_layout_init               (GtkCellLayoutIface  *iface);
static GtkCellArea *gtk_tree_menu_cell_layout_get_area        (GtkCellLayout        *layout);


/* TreeModel/DrawingArea callbacks and building menus/submenus */
89 90
static inline void rebuild_menu                               (GtkTreeMenu          *menu);
static gboolean   menu_occupied                               (GtkTreeMenu          *menu,
91 92 93 94
                                                               guint                 left_attach,
                                                               guint                 right_attach,
                                                               guint                 top_attach,
                                                               guint                 bottom_attach);
95
static void       relayout_item                               (GtkTreeMenu          *menu,
96 97 98
                                                               GtkWidget            *item,
                                                               GtkTreeIter          *iter,
                                                               GtkWidget            *prev);
99
static void       gtk_tree_menu_populate                      (GtkTreeMenu          *menu);
100
static GtkWidget *gtk_tree_menu_create_item                   (GtkTreeMenu          *menu,
101 102
                                                               GtkTreeIter          *iter,
                                                               gboolean              header_item);
103
static void       gtk_tree_menu_create_submenu                (GtkTreeMenu          *menu,
104 105
                                                               GtkWidget            *item,
                                                               GtkTreePath          *path);
106
static void       gtk_tree_menu_set_area                      (GtkTreeMenu          *menu,
107
                                                               GtkCellArea          *area);
108
static GtkWidget *gtk_tree_menu_get_path_item                 (GtkTreeMenu          *menu,
109
                                                               GtkTreePath          *path);
110
static gboolean   gtk_tree_menu_path_in_menu                  (GtkTreeMenu          *menu,
111 112
                                                               GtkTreePath          *path,
                                                               gboolean             *header_item);
113
static void       row_inserted_cb                             (GtkTreeModel         *model,
114 115 116
                                                               GtkTreePath          *path,
                                                               GtkTreeIter          *iter,
                                                               GtkTreeMenu          *menu);
117
static void       row_deleted_cb                              (GtkTreeModel         *model,
118 119
                                                               GtkTreePath          *path,
                                                               GtkTreeMenu          *menu);
120
static void       row_reordered_cb                            (GtkTreeModel         *model,
121 122 123 124
                                                               GtkTreePath          *path,
                                                               GtkTreeIter          *iter,
                                                               gint                 *new_order,
                                                               GtkTreeMenu          *menu);
125
static void       row_changed_cb                              (GtkTreeModel         *model,
126 127 128
                                                               GtkTreePath          *path,
                                                               GtkTreeIter          *iter,
                                                               GtkTreeMenu          *menu);
129
static void       context_size_changed_cb                     (GtkCellAreaContext   *context,
130 131
                                                               GParamSpec           *pspec,
                                                               GtkWidget            *menu);
132
static void       area_apply_attributes_cb                    (GtkCellArea          *area,
133 134 135 136 137
                                                               GtkTreeModel         *tree_model,
                                                               GtkTreeIter          *iter,
                                                               gboolean              is_expander,
                                                               gboolean              is_expanded,
                                                               GtkTreeMenu          *menu);
138
static void       item_activated_cb                           (GtkMenuItem          *item,
139
                                                               GtkTreeMenu          *menu);
140
static void       submenu_activated_cb                        (GtkTreeMenu          *submenu,
141 142
                                                               const gchar          *path,
                                                               GtkTreeMenu          *menu);
143
static void       gtk_tree_menu_set_model_internal            (GtkTreeMenu          *menu,
144
                                                               GtkTreeModel         *model);
145

146 147


148 149 150 151 152 153 154 155 156
struct _GtkTreeMenuPrivate
{
  /* TreeModel and parent for this menu */
  GtkTreeModel        *model;
  GtkTreeRowReference *root;

  /* CellArea and context for this menu */
  GtkCellArea         *area;
  GtkCellAreaContext  *context;
157

158 159
  /* Signals */
  gulong               size_changed_id;
160
  gulong               apply_attributes_id;
161 162 163
  gulong               row_inserted_id;
  gulong               row_deleted_id;
  gulong               row_reordered_id;
164 165 166 167 168 169 170 171 172 173
  gulong               row_changed_id;

  /* Grid menu mode */
  gint                 wrap_width;
  gint                 row_span_col;
  gint                 col_span_col;

  /* Flags */
  guint32              menu_with_header : 1;
  guint32              tearoff     : 1;
174 175 176 177 178 179 180 181 182 183 184

  /* Row separators */
  GtkTreeViewRowSeparatorFunc row_separator_func;
  gpointer                    row_separator_data;
  GDestroyNotify              row_separator_destroy;
};

enum {
  PROP_0,
  PROP_MODEL,
  PROP_ROOT,
185
  PROP_CELL_AREA,
186 187 188 189
  PROP_TEAROFF,
  PROP_WRAP_WIDTH,
  PROP_ROW_SPAN_COL,
  PROP_COL_SPAN_COL
190 191
};

192 193 194 195 196
enum {
  SIGNAL_MENU_ACTIVATE,
  N_SIGNALS
};

197 198
static guint   tree_menu_signals[N_SIGNALS] = { 0 };
static GQuark  tree_menu_path_quark = 0;
199

200
G_DEFINE_TYPE_WITH_CODE (GtkTreeMenu, _gtk_tree_menu, GTK_TYPE_MENU,
201
                         G_ADD_PRIVATE (GtkTreeMenu)
202 203
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
                                                gtk_tree_menu_cell_layout_init));
204 205

static void
206
_gtk_tree_menu_init (GtkTreeMenu *menu)
207
{
208 209 210
  menu->priv = _gtk_tree_menu_get_instance_private (menu);
  menu->priv->row_span_col = -1;
  menu->priv->col_span_col = -1;
211

212
  gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
213 214
}

215
static void
216
_gtk_tree_menu_class_init (GtkTreeMenuClass *class)
217 218 219 220
{
  GObjectClass   *object_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);

221 222
  tree_menu_path_quark = g_quark_from_static_string ("gtk-tree-menu-path");

223
  object_class->constructed  = gtk_tree_menu_constructed;
224 225 226 227 228
  object_class->dispose      = gtk_tree_menu_dispose;
  object_class->finalize     = gtk_tree_menu_finalize;
  object_class->set_property = gtk_tree_menu_set_property;
  object_class->get_property = gtk_tree_menu_get_property;

229 230
  widget_class->get_preferred_width  = gtk_tree_menu_get_preferred_width;
  widget_class->get_preferred_height = gtk_tree_menu_get_preferred_height;
231 232
  widget_class->get_preferred_width_for_height  = gtk_tree_menu_get_preferred_width_for_height;
  widget_class->get_preferred_height_for_width = gtk_tree_menu_get_preferred_height_for_width;
233

234
  /*
235 236 237 238 239 240 241 242 243 244 245
   * GtkTreeMenu::menu-activate:
   * @menu: a #GtkTreeMenu
   * @path: the #GtkTreePath string for the item which was activated
   * @user_data: the user data
   *
   * This signal is emitted to notify that a menu item in the #GtkTreeMenu
   * was activated and provides the path string from the #GtkTreeModel
   * to specify which row was selected.
   *
   * Since: 3.0
   */
246 247
  tree_menu_signals[SIGNAL_MENU_ACTIVATE] =
    g_signal_new (I_("menu-activate"),
248 249 250 251
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST,
                  0, /* No class closure here */
                  NULL, NULL,
252
                  NULL,
253
                  G_TYPE_NONE, 1, G_TYPE_STRING);
254

255
  /*
256 257 258
   * GtkTreeMenu:model:
   *
   * The #GtkTreeModel from which the menu is constructed.
259
   *
260 261
   * Since: 3.0
   */
262 263 264 265 266 267 268 269
  g_object_class_install_property (object_class,
                                   PROP_MODEL,
                                   g_param_spec_object ("model",
                                                        P_("TreeMenu model"),
                                                        P_("The model for the tree menu"),
                                                        GTK_TYPE_TREE_MODEL,
                                                        GTK_PARAM_READWRITE));

270
  /*
271 272 273 274 275 276 277 278 279 280
   * GtkTreeMenu:root:
   *
   * The #GtkTreePath that is the root for this menu, or %NULL.
   *
   * The #GtkTreeMenu recursively creates submenus for #GtkTreeModel
   * rows that have children and the "root" for each menu is provided
   * by the parent menu.
   *
   * If you dont provide a root for the #GtkTreeMenu then the whole
   * model will be added to the menu. Specifying a root allows you
281
   * to build a menu for a given #GtkTreePath and its children.
282 283 284
   * 
   * Since: 3.0
   */
285 286 287
  g_object_class_install_property (object_class,
                                   PROP_ROOT,
                                   g_param_spec_boxed ("root",
288 289 290 291 292
                                                       P_("TreeMenu root row"),
                                                       P_("The TreeMenu will display children of the "
                                                          "specified root"),
                                                       GTK_TYPE_TREE_PATH,
                                                       GTK_PARAM_READWRITE));
293

294
  /*
295 296 297 298 299 300 301 302 303
   * GtkTreeMenu:cell-area:
   *
   * The #GtkCellArea used to render cells in the menu items.
   *
   * You can provide a different cell area at object construction
   * time, otherwise the #GtkTreeMenu will use a #GtkCellAreaBox.
   *
   * Since: 3.0
   */
304
  g_object_class_install_property (object_class,
305 306 307 308 309 310
                                   PROP_CELL_AREA,
                                   g_param_spec_object ("cell-area",
                                                        P_("Cell Area"),
                                                        P_("The GtkCellArea used to layout cells"),
                                                        GTK_TYPE_CELL_AREA,
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
311

312
  /*
313 314 315 316 317 318
   * GtkTreeMenu:tearoff:
   *
   * Specifies whether this menu comes with a leading tearoff menu item
   *
   * Since: 3.0
   */
319
  g_object_class_install_property (object_class,
320 321 322 323 324 325
                                   PROP_TEAROFF,
                                   g_param_spec_boolean ("tearoff",
                                                         P_("Tearoff"),
                                                         P_("Whether the menu has a tearoff item"),
                                                         FALSE,
                                                         GTK_PARAM_READWRITE));
326

327
  /*
328 329
   * GtkTreeMenu:wrap-width:
   *
330 331
   * If wrap-width is set to a positive value, items in the popup will be laid
   * out along multiple columns, starting a new row on reaching the wrap width.
332 333 334 335
   *
   * Since: 3.0
   */
  g_object_class_install_property (object_class,
336 337 338 339 340 341 342 343
                                   PROP_WRAP_WIDTH,
                                   g_param_spec_int ("wrap-width",
                                                     P_("Wrap Width"),
                                                     P_("Wrap width for laying out items in a grid"),
                                                     0,
                                                     G_MAXINT,
                                                     0,
                                                     GTK_PARAM_READWRITE));
344

345
  /*
346 347
   * GtkTreeMenu:row-span-column:
   *
348
   * If this is set to a non-negative value, it must be the index of a column
349 350 351
   * of type %G_TYPE_INT in the model. The value in that column for each item
   * will determine how many rows that item will span in the popup. Therefore,
   * values in this column must be greater than zero.
352 353 354 355
   *
   * Since: 3.0
   */
  g_object_class_install_property (object_class,
356 357 358 359 360 361 362 363
                                   PROP_ROW_SPAN_COL,
                                   g_param_spec_int ("row-span-column",
                                                     P_("Row span column"),
                                                     P_("TreeModel column containing the row span values"),
                                                     -1,
                                                     G_MAXINT,
                                                     -1,
                                                     GTK_PARAM_READWRITE));
364

365
  /*
366 367
   * GtkTreeMenu:column-span-column:
   *
368
   * If this is set to a non-negative value, it must be the index of a column
369 370 371 372
   * of type %G_TYPE_INT in the model. The value in that column for each item
   * will determine how many columns that item will span in the popup.
   * Therefore, values in this column must be greater than zero, and the sum of
   * an item’s column position + span should not exceed #GtkTreeMenu:wrap-width.
373 374 375 376
   *
   * Since: 3.0
   */
  g_object_class_install_property (object_class,
377 378 379 380 381 382 383 384
                                   PROP_COL_SPAN_COL,
                                   g_param_spec_int ("column-span-column",
                                                     P_("Column span column"),
                                                     P_("TreeModel column containing the column span values"),
                                                     -1,
                                                     G_MAXINT,
                                                     -1,
                                                     GTK_PARAM_READWRITE));
385 386 387 388 389
}

/****************************************************************
 *                         GObjectClass                         *
 ****************************************************************/
390 391
static void
gtk_tree_menu_constructed (GObject *object)
392
{
393 394
  GtkTreeMenu *menu = GTK_TREE_MENU (object);
  GtkTreeMenuPrivate *priv = menu->priv;
395

396
  G_OBJECT_CLASS (_gtk_tree_menu_parent_class)->constructed (object);
397 398 399 400 401 402 403 404 405

  if (!priv->area)
    {
      GtkCellArea *area = gtk_cell_area_box_new ();

      gtk_tree_menu_set_area (menu, area);
    }

  priv->context = gtk_cell_area_create_context (priv->area);
406

407
  priv->size_changed_id =
408
    g_signal_connect (priv->context, "notify",
409
                      G_CALLBACK (context_size_changed_cb), menu);
410 411 412 413 414 415 416 417 418 419 420
}

static void
gtk_tree_menu_dispose (GObject *object)
{
  GtkTreeMenu        *menu;
  GtkTreeMenuPrivate *priv;

  menu = GTK_TREE_MENU (object);
  priv = menu->priv;

421
  _gtk_tree_menu_set_model (menu, NULL);
422 423 424 425 426 427 428 429 430 431 432 433
  gtk_tree_menu_set_area (menu, NULL);

  if (priv->context)
    {
      /* Disconnect signals */
      g_signal_handler_disconnect (priv->context, priv->size_changed_id);

      g_object_unref (priv->context);
      priv->context = NULL;
      priv->size_changed_id = 0;
    }

434
  G_OBJECT_CLASS (_gtk_tree_menu_parent_class)->dispose (object);
435 436 437 438 439
}

static void
gtk_tree_menu_finalize (GObject *object)
{
440 441 442 443 444 445
  GtkTreeMenu        *menu;
  GtkTreeMenuPrivate *priv;

  menu = GTK_TREE_MENU (object);
  priv = menu->priv;

446
  _gtk_tree_menu_set_row_separator_func (menu, NULL, NULL, NULL);
447

448
  if (priv->root)
449
    gtk_tree_row_reference_free (priv->root);
450

451
  G_OBJECT_CLASS (_gtk_tree_menu_parent_class)->finalize (object);
452 453 454 455
}

static void
gtk_tree_menu_set_property (GObject            *object,
456 457 458
                            guint               prop_id,
                            const GValue       *value,
                            GParamSpec         *pspec)
459 460 461 462 463 464
{
  GtkTreeMenu *menu = GTK_TREE_MENU (object);

  switch (prop_id)
    {
    case PROP_MODEL:
465
      _gtk_tree_menu_set_model (menu, g_value_get_object (value));
466 467 468
      break;

    case PROP_ROOT:
469
      _gtk_tree_menu_set_root (menu, g_value_get_boxed (value));
470 471 472 473 474 475 476
      break;

    case PROP_CELL_AREA:
      /* Construct-only, can only be assigned once */
      gtk_tree_menu_set_area (menu, (GtkCellArea *)g_value_get_object (value));
      break;

477
    case PROP_TEAROFF:
478
      _gtk_tree_menu_set_tearoff (menu, g_value_get_boolean (value));
479 480
      break;

481
    case PROP_WRAP_WIDTH:
482
      _gtk_tree_menu_set_wrap_width (menu, g_value_get_int (value));
483 484 485
      break;

     case PROP_ROW_SPAN_COL:
486
      _gtk_tree_menu_set_row_span_column (menu, g_value_get_int (value));
487 488 489
      break;

     case PROP_COL_SPAN_COL:
490
      _gtk_tree_menu_set_column_span_column (menu, g_value_get_int (value));
491 492
      break;

493 494 495 496 497 498 499 500
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_tree_menu_get_property (GObject            *object,
501 502 503
                            guint               prop_id,
                            GValue             *value,
                            GParamSpec         *pspec)
504 505 506 507 508 509
{
  GtkTreeMenu        *menu = GTK_TREE_MENU (object);
  GtkTreeMenuPrivate *priv = menu->priv;

  switch (prop_id)
    {
510 511 512
    case PROP_MODEL:
      g_value_set_object (value, priv->model);
      break;
513

514 515 516
    case PROP_ROOT:
      g_value_set_boxed (value, priv->root);
      break;
517

518 519 520
    case PROP_CELL_AREA:
      g_value_set_object (value, priv->area);
      break;
521

522 523 524
    case PROP_TEAROFF:
      g_value_set_boolean (value, priv->tearoff);
      break;
525

526 527 528
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
    }
}

/****************************************************************
 *                         GtkWidgetClass                       *
 ****************************************************************/

/* We tell all the menu items to reserve space for the submenu
 * indicator if there is at least one submenu, this way we ensure
 * that every internal cell area gets allocated the
 * same width (and requested height for the same appropriate width).
 */
static void
sync_reserve_submenu_size (GtkTreeMenu *menu)
{
  GList              *children, *l;
  gboolean            has_submenu = FALSE;

  children = gtk_container_get_children (GTK_CONTAINER (menu));
  for (l = children; l; l = l->next)
    {
      GtkMenuItem *item = l->data;

      if (gtk_menu_item_get_submenu (item) != NULL)
553 554 555 556
        {
          has_submenu = TRUE;
          break;
        }
557 558 559 560 561 562 563 564 565 566 567 568 569 570
    }

  for (l = children; l; l = l->next)
    {
      GtkMenuItem *item = l->data;

      gtk_menu_item_set_reserve_indicator (item, has_submenu);
    }

  g_list_free (children);
}

static void
gtk_tree_menu_get_preferred_width (GtkWidget           *widget,
571 572
                                   gint                *minimum_size,
                                   gint                *natural_size)
573 574 575 576
{
  GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
  GtkTreeMenuPrivate *priv = menu->priv;

577 578 579 580 581 582
  /* We leave the requesting work up to the cellviews which operate in the same
   * context, reserving space for the submenu indicator if any of the items have
   * submenus ensures that every cellview will receive the same allocated width.
   *
   * Since GtkMenu does hieght-for-width correctly, we know that the width of
   * every cell will be requested before the height-for-widths are requested.
583
   */
584
  g_signal_handler_block (priv->context, priv->size_changed_id);
585 586 587

  sync_reserve_submenu_size (menu);

588
  GTK_WIDGET_CLASS (_gtk_tree_menu_parent_class)->get_preferred_width (widget, minimum_size, natural_size);
589 590 591 592 593 594

  g_signal_handler_unblock (priv->context, priv->size_changed_id);
}

static void
gtk_tree_menu_get_preferred_height (GtkWidget           *widget,
595 596
                                    gint                *minimum_size,
                                    gint                *natural_size)
597 598 599 600 601 602 603 604
{
  GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
  GtkTreeMenuPrivate *priv = menu->priv;

  g_signal_handler_block (priv->context, priv->size_changed_id);

  sync_reserve_submenu_size (menu);

605
  GTK_WIDGET_CLASS (_gtk_tree_menu_parent_class)->get_preferred_height (widget, minimum_size, natural_size);
606 607 608 609

  g_signal_handler_unblock (priv->context, priv->size_changed_id);
}

610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
static void
gtk_tree_menu_get_preferred_width_for_height (GtkWidget           *widget,
                                              gint                 for_height,
                                              gint                *minimum_size,
                                              gint                *natural_size)
{
  GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
  GtkTreeMenuPrivate *priv = menu->priv;

  /* We leave the requesting work up to the cellviews which operate in the same
   * context, reserving space for the submenu indicator if any of the items have
   * submenus ensures that every cellview will receive the same allocated width.
   *
   * Since GtkMenu does hieght-for-width correctly, we know that the width of
   * every cell will be requested before the height-for-widths are requested.
   */
  g_signal_handler_block (priv->context, priv->size_changed_id);

  sync_reserve_submenu_size (menu);

  GTK_WIDGET_CLASS (_gtk_tree_menu_parent_class)->get_preferred_width_for_height (widget, for_height, minimum_size, natural_size);

  g_signal_handler_unblock (priv->context, priv->size_changed_id);
}

static void
gtk_tree_menu_get_preferred_height_for_width (GtkWidget           *widget,
                                              gint                 for_width,
                                              gint                *minimum_size,
                                              gint                *natural_size)
{
  GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
  GtkTreeMenuPrivate *priv = menu->priv;

  g_signal_handler_block (priv->context, priv->size_changed_id);

  sync_reserve_submenu_size (menu);

  GTK_WIDGET_CLASS (_gtk_tree_menu_parent_class)->get_preferred_height_for_width (widget, for_width, minimum_size, natural_size);

  g_signal_handler_unblock (priv->context, priv->size_changed_id);
}

653 654 655 656 657 658
/****************************************************************
 *                      GtkCellLayoutIface                      *
 ****************************************************************/
static void
gtk_tree_menu_cell_layout_init (GtkCellLayoutIface  *iface)
{
659
  iface->get_area = gtk_tree_menu_cell_layout_get_area;
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
}

static GtkCellArea *
gtk_tree_menu_cell_layout_get_area (GtkCellLayout *layout)
{
  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
  GtkTreeMenuPrivate *priv = menu->priv;

  return priv->area;
}


/****************************************************************
 *             TreeModel callbacks/populating menus             *
 ****************************************************************/
675 676
static GtkWidget *
gtk_tree_menu_get_path_item (GtkTreeMenu          *menu,
677
                             GtkTreePath          *search)
678 679 680 681 682 683 684 685 686 687 688 689
{
  GtkWidget *item = NULL;
  GList     *children, *l;

  children = gtk_container_get_children (GTK_CONTAINER (menu));

  for (l = children; item == NULL && l != NULL; l = l->next)
    {
      GtkWidget   *child = l->data;
      GtkTreePath *path  = NULL;

      if (GTK_IS_SEPARATOR_MENU_ITEM (child))
690 691 692 693 694 695 696 697 698
        {
          GtkTreeRowReference *row =
            g_object_get_qdata (G_OBJECT (child), tree_menu_path_quark);

          if (row)
            {
              path = gtk_tree_row_reference_get_path (row);

              if (!path)
699
                /* Return any first child where its row-reference became invalid,
700 701 702 703 704 705
                 * this is because row-references get null paths before we recieve
                 * the "row-deleted" signal.
                 */
                item = child;
            }
        }
706
      else if (!GTK_IS_TEAROFF_MENU_ITEM (child))
707 708
        {
          GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
709

710 711 712
          /* It's always a cellview */
          if (GTK_IS_CELL_VIEW (view))
            path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view));
713

714
          if (!path)
715
            /* Return any first child where its row-reference became invalid,
716 717 718 719 720
             * this is because row-references get null paths before we recieve
             * the "row-deleted" signal.
             */
            item = child;
        }
721 722

      if (path)
723 724 725
        {
          if (gtk_tree_path_compare (search, path) == 0)
            item = child;
726

727 728
          gtk_tree_path_free (path);
        }
729 730 731 732 733 734 735
    }

  g_list_free (children);

  return item;
}

736 737
static gboolean
gtk_tree_menu_path_in_menu (GtkTreeMenu  *menu,
738 739
                            GtkTreePath  *path,
                            gboolean     *header_item)
740 741
{
  GtkTreeMenuPrivate *priv = menu->priv;
742 743
  gboolean            in_menu = FALSE;
  gboolean            is_header = FALSE;
744

745
  /* Check if the is in root of the model */
746 747
  if (gtk_tree_path_get_depth (path) == 1 && !priv->root)
    in_menu = TRUE;
748
  /* If we are a submenu, compare the parent path */
749
  else if (priv->root)
750
    {
751
      GtkTreePath *root_path   = gtk_tree_row_reference_get_path (priv->root);
752
      GtkTreePath *search_path = gtk_tree_path_copy (path);
753

754
      if (root_path)
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
        {
          if (priv->menu_with_header &&
              gtk_tree_path_compare (root_path, search_path) == 0)
            {
              in_menu   = TRUE;
              is_header = TRUE;
            }
          else if (gtk_tree_path_get_depth (search_path) > 1)
            {
              gtk_tree_path_up (search_path);

              if (gtk_tree_path_compare (root_path, search_path) == 0)
                in_menu = TRUE;
            }
        }
770
      gtk_tree_path_free (root_path);
771
      gtk_tree_path_free (search_path);
772 773
    }

774 775 776 777 778 779
  if (header_item)
    *header_item = is_header;

  return in_menu;
}

780
static GtkWidget *
781 782
gtk_tree_menu_path_needs_submenu (GtkTreeMenu *menu,
                                  GtkTreePath *search)
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
{
  GtkWidget   *item = NULL;
  GList       *children, *l;
  GtkTreePath *parent_path;

  if (gtk_tree_path_get_depth (search) <= 1)
    return NULL;

  parent_path = gtk_tree_path_copy (search);
  gtk_tree_path_up (parent_path);

  children    = gtk_container_get_children (GTK_CONTAINER (menu));

  for (l = children; item == NULL && l != NULL; l = l->next)
    {
      GtkWidget   *child = l->data;
      GtkTreePath *path  = NULL;

      /* Separators dont get submenus, if it already has a submenu then let
       * the submenu handle inserted rows */
803 804 805 806
      if (!GTK_IS_SEPARATOR_MENU_ITEM (child) &&
          !gtk_menu_item_get_submenu (GTK_MENU_ITEM (child)))
        {
          GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
807

808 809 810 811
          /* It's always a cellview */
          if (GTK_IS_CELL_VIEW (view))
            path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view));
        }
812 813

      if (path)
814 815 816
        {
          if (gtk_tree_path_compare (parent_path, path) == 0)
            item = child;
817

818 819
          gtk_tree_path_free (path);
        }
820 821 822 823 824 825 826 827
    }

  g_list_free (children);
  gtk_tree_path_free (parent_path);

  return item;
}

828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
static GtkWidget *
find_empty_submenu (GtkTreeMenu  *menu)
{
  GtkTreeMenuPrivate *priv = menu->priv;
  GList              *children, *l;
  GtkWidget          *submenu = NULL;

  children = gtk_container_get_children (GTK_CONTAINER (menu));

  for (l = children; submenu == NULL && l != NULL; l = l->next)
    {
      GtkWidget   *child = l->data;
      GtkTreePath *path  = NULL;
      GtkTreeIter  iter;

      /* Separators dont get submenus, if it already has a submenu then let
       * the submenu handle inserted rows */
      if (!GTK_IS_SEPARATOR_MENU_ITEM (child) && !GTK_IS_TEAROFF_MENU_ITEM (child))
846 847
        {
          GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
848

849 850 851 852
          /* It's always a cellview */
          if (GTK_IS_CELL_VIEW (view))
            path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view));
        }
853 854

      if (path)
855 856 857 858
        {
          if (gtk_tree_model_get_iter (priv->model, &iter, path) &&
              !gtk_tree_model_iter_has_child (priv->model, &iter))
            submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (child));
859

860 861
          gtk_tree_path_free (path);
        }
862 863 864 865 866 867 868
    }

  g_list_free (children);

  return submenu;
}

869 870
static void
row_inserted_cb (GtkTreeModel     *model,
871 872 873
                 GtkTreePath      *path,
                 GtkTreeIter      *iter,
                 GtkTreeMenu      *menu)
874 875 876
{
  GtkTreeMenuPrivate *priv = menu->priv;
  gint               *indices, index, depth;
877
  GtkWidget          *item;
878

879
  /* If the iter should be in this menu then go ahead and insert it */
880
  if (gtk_tree_menu_path_in_menu (menu, path, NULL))
881
    {
882
      if (priv->wrap_width > 0)
883
        rebuild_menu (menu);
884
      else
885 886 887 888 889 890
        {
          /* Get the index of the path for this depth */
          indices = gtk_tree_path_get_indices (path);
          depth   = gtk_tree_path_get_depth (path);
          index   = indices[depth -1];

891
          /* Menus with a header include a menuitem for its root node
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
           * and a separator menu item */
          if (priv->menu_with_header)
            index += 2;

          /* Index after the tearoff item for the root menu if
           * there is a tearoff item
           */
          if (priv->root == NULL && priv->tearoff)
            index += 1;

          item = gtk_tree_menu_create_item (menu, iter, FALSE);
          gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, index);

          /* Resize everything */
          gtk_cell_area_context_reset (menu->priv->context);
        }
908
    }
909 910 911 912 913
  else
    {
      /* Create submenus for iters if we need to */
      item = gtk_tree_menu_path_needs_submenu (menu, path);
      if (item)
914 915
        {
          GtkTreePath *item_path = gtk_tree_path_copy (path);
916

917 918 919 920
          gtk_tree_path_up (item_path);
          gtk_tree_menu_create_submenu (menu, item, item_path);
          gtk_tree_path_free (item_path);
        }
921
    }
922 923 924 925
}

static void
row_deleted_cb (GtkTreeModel     *model,
926 927
                GtkTreePath      *path,
                GtkTreeMenu      *menu)
928 929 930 931
{
  GtkTreeMenuPrivate *priv = menu->priv;
  GtkWidget          *item;

932
  /* If it's the header item we leave it to the parent menu
933 934
   * to remove us from its menu
   */
935 936 937
  item = gtk_tree_menu_get_path_item (menu, path);

  if (item)
938
    {
939
      if (priv->wrap_width > 0)
940
        rebuild_menu (menu);
941
      else
942 943 944 945 946 947 948
        {
          /* Get rid of the deleted item */
          gtk_widget_destroy (item);

          /* Resize everything */
          gtk_cell_area_context_reset (menu->priv->context);
        }
949
    }
950
  else
951 952 953 954 955
    {
      /* It's up to the parent menu to destroy a child menu that becomes empty
       * since the topmost menu belongs to the user and is allowed to have no contents */
      GtkWidget *submenu = find_empty_submenu (menu);
      if (submenu)
956
        gtk_widget_destroy (submenu);
957
    }
958 959 960 961
}

static void
row_reordered_cb (GtkTreeModel    *model,
962 963 964 965
                  GtkTreePath     *path,
                  GtkTreeIter     *iter,
                  gint            *new_order,
                  GtkTreeMenu     *menu)
966 967 968 969
{
  GtkTreeMenuPrivate *priv = menu->priv;
  gboolean            this_menu = FALSE;

970
  if (gtk_tree_path_get_depth (path) == 0 && !priv->root)
971 972 973 974
    this_menu = TRUE;
  else if (priv->root)
    {
      GtkTreePath *root_path =
975
        gtk_tree_row_reference_get_path (priv->root);
976 977

      if (gtk_tree_path_compare (root_path, path) == 0)
978
        this_menu = TRUE;
979 980 981 982 983

      gtk_tree_path_free (root_path);
    }

  if (this_menu)
984 985 986
    rebuild_menu (menu);
}

987
static gint
988
menu_item_position (GtkTreeMenu *menu,
989
                    GtkWidget   *item)
990 991 992 993 994 995 996 997 998 999
{
  GList *children, *l;
  gint   position;

  children = gtk_container_get_children (GTK_CONTAINER (menu));
  for (position = 0, l = children; l; position++, l = l->next)
    {
      GtkWidget *iitem = l->data;

      if (item == iitem)
1000
        break;
1001 1002 1003 1004 1005 1006 1007 1008 1009
    }

  g_list_free (children);

  return position;
}

static void
row_changed_cb (GtkTreeModel         *model,
1010 1011 1012
                GtkTreePath          *path,
                GtkTreeIter          *iter,
                GtkTreeMenu          *menu)
1013 1014 1015 1016 1017 1018 1019 1020
{
  GtkTreeMenuPrivate *priv = menu->priv;
  gboolean            is_separator = FALSE;
  GtkWidget          *item;

  item = gtk_tree_menu_get_path_item (menu, path);

  if (priv->root)
1021
    {
1022
      GtkTreePath *root_path =
1023 1024
        gtk_tree_row_reference_get_path (priv->root);

1025
      if (root_path && gtk_tree_path_compare (root_path, path) == 0)
1026
        {
1027
          if (item)
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
            {
              /* Destroy the header item and then the following separator */
              gtk_widget_destroy (item);
              gtk_widget_destroy (GTK_MENU_SHELL (menu)->priv->children->data);

              priv->menu_with_header = FALSE;
            }

          gtk_tree_path_free (root_path);
        }
1038
    }
1039

1040 1041 1042
  if (item)
    {
      if (priv->wrap_width > 0)
1043 1044 1045 1046
        /* Ugly, we need to rebuild the menu here if
         * the row-span/row-column values change
         */
        rebuild_menu (menu);
1047
      else
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
        {
          if (priv->row_separator_func)
            is_separator =
              priv->row_separator_func (model, iter,
                                        priv->row_separator_data);


          if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
            {
              gint position = menu_item_position (menu, item);

              gtk_widget_destroy (item);
              item = gtk_tree_menu_create_item (menu, iter, FALSE);
              gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, position);
            }
        }
1064 1065 1066
    }
}

1067 1068
static void
context_size_changed_cb (GtkCellAreaContext  *context,
1069 1070
                         GParamSpec          *pspec,
                         GtkWidget           *menu)
1071 1072 1073 1074 1075
{
  if (!strcmp (pspec->name, "minimum-width") ||
      !strcmp (pspec->name, "natural-width") ||
      !strcmp (pspec->name, "minimum-height") ||
      !strcmp (pspec->name, "natural-height"))
1076
    gtk_widget_queue_resize (menu);
1077 1078
}

1079 1080 1081 1082 1083
static gboolean
area_is_sensitive (GtkCellArea *area)
{
  GList    *cells, *list;
  gboolean  sensitive = FALSE;
1084

1085 1086 1087 1088 1089
  cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));

  for (list = cells; list; list = list->next)
    {
      g_object_get (list->data, "sensitive", &sensitive, NULL);
1090

1091
      if (sensitive)
1092
        break;
1093 1094 1095 1096 1097 1098 1099 1100
    }
  g_list_free (cells);

  return sensitive;
}

static void
area_apply_attributes_cb (GtkCellArea          *area,
1101 1102 1103 1104 1105
                          GtkTreeModel         *tree_model,
                          GtkTreeIter          *iter,
                          gboolean              is_expander,
                          gboolean              is_expanded,
                          GtkTreeMenu          *menu)
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
{
  /* If the menu for this iter has a submenu */
  GtkTreeMenuPrivate *priv = menu->priv;
  GtkTreePath        *path;
  GtkWidget          *item;
  gboolean            is_header;
  gboolean            sensitive;

  path = gtk_tree_model_get_path (tree_model, iter);

  if (gtk_tree_menu_path_in_menu (menu, path, &is_header))
    {
      item = gtk_tree_menu_get_path_item (menu, path);

      /* If there is no submenu, go ahead and update item sensitivity,
       * items with submenus are always sensitive */
1122
      if (item && !gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)))
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
        {
          sensitive = area_is_sensitive (priv->area);

          gtk_widget_set_sensitive (item, sensitive);

          if (is_header)
            {
              /* For header items we need to set the sensitivity
               * of the following separator item
               */
              if (GTK_MENU_SHELL (menu)->priv->children &&
                  GTK_MENU_SHELL (menu)->priv->children->next)
                {
                  GtkWidget *separator =
                    GTK_MENU_SHELL (menu)->priv->children->next->data;

                  gtk_widget_set_sensitive (separator, sensitive);
                }
            }
        }
1143 1144 1145 1146 1147
    }

  gtk_tree_path_free (path);
}

1148 1149
static void
gtk_tree_menu_set_area (GtkTreeMenu *menu,
1150
                        GtkCellArea *area)
1151 1152 1153 1154
{
  GtkTreeMenuPrivate *priv = menu->priv;

  if (priv->area)
1155 1156
    {
      g_signal_handler_disconnect (priv->area,
1157
                                   priv->apply_attributes_id);
1158 1159 1160 1161
      priv->apply_attributes_id = 0;

      g_object_unref (priv->area);
    }
1162 1163 1164 1165

  priv->area = area;

  if (priv->area)
1166 1167 1168 1169
    {
      g_object_ref_sink (priv->area);

      priv->apply_attributes_id =
1170 1171
        g_signal_connect (priv->area, "apply-attributes",
                          G_CALLBACK (area_apply_attributes_cb), menu);
1172
    }
1173 1174
}

1175 1176 1177 1178 1179 1180 1181 1182 1183
static gboolean
menu_occupied (GtkTreeMenu *menu,
               guint        left_attach,
               guint        right_attach,
               guint        top_attach,
               guint        bottom_attach)
{
  GList *i;

1184
  for (i = GTK_MENU_SHELL (menu)->priv->children; i; i = i->next)
1185 1186 1187
    {
      guint l, r, b, t;

1188 1189
      gtk_container_child_get (GTK_CONTAINER (menu),
                               i->data,
1190 1191 1192 1193 1194 1195 1196 1197
                               "left-attach", &l,
                               "right-attach", &r,
                               "bottom-attach", &b,
                               "top-attach", &t,
                               NULL);

      /* look if this item intersects with the given coordinates */
      if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
1198
        return TRUE;
1199 1200 1201 1202 1203 1204 1205
    }

  return FALSE;
}

static void
relayout_item (GtkTreeMenu *menu,
1206 1207 1208
               GtkWidget   *item,
               GtkTreeIter *iter,
               GtkWidget   *prev)
1209 1210 1211 1212
{
  GtkTreeMenuPrivate *priv = menu->priv;
  gint                current_col = 0, current_row = 0;
  gint                rows = 1, cols = 1;
1213

1214 1215 1216 1217 1218
  if (priv->col_span_col == -1 &&
      priv->row_span_col == -1 &&
      prev)
    {
      gtk_container_child_get (GTK_CONTAINER (menu), prev,
1219 1220 1221
                               "right-attach", &current_col,
                               "top-attach", &current_row,
                               NULL);
1222
      if (current_col + cols > priv->wrap_width)
1223 1224 1225 1226
        {
          current_col = 0;
          current_row++;
        }
1227 1228 1229 1230
    }
  else
    {
      if (priv->col_span_col != -1)
1231 1232 1233
        gtk_tree_model_get (priv->model, iter,
                            priv->col_span_col, &cols,
                            -1);
1234
      if (priv->row_span_col != -1)
1235 1236 1237
        gtk_tree_model_get (priv->model, iter,
                            priv->row_span_col, &rows,
                            -1);
1238 1239

      while (1)
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
        {
          if (current_col + cols > priv->wrap_width)
            {
              current_col = 0;
              current_row++;
            }

          if (!menu_occupied (menu,
                              current_col, current_col + cols,
                              current_row, current_row + rows))
            break;

          current_col++;
        }
1254 1255 1256 1257 1258 1259 1260 1261
    }

  /* set attach props */
  gtk_menu_attach (GTK_MENU (menu), item,
                   current_col, current_col + cols,
                   current_row, current_row + rows);
}

1262 1263
static void
gtk_tree_menu_create_submenu (GtkTreeMenu *menu,
1264 1265
                              GtkWidget   *item,
                              GtkTreePath *path)
1266 1267 1268 1269 1270 1271 1272 1273
{
  GtkTreeMenuPrivate *priv = menu->priv;
  GtkWidget          *view;
  GtkWidget          *submenu;

  view = gtk_bin_get_child (GTK_BIN (item));
  gtk_cell_view_set_draw_sensitive (GTK_CELL_VIEW (view), TRUE);

1274
  submenu = _gtk_tree_menu_new_with_area (priv->area);
1275

1276 1277 1278 1279
  _gtk_tree_menu_set_row_separator_func (GTK_TREE_MENU (submenu),
                                         priv->row_separator_func,
                                         priv->row_separator_data,
                                         priv->row_separator_destroy);
1280

1281 1282 1283
  _gtk_tree_menu_set_wrap_width (GTK_TREE_MENU (submenu), priv->wrap_width);
  _gtk_tree_menu_set_row_span_column (GTK_TREE_MENU (submenu), priv->row_span_col);
  _gtk_tree_menu_set_column_span_column (GTK_TREE_MENU (submenu), priv->col_span_col);
1284

1285
  gtk_tree_menu_set_model_internal (GTK_TREE_MENU (submenu), priv->model);
1286
  _gtk_tree_menu_set_root (GTK_TREE_MENU (submenu), path);
1287
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1288 1289 1290

  g_signal_connect (submenu, "menu-activate",
                    G_CALLBACK (submenu_activated_cb), menu);
1291 1292
}

1293 1294
static GtkWidget *
gtk_tree_menu_create_item (GtkTreeMenu *menu,
1295 1296
                           GtkTreeIter *iter,
                           gboolean     header_item)
1297 1298 1299 1300
{
  GtkTreeMenuPrivate *priv = menu->priv;
  GtkWidget          *item, *view;
  GtkTreePath        *path;
1301
  gboolean            is_separator = FALSE;
1302 1303 1304

  path = gtk_tree_model_get_path (priv->model, iter);

1305
  if (priv->row_separator_func)
1306
    is_separator =
1307
      priv->row_separator_func (priv->model, iter,
1308
                                priv->row_separator_data);
1309

1310 1311 1312
  if (is_separator)
    {
      item = gtk_separator_menu_item_new ();
1313
      gtk_widget_show (item);
1314

1315
      g_object_set_qdata_full (G_OBJECT (item),
1316 1317 1318
                               tree_menu_path_quark,
                               gtk_tree_row_reference_new (priv->model, path),
                               (GDestroyNotify)gtk_tree_row_reference_free);
1319 1320 1321 1322 1323 1324 1325
    }
  else
    {
      view = gtk_cell_view_new_with_context (priv->area, priv->context);
      item = gtk_menu_item_new ();
      gtk_widget_show (view);
      gtk_widget_show (item);
1326

1327 1328
      gtk_cell_view_set_model (GTK_CELL_VIEW (view), priv->model);
      gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (view), path);
1329

1330 1331
      gtk_widget_show (view);
      gtk_container_add (GTK_CONTAINER (item), view);
1332

1333 1334 1335 1336
      g_signal_connect (item, "activate", G_CALLBACK (item_activated_cb), menu);

      /* Add a GtkTreeMenu submenu to render the children of this row */
      if (header_item == FALSE &&
1337 1338
          gtk_tree_model_iter_has_child (priv->model, iter))
        gtk_tree_menu_create_submenu (menu, item, path);
1339 1340 1341
    }

  gtk_tree_path_free (path);
1342

1343 1344 1345
  return item;
}

1346
static inline void
1347 1348 1349 1350 1351
rebuild_menu (GtkTreeMenu *menu)
{
  GtkTreeMenuPrivate *priv = menu->priv;

  /* Destroy all the menu items */
1352 1353 1354
  gtk_container_foreach (GTK_CONTAINER (menu),
                         (GtkCallback) gtk_widget_destroy, NULL);

1355 1356 1357 1358 1359 1360
  /* Populate */
  if (priv->model)
    gtk_tree_menu_populate (menu);
}


1361 1362 1363 1364 1365 1366 1367 1368
static void
gtk_tree_menu_populate (GtkTreeMenu *menu)
{
  GtkTreeMenuPrivate *priv = menu->priv;
  GtkTreePath        *path = NULL;
  GtkTreeIter         parent;
  GtkTreeIter         iter;
  gboolean            valid = FALSE;
1369
  GtkWidget          *menu_item, *prev = NULL;
1370 1371 1372 1373 1374 1375 1376 1377 1378 1379

  if (!priv->model)
    return;

  if (priv->root)
    path = gtk_tree_row_reference_get_path (priv->root);

  if (path)
    {
      if (gtk_tree_model_get_iter (priv->model, &parent, path))
1380
        valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
1381

1382 1383 1384
      gtk_tree_path_free (path);
    }
  else
1385 1386 1387
    {
      /* Tearoff menu items only go in the root menu */
      if (priv->tearoff)
1388 1389 1390
        {
          menu_item = gtk_tearoff_menu_item_new ();
          gtk_widget_show (menu_item);
1391

1392 1393 1394 1395
          if (priv->wrap_width > 0)
            gtk_menu_attach (GTK_MENU (menu), menu_item, 0, priv->wrap_width, 0, 1);
          else
            gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1396

1397 1398
          prev = menu_item;
        }
1399 1400 1401

      valid = gtk_tree_model_iter_children (priv->model, &iter, NULL);
    }
1402 1403 1404 1405 1406

  /* Create a menu item for every row at the current depth, add a GtkTreeMenu
   * submenu for iters/items that have children */
  while (valid)
    {
1407
      menu_item = gtk_tree_menu_create_item (menu, &iter, FALSE);
1408

1409 1410
      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);

1411
      if (priv->wrap_width > 0)
1412
        relayout_item (menu, menu_item, &iter, prev);
1413 1414

      prev  = menu_item;
1415 1416 1417 1418
      valid = gtk_tree_model_iter_next (priv->model, &iter);
    }
}

1419 1420
static void
item_activated_cb (GtkMenuItem          *item,
1421
                   GtkTreeMenu          *menu)
1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
{
  GtkCellView *view;
  GtkTreePath *path;
  gchar       *path_str;

  /* Only activate leafs, not parents */
  if (!gtk_menu_item_get_submenu (item))
    {
      view     = GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)));
      path     = gtk_cell_view_get_displayed_row (view);
      path_str = gtk_tree_path_to_string (path);
1433

1434
      g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path_str);
1435

1436 1437 1438 1439 1440 1441 1442
      g_free (path_str);
      gtk_tree_path_free (path);
    }
}

static void
submenu_activated_cb (GtkTreeMenu          *submenu,
1443 1444
                      const gchar          *path,
                      GtkTreeMenu          *menu)
1445 1446 1447 1448
{
  g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path);
}

1449 1450 1451 1452 1453
/* Sets the model without rebuilding the menu, prevents
 * infinite recursion while building submenus (we wait
 * until the root is set and then build the menu) */
static void
gtk_tree_menu_set_model_internal (GtkTreeMenu  *menu,
1454
                                  GtkTreeModel *model)
1455 1456 1457 1458 1459 1460 1461 1462
{
  GtkTreeMenuPrivate *priv;

  priv = menu->priv;

  if (priv->model != model)
    {
      if (priv->model)
1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479
        {
          /* Disconnect signals */
          g_signal_handler_disconnect (priv->model,
                                       priv->row_inserted_id);
          g_signal_handler_disconnect (priv->model,
                                       priv->row_deleted_id);
          g_signal_handler_disconnect (priv->model,
                                       priv->row_reordered_id);
          g_signal_handler_disconnect (priv->model,
                                       priv->row_changed_id);
          priv->row_inserted_id  = 0;
          priv->row_deleted_id   = 0;
          priv->row_reordered_id = 0;
          priv->row_changed_id = 0;

          g_object_unref (priv->model);
        }
1480 1481 1482 1483

      priv->model = model;

      if (priv->model)
1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
        {
          g_object_ref (priv->model);

          /* Connect signals */
          priv->row_inserted_id  = g_signal_connect (priv->model, "row-inserted",
                                                     G_CALLBACK (row_inserted_cb), menu);
          priv->row_deleted_id   = g_signal_connect (priv->model, "row-deleted",
                                                     G_CALLBACK (row_deleted_cb), menu);
          priv->row_reordered_id = g_signal_connect (priv->model, "rows-reordered",
                                                     G_CALLBACK (row_reordered_cb), menu);
          priv->row_changed_id   = g_signal_connect (priv->model, "row-changed",
                                                     G_CALLBACK (row_changed_cb), menu);
        }
1497 1498 1499
    }
}

1500 1501 1502
/****************************************************************
 *                            API                               *
 ****************************************************************/