gtkmenutoolbutton.c 14.2 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * Copyright (C) 2003 Ricardo Fernandez Pascual
 * Copyright (C) 2004 Paolo Borelli
 *
 * 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 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
 * 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
17
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 19
 */

20
#include "config.h"
21

22
#include "gtkmenutoolbutton.h"
23

24 25
#include "gtktogglebutton.h"
#include "gtkarrow.h"
26 27
#include "gtkmenubutton.h"
#include "gtkmenubuttonprivate.h"
28
#include "gtkbox.h"
29 30
#include "gtkmenu.h"
#include "gtkmain.h"
31
#include "gtksizerequest.h"
32
#include "gtkbuildable.h"
33

34
#include "gtkprivate.h"
35
#include "gtkintl.h"
36 37


38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
/**
 * SECTION:gtkmenutoolbutton
 * @Short_description: A GtkToolItem containing a button with an additional dropdown menu
 * @Title: GtkMenuToolButton
 * @See_also: #GtkToolbar, #GtkToolButton
 *
 * A #GtkMenuToolButton is a #GtkToolItem that contains a button and
 * a small additional button with an arrow. When clicked, the arrow
 * button pops up a dropdown menu.
 *
 * Use gtk_menu_tool_button_new() to create a new
 * #GtkMenuToolButton. Use gtk_menu_tool_button_new_from_stock() to
 * create a new #GtkMenuToolButton containing a stock item.
 *
 * <refsect2 id="GtkMenuToolButton-BUILDER-UI">
 * <title>GtkMenuToolButton as GtkBuildable</title>
 * <para>
 * The GtkMenuToolButton implementation of the GtkBuildable interface
 * supports adding a menu by specifying "menu" as the "type"
 * attribute of a &lt;child&gt; element.
 *
 * <example>
 * <title>A UI definition fragment with menus</title>
 * <programlisting><![CDATA[
 * <object class="GtkMenuToolButton">
 *   <child type="menu">
 *     <object class="GtkMenu"/>
 *   </child>
 * </object>
 * ]]></programlisting>
 * </example>
 * </para>
 * </refsect2>
 */


74 75 76 77 78 79 80
struct _GtkMenuToolButtonPrivate
{
  GtkWidget *button;
  GtkWidget *arrow_button;
  GtkWidget *box;
};

81 82 83 84 85 86
static void gtk_menu_tool_button_buildable_interface_init (GtkBuildableIface   *iface);
static void gtk_menu_tool_button_buildable_add_child      (GtkBuildable        *buildable,
							   GtkBuilder          *builder,
							   GObject             *child,
							   const gchar         *type);

87 88
enum
{
89
  SHOW_MENU,
90 91 92 93 94 95
  LAST_SIGNAL
};

enum
{
  PROP_0,
96
  PROP_MENU
97 98 99 100
};

static gint signals[LAST_SIGNAL];

101 102 103 104 105
static GtkBuildableIface *parent_buildable_iface;

G_DEFINE_TYPE_WITH_CODE (GtkMenuToolButton, gtk_menu_tool_button, GTK_TYPE_TOOL_BUTTON,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                gtk_menu_tool_button_buildable_interface_init))
106 107 108 109

