gtkmodelmenuitem.c 13.5 KB
Newer Older
1
/*
2
 * Copyright © 2011, 2013 Canonical Limited
3 4 5 6 7 8 9 10 11 12 13 14
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the licence, 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 17 18 19 20 21 22 23
 *
 * Author: Ryan Lortie <desrt@desrt.ca>
 */

#include "config.h"

#include "gtkmodelmenuitem.h"

24
#include "gtkaccellabel.h"
25
#include "gtkcheckmenuitemprivate.h"
26 27
#include "gtkimage.h"
#include "gtkbox.h"
28

29 30 31
struct _GtkModelMenuItem
{
  GtkCheckMenuItem parent_instance;
32
  GtkMenuTrackerItemRole role;
33
  gboolean has_indicator;
34 35 36 37
};

typedef GtkCheckMenuItemClass GtkModelMenuItemClass;

38
G_DEFINE_TYPE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM)
39

40 41 42 43 44 45 46 47 48
enum
{
  PROP_0,
  PROP_ACTION_ROLE,
  PROP_ICON,
  PROP_TEXT,
  PROP_TOGGLED,
  PROP_ACCEL
};
49

50 51 52 53 54 55 56 57 58 59 60 61 62 63
static void
gtk_model_menu_item_toggle_size_request (GtkMenuItem *menu_item,
                                         gint        *requisition)
{
  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);

  if (item->has_indicator)
    GTK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
      ->toggle_size_request (menu_item, requisition);

  else
    *requisition = 0;
}

64 65 66 67 68 69
static void
gtk_model_menu_item_activate (GtkMenuItem *item)
{
  /* block the automatic toggle behaviour -- just do nothing */
}

70 71 72 73 74 75 76 77 78 79 80
static void
gtk_model_menu_item_draw_indicator (GtkCheckMenuItem *check_item,
                                    cairo_t          *cr)
{
  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (check_item);

  if (item->has_indicator)
    GTK_CHECK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
      ->draw_indicator (check_item, cr);
}

81
static void
82 83
gtk_model_menu_item_set_has_indicator (GtkModelMenuItem *item,
                                       gboolean          has_indicator)
84
{
85 86
  if (has_indicator == item->has_indicator)
    return;
87

88
  item->has_indicator = has_indicator;
89

90
  gtk_widget_queue_resize (GTK_WIDGET (item));
91 92 93
}

static void
94 95
gtk_model_menu_item_set_action_role (GtkModelMenuItem       *item,
                                     GtkMenuTrackerItemRole  role)
96
{
97 98
  AtkObject *accessible;
  AtkRole a11y_role;
99

100 101
  if (role == item->role)
    return;
102

103 104
  gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), role == GTK_MENU_TRACKER_ITEM_ROLE_RADIO);
  gtk_model_menu_item_set_has_indicator (item, role != GTK_MENU_TRACKER_ITEM_ROLE_NORMAL);
105

106 107
  accessible = gtk_widget_get_accessible (GTK_WIDGET (item));
  switch (role)
108
    {
109 110 111
    case GTK_MENU_TRACKER_ITEM_ROLE_NORMAL:
      a11y_role = ATK_ROLE_MENU_ITEM;
      break;
112

113 114 115
    case GTK_MENU_TRACKER_ITEM_ROLE_CHECK:
      a11y_role = ATK_ROLE_CHECK_MENU_ITEM;
      break;
116

117 118 119
    case GTK_MENU_TRACKER_ITEM_ROLE_RADIO:
      a11y_role = ATK_ROLE_RADIO_MENU_ITEM;
      break;
120

121 122 123
    default:
      g_assert_not_reached ();
    }
124

125
  atk_object_set_role (accessible, a11y_role);
126
  g_object_notify (G_OBJECT (item), "action-role");
127
}
128

129 130 131 132 133
static void
gtk_model_menu_item_set_icon (GtkModelMenuItem *item,
                              GIcon            *icon)
{
  GtkWidget *child;
134

135 136
  g_return_if_fail (GTK_IS_MODEL_MENU_ITEM (item));
  g_return_if_fail (icon == NULL || G_IS_ICON (icon));
137

138
  child = gtk_bin_get_child (GTK_BIN (item));
139

140 141 142 143 144 145 146 147 148 149 150 151 152 153
  /* There are only three possibilities here:
   *
   *   - no child
   *   - accel label child
   *   - already a box
   *
   * Handle the no-child case by having GtkMenuItem create the accel
   * label, then we will only have two possible cases.
   */
  if (child == NULL)
    {
      gtk_menu_item_get_label (GTK_MENU_ITEM (item));
      child = gtk_bin_get_child (GTK_BIN (item));
      g_assert (GTK_IS_LABEL (child));
154 155
    }

156 157 158
  /* If it is a box, make sure there are no images inside of it already.
   */
  if (GTK_IS_BOX (child))
159
    {
160 161 162 163 164 165 166
      GList *children;

      children = gtk_container_get_children (GTK_CONTAINER (child));
      while (children)
        {
          if (GTK_IS_IMAGE (children->data))
            gtk_widget_destroy (children->data);
167

168 169 170
          children = g_list_delete_link (children, children);
        }
    }
171
  else
172
    {
173
      GtkWidget *box;
174

175 176 177
      if (icon == NULL)
        return;

178
      box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
179

180 181 182 183 184
      /* Reparent the child without destroying it */
      g_object_ref (child);
      gtk_container_remove (GTK_CONTAINER (item), child);
      gtk_box_pack_end (GTK_BOX (box), child, TRUE, TRUE, 0);
      g_object_unref (child);
185

186 187
      gtk_container_add (GTK_CONTAINER (item), box);
      gtk_widget_show (box);
188

189 190
      /* Now we have a box */
      child = box;
191 192
    }

193 194 195 196 197 198
  g_assert (GTK_IS_BOX (child));

  /* child is now a box containing a label and no image.  Add the icon,
   * if appropriate.
   */
  if (icon != NULL)
199
    {
200
      GtkWidget *image;
201

202
      image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
203
      gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
204 205 206
      gtk_box_pack_start (GTK_BOX (child), image, FALSE, FALSE, 0);
      gtk_widget_show (image);
    }
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234

  g_object_notify (G_OBJECT (item), "icon");
}

