gtkmodelmenuitem.c 9.99 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 125
  atk_object_set_role (accessible, a11y_role);
}
126

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

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

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

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

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

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

166 167 168
          children = g_list_delete_link (children, children);
        }
    }
169

170 171
  /* If it is not a box, put it into a box, at the end */
  if (!GTK_IS_BOX (child))
172
    {
173
      GtkWidget *box;
174

175
      box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
176

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

183 184
      gtk_container_add (GTK_CONTAINER (item), box);
      gtk_widget_show (box);
185

186 187
      /* Now we have a box */
      child = box;
188 189
    }

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

199 200 201 202 203
      image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
      gtk_box_pack_start (GTK_BOX (child), image, FALSE, FALSE, 0);
      gtk_widget_show (image);
    }
}
204

205 206 207 208 209 210
static void
gtk_model_menu_item_set_text (GtkModelMenuItem *item,
                              const gchar      *text)
{
  GtkWidget *child;
  GList *children;
211

212 213 214 215 216 217 218
  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));
    }
219

220 221 222 223 224
  if (GTK_IS_LABEL (child))
    {
      gtk_label_set_text_with_mnemonic (GTK_LABEL (child), text);
      return;
    }
225

226 227
  if (!GTK_IS_CONTAINER (child))
    return;
228

229
  children = gtk_container_get_children (GTK_CONTAINER (child));
230

231 232 233 234
  while (children)
    {
      if (GTK_IS_LABEL (children->data))
        gtk_label_set_label (GTK_LABEL (children->data), text);
235

236 237 238
      children = g_list_delete_link (children, children);
    }
}
239

240 241 242 243 244 245 246 247
static void
gtk_model_menu_item_set_accel (GtkModelMenuItem *item,
                               const gchar      *accel)
{
  GtkWidget *child;
  GList *children;
  GdkModifierType modifiers;
  guint key;
248

249 250 251 252 253 254 255 256 257 258 259
  if (accel)
    {
      gtk_accelerator_parse (accel, &key, &modifiers);
      if (!key)
        modifiers = 0;
    }
  else
    {
      key = 0;
      modifiers = 0;
    }
260

261 262 263 264 265 266
  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));
267 268
    }

269 270 271 272 273
  if (GTK_IS_LABEL (child))
    {
      gtk_accel_label_set_accel (GTK_ACCEL_LABEL (child), key, modifiers);
      return;
    }
274

275
  if (!GTK_IS_CONTAINER (child))
276 277
    return;

278
  children = gtk_container_get_children (GTK_CONTAINER (child));
279

280 281 282 283 284 285 286
  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);
    }
287 288
}

289
void
290 291
gtk_model_menu_item_set_property (GObject *object, guint prop_id,
                                  const GValue *value, GParamSpec *pspec)
292
{
293 294
  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (object);

295 296 297 298 299
  switch (prop_id)
    {
    case PROP_ACTION_ROLE:
      gtk_model_menu_item_set_action_role (item, g_value_get_enum (value));
      break;
300

301 302 303
    case PROP_ICON:
      gtk_model_menu_item_set_icon (item, g_value_get_object (value));
      break;
304

305 306
    case PROP_TEXT:
      gtk_model_menu_item_set_text (item, g_value_get_string (value));
307 308
      break;

309 310
    case PROP_TOGGLED:
      _gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), g_value_get_boolean (value));
311 312
      break;

313 314
    case PROP_ACCEL:
      gtk_model_menu_item_set_accel (item, g_value_get_string (value));
315 316 317 318 319
      break;

    default:
      g_assert_not_reached ();
    }
320 321 322
}

static void
323
gtk_model_menu_item_init (GtkModelMenuItem *item)
324 325 326 327 328 329
{
}

static void
gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
{
330
  GtkCheckMenuItemClass *check_class = GTK_CHECK_MENU_ITEM_CLASS (class);
331 332 333
  GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class);
  GObjectClass *object_class = G_OBJECT_CLASS (class);

334 335 336
  check_class->draw_indicator = gtk_model_menu_item_draw_indicator;

  item_class->toggle_size_request = gtk_model_menu_item_toggle_size_request;
337
  item_class->activate = gtk_model_menu_item_activate;
338

339 340 341
  object_class->set_property = gtk_model_menu_item_set_property;

  g_object_class_install_property (object_class, PROP_ACTION_ROLE,
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
                                   g_param_spec_enum ("action-role", "action role", "action role",
                                                      GTK_TYPE_MENU_TRACKER_ITEM_ROLE,
                                                      GTK_MENU_TRACKER_ITEM_ROLE_NORMAL,
                                                      G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (object_class, PROP_ICON,
                                   g_param_spec_object ("icon", "icon", "icon", G_TYPE_ICON,
                                                        G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (object_class, PROP_TEXT,
                                   g_param_spec_string ("text", "text", "text", NULL,
                                                        G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (object_class, PROP_TOGGLED,
                                   g_param_spec_boolean ("toggled", "toggled", "toggled", FALSE,
                                                         G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (object_class, PROP_ACCEL,
                                   g_param_spec_string ("accel", "accel", "accel", NULL,
                                                        G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
358 359
}

360
GtkWidget *
361
gtk_model_menu_item_new (void)
362
{
363
  return g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL);
364
}