static void
gtk_menu_tool_button_construct_contents (GtkMenuToolButton *button)
{
110
  GtkMenuToolButtonPrivate *priv = button->priv;
111
  GtkWidget *box;
112
  GtkWidget *parent;
113 114 115 116 117 118
  GtkOrientation orientation;

  orientation = gtk_tool_item_get_orientation (GTK_TOOL_ITEM (button));

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    {
119
      box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
120
      gtk_menu_button_set_direction (GTK_MENU_BUTTON (priv->arrow_button), GTK_ARROW_DOWN);
121 122 123
    }
  else
    {
124 125 126
      GtkTextDirection direction;
      GtkArrowType type;

127
      box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
128 129 130
      direction = gtk_widget_get_direction (GTK_WIDGET (button));
      type = (direction == GTK_TEXT_DIR_LTR ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT);
      gtk_menu_button_set_direction (GTK_MENU_BUTTON (priv->arrow_button), type);
131 132
    }

133 134
  parent = gtk_widget_get_parent (priv->button);
  if (priv->button && parent)
135 136
    {
      g_object_ref (priv->button);
137
      gtk_container_remove (GTK_CONTAINER (parent),
138 139 140 141 142
                            priv->button);
      gtk_container_add (GTK_CONTAINER (box), priv->button);
      g_object_unref (priv->button);
    }

143 144
  parent = gtk_widget_get_parent (priv->arrow_button);
  if (priv->arrow_button && parent)
145 146
    {
      g_object_ref (priv->arrow_button);
147
      gtk_container_remove (GTK_CONTAINER (parent),
148 149 150 151 152 153 154 155
                            priv->arrow_button);
      gtk_box_pack_end (GTK_BOX (box), priv->arrow_button,
                        FALSE, FALSE, 0);
      g_object_unref (priv->arrow_button);
    }

  if (priv->box)
    {
156 157 158 159 160 161 162 163 164 165 166
      gchar *tmp;

      /* Transfer a possible tooltip to the new box */
      g_object_get (priv->box, "tooltip-markup", &tmp, NULL);

      if (tmp)
        {
	  g_object_set (box, "tooltip-markup", tmp, NULL);
	  g_free (tmp);
	}

167 168 169 170 171 172 173 174 175 176 177
      /* Note: we are not destroying the button and the arrow_button
       * here because they were removed from their container above
       */
      gtk_widget_destroy (priv->box);
    }

  priv->box = box;

  gtk_container_add (GTK_CONTAINER (button), priv->box);
  gtk_widget_show_all (priv->box);

178 179 180
  gtk_button_set_relief (GTK_BUTTON (priv->arrow_button),
			 gtk_tool_item_get_relief_style (GTK_TOOL_ITEM (button)));
  
181 182 183 184 185 186 187 188 189
  gtk_widget_queue_resize (GTK_WIDGET (button));
}

static void
gtk_menu_tool_button_toolbar_reconfigured (GtkToolItem *toolitem)
{
  gtk_menu_tool_button_construct_contents (GTK_MENU_TOOL_BUTTON (toolitem));

  /* chain up */
Matthias Clasen's avatar
Matthias Clasen committed
190
  GTK_TOOL_ITEM_CLASS (gtk_menu_tool_button_parent_class)->toolbar_reconfigured (toolitem);
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
}