static GIcon *
gtk_model_menu_item_get_icon (GtkModelMenuItem *item)
{
  GtkWidget *child;
  GIcon *icon = NULL;

  child = gtk_bin_get_child (GTK_BIN (item));
  if (GTK_IS_BOX (child))
    {
      GList *children, *l;

      children = gtk_container_get_children (GTK_CONTAINER (child));
      for (l = children; l; l = l->next)
        {
          if (GTK_IS_IMAGE (l->data))
            {
              gtk_image_get_gicon (GTK_IMAGE (l->data), &icon, NULL);
              break;
            }
        }
      g_list_free (children);
    }

  return icon;
235
}
236

237 238 239 240 241 242
static void
gtk_model_menu_item_set_text (GtkModelMenuItem *item,
                              const gchar      *text)
{
  GtkWidget *child;
  GList *children;
243

244 245 246 247 248 249 250
  child = gtk_bin_get_child (GTK_BIN (item));
  if (child == NULL)
    {
      gtk_menu_item_get_label (GTK_MENU_ITEM (item));
      child = gtk_bin_get_child (GTK_BIN (item));
      g_assert (GTK_IS_LABEL (child));
    }
251

252 253 254 255 256
  if (GTK_IS_LABEL (child))
    {
      gtk_label_set_text_with_mnemonic (GTK_LABEL (child), text);
      return;
    }
257

258 259
  if (!GTK_IS_CONTAINER (child))
    return;
260

261
  children = gtk_container_get_children (GTK_CONTAINER (child));
262

263 264 265 266
  while (children)
    {
      if (GTK_IS_LABEL (children->data))
        gtk_label_set_label (GTK_LABEL (children->data), text);
267

268 269
      children = g_list_delete_link (children, children);
    }
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303

  g_object_notify (G_OBJECT (item), "text");
}

static const gchar *
gtk_model_menu_item_get_text (GtkModelMenuItem *item)
{
  GtkWidget *child;

  child = gtk_bin_get_child (GTK_BIN (item));

  if (GTK_IS_LABEL (child))
    return gtk_label_get_text (GTK_LABEL (child));

  if (GTK_IS_CONTAINER (child))
    {
      GList *children, *l;
      const gchar *text = NULL;

      children = gtk_container_get_children (GTK_CONTAINER (child));
      for (l = children; l; l = l->next)
        {
          if (GTK_IS_LABEL (l->data))
            {
              text = gtk_label_get_text (GTK_LABEL (l->data));
              break;
            }
        }
      g_list_free (children);

      return text;
    }

  return NULL;
304
}
305

306 307 308 309 310 311 312 313
static void
gtk_model_menu_item_set_accel (GtkModelMenuItem *item,
                               const gchar      *accel)
{
  GtkWidget *child;
  GList *children;
  GdkModifierType modifiers;
  guint key;
314

315 316 317 318 319 320 321 322 323 324 325
  if (accel)
    {
      gtk_accelerator_parse (accel, &key, &modifiers);
      if (!key)
        modifiers = 0;
    }
  else
    {
      key = 0;
      modifiers = 0;
    }
326

327 328 329 330 331 332
  child = gtk_bin_get_child (GTK_BIN (item));
  if (child == NULL)
    {
      gtk_menu_item_get_label (GTK_MENU_ITEM (item));
      child = gtk_bin_get_child (GTK_BIN (item));
      g_assert (GTK_IS_LABEL (child));
333 334
    }

335
  if (GTK_IS_ACCEL_LABEL (child))
336 337 338 339
    {
      gtk_accel_label_set_accel (GTK_ACCEL_LABEL (child), key, modifiers);
      return;
    }
340

341
  if (!GTK_IS_CONTAINER (child))
342 343
    return;

344
  children = gtk_container_get_children (GTK_CONTAINER (child));
345

346 347 348 349 350 351 352
  while (children)
    {
      if (GTK_IS_ACCEL_LABEL (children->data))
        gtk_accel_label_set_accel (children->data, key, modifiers);

      children = g_list_delete_link (children, children);
    }
353 354
}

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
static gchar *
gtk_model_menu_item_get_accel (GtkModelMenuItem *item)
{
  GtkWidget *child;
  GtkWidget *accel_label = NULL;

  child = gtk_bin_get_child (GTK_BIN (item));

  if (GTK_IS_ACCEL_LABEL (child))
    accel_label = child;
  else if (GTK_IS_CONTAINER (child))
    {
      GList *children, *l;

      children = gtk_container_get_children (GTK_CONTAINER (child));
      for (l = children; l; l = l->next)
        {
          if (GTK_IS_ACCEL_LABEL (l->data))
            {
              accel_label = GTK_WIDGET (l->data);
              break;
            }
        }
      g_list_free (children);
    }

  if (accel_label)
    {
      guint key;
      GdkModifierType mods;

      gtk_accel_label_get_accel (GTK_ACCEL_LABEL (accel_label), &key, &mods);

      return gtk_accelerator_name (key, mods);
    }

  return NULL;
}

