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 26
#include "gtkimage.h"
#include "gtkbox.h"
27

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

typedef GtkCheckMenuItemClass GtkModelMenuItemClass;

37
G_DEFINE_TYPE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM)
38

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

49 50 51 52 53 54 55 56 57 58 59 60 61 62
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;
}

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

69 70 71 72 73 74 75 76 77 78 79
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);
}

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

87
  item->has_indicator = has_indicator;
88

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

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

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

102 103
  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);
104

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

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

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

120 121 122
    default:
      g_assert_not_reached ();
    }
123

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

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

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

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

139 140 141 142 143 144 145 146 147 148 149 150 151 152
  /* 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));
153 154
    }

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

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

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

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

177
      box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
178

179 180 181 182 183
      /* 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);
184

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

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

192 193 194 195 196 197
  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)
198
    {
199
      GtkWidget *image;
200

201
      image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
202
      gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
203 204 205
      gtk_box_pack_start (GTK_BOX (child), image, FALSE, FALSE, 0);
      gtk_widget_show (image);
    }
206 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

  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;
234
}
235

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

243 244 245 246 247 248 249
  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));
    }
250

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

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

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

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

267 268
      children = g_list_delete_link (children, children);
    }
269 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

  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;
303
}
304

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

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

326 327 328 329 330 331
  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));
332 333
    }

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

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

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

345 346 347 348 349 350 351
  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);
    }
352 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
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;
}

393
static void
394 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
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 ();
    }
}
427 428

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

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

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

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

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

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

    default:
      g_assert_not_reached ();
    }
462 463 464
}

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

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

476 477 478
  check_class->draw_indicator = gtk_model_menu_item_draw_indicator;

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

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

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

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

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