static void
gtk_menu_tool_button_set_property (GObject      *object,
                                   guint         prop_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)
{
  GtkMenuToolButton *button = GTK_MENU_TOOL_BUTTON (object);

  switch (prop_id)
    {
    case PROP_MENU:
      gtk_menu_tool_button_set_menu (button, g_value_get_object (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_menu_tool_button_get_property (GObject    *object,
                                   guint       prop_id,
                                   GValue     *value,
                                   GParamSpec *pspec)
{
  GtkMenuToolButton *button = GTK_MENU_TOOL_BUTTON (object);

  switch (prop_id)
    {
    case PROP_MENU:
224
      g_value_set_object (value, gtk_menu_button_get_popup (GTK_MENU_BUTTON (button->priv->arrow_button)));
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_menu_tool_button_class_init (GtkMenuToolButtonClass *klass)
{
  GObjectClass *object_class;
  GtkToolItemClass *toolitem_class;

  object_class = (GObjectClass *)klass;
  toolitem_class = (GtkToolItemClass *)klass;

  object_class->set_property = gtk_menu_tool_button_set_property;
  object_class->get_property = gtk_menu_tool_button_get_property;
244

245 246
  toolitem_class->toolbar_reconfigured = gtk_menu_tool_button_toolbar_reconfigured;

Matthias Clasen's avatar
Matthias Clasen committed
247 248 249 250 251 252 253
  /**
   * GtkMenuToolButton::show-menu:
   * @button: the object on which the signal is emitted
   *
   * The ::show-menu signal is emitted before the menu is shown.
   *
   * It can be used to populate the menu on demand, using 
254
   * gtk_menu_tool_button_set_menu().
Matthias Clasen's avatar
Matthias Clasen committed
255 256 257 258 259

   * Note that even if you populate the menu dynamically in this way, 
   * you must set an empty menu on the #GtkMenuToolButton beforehand,
   * since the arrow is made insensitive if the menu is not set.
   */
260
  signals[SHOW_MENU] =
261
    g_signal_new (I_("show-menu"),
262
                  G_OBJECT_CLASS_TYPE (klass),
263 264 265 266 267
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkMenuToolButtonClass, show_menu),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
268 269 270 271 272 273 274

  g_object_class_install_property (object_class,
                                   PROP_MENU,
                                   g_param_spec_object ("menu",
                                                        P_("Menu"),
                                                        P_("The dropdown menu"),
                                                        GTK_TYPE_MENU,
275
                                                        GTK_PARAM_READWRITE));
276 277 278 279 280 281 282 283 284 285 286

  g_type_class_add_private (object_class, sizeof (GtkMenuToolButtonPrivate));
}

static void
gtk_menu_tool_button_init (GtkMenuToolButton *button)
{
  GtkWidget *box;
  GtkWidget *arrow_button;
  GtkWidget *real_button;

287 288 289
  button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
                                              GTK_TYPE_MENU_TOOL_BUTTON,
                                              GtkMenuToolButtonPrivate);
290 291 292

  gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (button), FALSE);

293
  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
294

Javier Jardón's avatar
Javier Jardón committed
295
  real_button = gtk_bin_get_child (GTK_BIN (button));
296 297 298 299 300
  g_object_ref (real_button);
  gtk_container_remove (GTK_CONTAINER (button), real_button);
  gtk_container_add (GTK_CONTAINER (box), real_button);
  g_object_unref (real_button);

301
  arrow_button = gtk_menu_button_new ();
302 303 304 305 306 307 308 309 310
  gtk_box_pack_end (GTK_BOX (box), arrow_button,
                    FALSE, FALSE, 0);

  /* the arrow button is insentive until we set a menu */
  gtk_widget_set_sensitive (arrow_button, FALSE);

  gtk_widget_show_all (box);

  gtk_container_add (GTK_CONTAINER (button), box);
311 312
  gtk_menu_button_set_align_widget (GTK_MENU_BUTTON (arrow_button),
                                    GTK_WIDGET (button));
313 314 315 316 317 318

  button->priv->button = real_button;
  button->priv->arrow_button = arrow_button;
  button->priv->box = box;
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
static void
gtk_menu_tool_button_buildable_add_child (GtkBuildable *buildable,
					  GtkBuilder   *builder,
					  GObject      *child,
					  const gchar  *type)
{
  if (type && strcmp (type, "menu") == 0)
    gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (buildable),
                                   GTK_WIDGET (child));
  else
    parent_buildable_iface->add_child (buildable, builder, child, type);
}

static void
gtk_menu_tool_button_buildable_interface_init (GtkBuildableIface *iface)
{
  parent_buildable_iface = g_type_interface_peek_parent (iface);
  iface->add_child = gtk_menu_tool_button_buildable_add_child;
}

339 340
/**
 * gtk_menu_tool_button_new:
341 342
 * @icon_widget: (allow-none): a widget that will be used as icon widget, or %NULL
 * @label: (allow-none): a string that will be used as label, or %NULL
343 344 345 346 347 348 349 350 351 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
 *
 * Creates a new #GtkMenuToolButton using @icon_widget as icon and
 * @label as label.
 *
 * Return value: the new #GtkMenuToolButton
 *
 * Since: 2.6
 **/
GtkToolItem *
gtk_menu_tool_button_new (GtkWidget   *icon_widget,
                          const gchar *label)
{
  GtkMenuToolButton *button;

  button = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON, NULL);

  if (label)
    gtk_tool_button_set_label (GTK_TOOL_BUTTON (button), label);

  if (icon_widget)
    gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (button), icon_widget);

  return GTK_TOOL_ITEM (button);
}

/**
 * gtk_menu_tool_button_new_from_stock:
 * @stock_id: the name of a stock item
 *
 * Creates a new #GtkMenuToolButton.
 * The new #GtkMenuToolButton will contain an icon and label from
 * the stock item indicated by @stock_id.
 *
 * Return value: the new #GtkMenuToolButton
 *
 * Since: 2.6
 **/
GtkToolItem *
gtk_menu_tool_button_new_from_stock (const gchar *stock_id)
{
  GtkMenuToolButton *button;

  g_return_val_if_fail (stock_id != NULL, NULL);

  button = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON,
388
			 "stock-id", stock_id,
389 390 391 392 393
			 NULL);

  return GTK_TOOL_ITEM (button);
}

394
static void
395
_show_menu_emit (gpointer user_data)
396
{
397 398
  GtkMenuToolButton *button = (GtkMenuToolButton *) user_data;
  g_signal_emit (button, signals[SHOW_MENU], 0);
399 400
}

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
/**
 * gtk_menu_tool_button_set_menu:
 * @button: a #GtkMenuToolButton
 * @menu: the #GtkMenu associated with #GtkMenuToolButton
 *
 * Sets the #GtkMenu that is popped up when the user clicks on the arrow.
 * If @menu is NULL, the arrow button becomes insensitive.
 *
 * Since: 2.6
 **/
void
gtk_menu_tool_button_set_menu (GtkMenuToolButton *button,
                               GtkWidget         *menu)
{
  GtkMenuToolButtonPrivate *priv;

  g_return_if_fail (GTK_IS_MENU_TOOL_BUTTON (button));
  g_return_if_fail (GTK_IS_MENU (menu) || menu == NULL);

  priv = button->priv;

422 423 424 425
  _gtk_menu_button_set_popup_with_func (GTK_MENU_BUTTON (priv->arrow_button),
                                        menu,
                                        _show_menu_emit,
                                        button);
426 427 428 429 430 431 432 433 434 435

  g_object_notify (G_OBJECT (button), "menu");
}

/**
 * gtk_menu_tool_button_get_menu:
 * @button: a #GtkMenuToolButton
 *
 * Gets the #GtkMenu associated with #GtkMenuToolButton.
 *
436 437
 * Return value: (transfer none): the #GtkMenu associated
 *     with #GtkMenuToolButton
438 439 440 441 442 443
 *
 * Since: 2.6
 **/
GtkWidget *
gtk_menu_tool_button_get_menu (GtkMenuToolButton *button)
{
444 445
  GtkMenu *ret;

446 447
  g_return_val_if_fail (GTK_IS_MENU_TOOL_BUTTON (button), NULL);

448
  ret = gtk_menu_button_get_popup (GTK_MENU_BUTTON (button->priv->arrow_button));
449 450 451 452
  if (!ret)
    return NULL;

  return GTK_WIDGET (ret);
453 454
}

455 456 457 458 459 460
/**
 * gtk_menu_tool_button_set_arrow_tooltip_text:
 * @button: a #GtkMenuToolButton
 * @text: text to be used as tooltip text for button's arrow button
 *
 * Sets the tooltip text to be used as tooltip for the arrow button which
461
 * pops up the menu.  See gtk_tool_item_set_tooltip_text() for setting a tooltip
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
 * on the whole #GtkMenuToolButton.
 *
 * Since: 2.12
 **/
void
gtk_menu_tool_button_set_arrow_tooltip_text (GtkMenuToolButton *button,
					     const gchar       *text)
{
  g_return_if_fail (GTK_IS_MENU_TOOL_BUTTON (button));

  gtk_widget_set_tooltip_text (button->priv->arrow_button, text);
}

/**
 * gtk_menu_tool_button_set_arrow_tooltip_markup:
 * @button: a #GtkMenuToolButton
 * @markup: markup text to be used as tooltip text for button's arrow button
 *
 * Sets the tooltip markup text to be used as tooltip for the arrow button
481
 * which pops up the menu.  See gtk_tool_item_set_tooltip_text() for setting a
482 483 484 485 486 487 488 489 490 491 492 493
 * tooltip on the whole #GtkMenuToolButton.
 *
 * Since: 2.12
 **/
void
gtk_menu_tool_button_set_arrow_tooltip_markup (GtkMenuToolButton *button,
					       const gchar       *markup)
{
  g_return_if_fail (GTK_IS_MENU_TOOL_BUTTON (button));

  gtk_widget_set_tooltip_markup (button->priv->arrow_button, markup);
}