394
static void
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
gtk_model_menu_item_get_property (GObject    *object,
                                  guint       prop_id,
                                  GValue     *value,
                                  GParamSpec *pspec)
{
  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (object);

  switch (prop_id)
    {
    case PROP_ACTION_ROLE:
      g_value_set_enum (value, item->role);
      break;

    case PROP_ICON:
      g_value_set_object (value, gtk_model_menu_item_get_icon (item));
      break;

    case PROP_TEXT:
      g_value_set_string (value, gtk_model_menu_item_get_text (item));
      break;

    case PROP_TOGGLED:
      g_value_set_boolean (value, gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)));
      break;

    case PROP_ACCEL:
      g_value_take_string (value, gtk_model_menu_item_get_accel (item));
      break;

    default:
      g_assert_not_reached ();
    }
}
428 429

static void
430 431 432 433
gtk_model_menu_item_set_property (GObject      *object,
                                  guint         prop_id,
                                  const GValue *value,
                                  GParamSpec   *pspec)
434
{
435 436
  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (object);

437 438 439 440 441
  switch (prop_id)
    {
    case PROP_ACTION_ROLE:
      gtk_model_menu_item_set_action_role (item, g_value_get_enum (value));
      break;
442

443 444 445
    case PROP_ICON:
      gtk_model_menu_item_set_icon (item, g_value_get_object (value));
      break;
446

447 448
    case PROP_TEXT:
      gtk_model_menu_item_set_text (item, g_value_get_string (value));
449 450
      break;

451 452
    case PROP_TOGGLED:
      _gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), g_value_get_boolean (value));
453
      g_object_notify (object, "active");
454 455
      break;

456 457
    case PROP_ACCEL:
      gtk_model_menu_item_set_accel (item, g_value_get_string (value));
458 459 460 461 462
      break;

    default:
      g_assert_not_reached ();
    }
463 464 465
}

static void
466
gtk_model_menu_item_init (GtkModelMenuItem *item)
467 468 469 470 471 472
{
}

static void
gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
{
473
  GtkCheckMenuItemClass *check_class = GTK_CHECK_MENU_ITEM_CLASS (class);
474 475 476
  GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class);
  GObjectClass *object_class = G_OBJECT_CLASS (class);

477 478 479
  check_class->draw_indicator = gtk_model_menu_item_draw_indicator;

  item_class->toggle_size_request = gtk_model_menu_item_toggle_size_request;
480
  item_class->activate = gtk_model_menu_item_activate;
481

482
  object_class->get_property = gtk_model_menu_item_get_property;
483 484 485
  object_class->set_property = gtk_model_menu_item_set_property;

  g_object_class_install_property (object_class, PROP_ACTION_ROLE,
486 487 488
                                   g_param_spec_enum ("action-role", "action role", "action role",
                                                      GTK_TYPE_MENU_TRACKER_ITEM_ROLE,
                                                      GTK_MENU_TRACKER_ITEM_ROLE_NORMAL,
489
                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
490 491
  g_object_class_install_property (object_class, PROP_ICON,
                                   g_param_spec_object ("icon", "icon", "icon", G_TYPE_ICON,
492
                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
493 494
  g_object_class_install_property (object_class, PROP_TEXT,
                                   g_param_spec_string ("text", "text", "text", NULL,
495
                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
496 497
  g_object_class_install_property (object_class, PROP_TOGGLED,
                                   g_param_spec_boolean ("toggled", "toggled", "toggled", FALSE,
498
                                                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
499 500
  g_object_class_install_property (object_class, PROP_ACCEL,
                                   g_param_spec_string ("accel", "accel", "accel", NULL,
501
                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
502 503

  gtk_widget_class_set_accessible_role (GTK_WIDGET_CLASS (class), ATK_ROLE_MENU_ITEM);
504 505
}

506
GtkWidget *
507
gtk_model_menu_item_new (void)
508
{
509
  return g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL);
510
}