gtkmenubar.c 14.4 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

27 28
#include "gdk/gdkkeysyms.h"
#include "gtkbindings.h"
Elliot Lee's avatar
Elliot Lee committed
29 30 31
#include "gtkmain.h"
#include "gtkmenubar.h"
#include "gtkmenuitem.h"
Havoc Pennington's avatar
Havoc Pennington committed
32 33 34 35
#include "gtksettings.h"
#include "gtkintl.h"
#include "gtkwindow.h"
#include "gtksignal.h"
Elliot Lee's avatar
Elliot Lee committed
36 37


38
#define BORDER_SPACING  0
Elliot Lee's avatar
Elliot Lee committed
39
#define CHILD_SPACING   3
40
#define DEFAULT_IPADDING 1
Elliot Lee's avatar
Elliot Lee committed
41 42 43 44 45 46

static void gtk_menu_bar_class_init    (GtkMenuBarClass *klass);
static void gtk_menu_bar_size_request  (GtkWidget       *widget,
					GtkRequisition  *requisition);
static void gtk_menu_bar_size_allocate (GtkWidget       *widget,
					GtkAllocation   *allocation);
47 48
static void gtk_menu_bar_paint         (GtkWidget       *widget,
					GdkRectangle    *area);
Elliot Lee's avatar
Elliot Lee committed
49 50
static gint gtk_menu_bar_expose        (GtkWidget       *widget,
					GdkEventExpose  *event);
51 52
static void gtk_menu_bar_hierarchy_changed (GtkWidget   *widget,
					    GtkWidget   *old_toplevel);
Havoc Pennington's avatar
Havoc Pennington committed
53
static GtkShadowType get_shadow_type   (GtkMenuBar      *menubar);
Elliot Lee's avatar
Elliot Lee committed
54

55 56
static GtkMenuShellClass *parent_class = NULL;

57
GtkType
58
gtk_menu_bar_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
59
{
60
  static GtkType menu_bar_type = 0;
Elliot Lee's avatar
Elliot Lee committed
61 62 63

  if (!menu_bar_type)
    {
64
      static const GtkTypeInfo menu_bar_info =
Elliot Lee's avatar
Elliot Lee committed
65 66 67 68 69
      {
	"GtkMenuBar",
	sizeof (GtkMenuBar),
	sizeof (GtkMenuBarClass),
	(GtkClassInitFunc) gtk_menu_bar_class_init,
Havoc Pennington's avatar
Havoc Pennington committed
70
	(GtkObjectInitFunc) NULL,
71 72
	/* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
73
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
74 75 76 77 78 79 80 81 82 83 84
      };

      menu_bar_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_bar_info);
    }

  return menu_bar_type;
}

static void
gtk_menu_bar_class_init (GtkMenuBarClass *class)
{
85
  GtkObjectClass *object_class;
Elliot Lee's avatar
Elliot Lee committed
86 87 88
  GtkWidgetClass *widget_class;
  GtkMenuShellClass *menu_shell_class;

89 90
  GtkBindingSet *binding_set;

91 92
  parent_class = g_type_class_peek_parent (class);
  
93
  object_class = (GtkObjectClass*) class;
Elliot Lee's avatar
Elliot Lee committed
94 95 96 97 98 99
  widget_class = (GtkWidgetClass*) class;
  menu_shell_class = (GtkMenuShellClass*) class;

  widget_class->size_request = gtk_menu_bar_size_request;
  widget_class->size_allocate = gtk_menu_bar_size_allocate;
  widget_class->expose_event = gtk_menu_bar_expose;
Havoc Pennington's avatar
Havoc Pennington committed
100 101
  widget_class->hierarchy_changed = gtk_menu_bar_hierarchy_changed;
  
Elliot Lee's avatar
Elliot Lee committed
102
  menu_shell_class->submenu_placement = GTK_TOP_BOTTOM;
103 104 105 106 107 108 109

  binding_set = gtk_binding_set_by_class (class);
  gtk_binding_entry_add_signal (binding_set,
				GDK_Left, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_PREV);
110 111 112 113 114
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Left, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_PREV);
115 116 117 118 119
  gtk_binding_entry_add_signal (binding_set,
				GDK_Right, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_NEXT);
120 121 122 123 124
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Right, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_NEXT);
125 126 127 128 129
  gtk_binding_entry_add_signal (binding_set,
				GDK_Up, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_PARENT);
130 131 132 133 134
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Up, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_PARENT);
135 136 137 138 139
  gtk_binding_entry_add_signal (binding_set,
				GDK_Down, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_CHILD);
140 141 142 143 144 145
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Down, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_CHILD);
  
146
  gtk_settings_install_property (g_param_spec_string ("gtk-menu-bar-accel",
Havoc Pennington's avatar
Havoc Pennington committed
147 148 149 150 151 152 153 154 155 156 157 158
                                                      _("Menu bar accelerator"),
                                                      _("Keybinding to activate the menu bar"),
                                                      "F10",
                                                      G_PARAM_READWRITE));

  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_enum ("shadow_type",
                                                              _("Shadow type"),
                                                              _("Style of bevel around the menubar"),
                                                              GTK_TYPE_SHADOW_TYPE,
                                                              GTK_SHADOW_OUT,
                                                              G_PARAM_READABLE));
159 160 161 162 163 164 165 166 167 168

  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("internal_padding",
							     _("Internal padding"),
							     _("Amount of border space between the menubar shadow and the menu items"),
							     0,
							     G_MAXINT,
                                                             DEFAULT_IPADDING,
                                                             G_PARAM_READABLE));

169 170
}
 
Elliot Lee's avatar
Elliot Lee committed
171
GtkWidget*
172
gtk_menu_bar_new (void)
Elliot Lee's avatar
Elliot Lee committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
{
  return GTK_WIDGET (gtk_type_new (gtk_menu_bar_get_type ()));
}

void
gtk_menu_bar_append (GtkMenuBar *menu_bar,
		     GtkWidget  *child)
{
  gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), child);
}

void
gtk_menu_bar_prepend (GtkMenuBar *menu_bar,
		      GtkWidget  *child)
{
  gtk_menu_shell_prepend (GTK_MENU_SHELL (menu_bar), child);
}

void
gtk_menu_bar_insert (GtkMenuBar *menu_bar,
		     GtkWidget  *child,
		     gint        position)
{
  gtk_menu_shell_insert (GTK_MENU_SHELL (menu_bar), child, position);
}


static void
gtk_menu_bar_size_request (GtkWidget      *widget,
			   GtkRequisition *requisition)
{
  GtkMenuBar *menu_bar;
  GtkMenuShell *menu_shell;
  GtkWidget *child;
  GList *children;
  gint nchildren;
209
  GtkRequisition child_requisition;
210
  gint ipadding;
Elliot Lee's avatar
Elliot Lee committed
211 212 213 214 215 216 217

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_MENU_BAR (widget));
  g_return_if_fail (requisition != NULL);

  requisition->width = 0;
  requisition->height = 0;
218
  
Elliot Lee's avatar
Elliot Lee committed
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
  if (GTK_WIDGET_VISIBLE (widget))
    {
      menu_bar = GTK_MENU_BAR (widget);
      menu_shell = GTK_MENU_SHELL (widget);

      nchildren = 0;
      children = menu_shell->children;

      while (children)
	{
	  child = children->data;
	  children = children->next;

	  if (GTK_WIDGET_VISIBLE (child))
	    {
234 235
              gint toggle_size;
              
Elliot Lee's avatar
Elliot Lee committed
236
	      GTK_MENU_ITEM (child)->show_submenu_indicator = FALSE;
237
	      gtk_widget_size_request (child, &child_requisition);
238 239 240
              gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child),
                                                 &toggle_size);
              
241
	      requisition->width += child_requisition.width;
242 243
              requisition->width += toggle_size;
              
244
	      requisition->height = MAX (requisition->height, child_requisition.height);
245
	      /* Support for the right justified help menu */
246 247
	      if ((children == NULL) && GTK_IS_MENU_ITEM(child) &&
		  GTK_MENU_ITEM(child)->right_justify)
248 249 250
		{
		  requisition->width += CHILD_SPACING;
		}
Elliot Lee's avatar
Elliot Lee committed
251 252 253 254 255

	      nchildren += 1;
	    }
	}

256 257
      gtk_widget_style_get (widget, "internal_padding", &ipadding, NULL);
      
Elliot Lee's avatar
Elliot Lee committed
258
      requisition->width += (GTK_CONTAINER (menu_bar)->border_width +
259
			     widget->style->xthickness +
260
                             ipadding + 
Elliot Lee's avatar
Elliot Lee committed
261 262
			     BORDER_SPACING) * 2;
      requisition->height += (GTK_CONTAINER (menu_bar)->border_width +
263
			      widget->style->ythickness +
264
                              ipadding +
Elliot Lee's avatar
Elliot Lee committed
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
			      BORDER_SPACING) * 2;

      if (nchildren > 0)
	requisition->width += 2 * CHILD_SPACING * (nchildren - 1);
    }
}

static void
gtk_menu_bar_size_allocate (GtkWidget     *widget,
			    GtkAllocation *allocation)
{
  GtkMenuBar *menu_bar;
  GtkMenuShell *menu_shell;
  GtkWidget *child;
  GList *children;
  GtkAllocation child_allocation;
281
  GtkRequisition child_requisition;
Elliot Lee's avatar
Elliot Lee committed
282
  guint offset;
283
  gint ipadding;
Elliot Lee's avatar
Elliot Lee committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297
  
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_MENU_BAR (widget));
  g_return_if_fail (allocation != NULL);

  menu_bar = GTK_MENU_BAR (widget);
  menu_shell = GTK_MENU_SHELL (widget);

  widget->allocation = *allocation;
  if (GTK_WIDGET_REALIZED (widget))
    gdk_window_move_resize (widget->window,
			    allocation->x, allocation->y,
			    allocation->width, allocation->height);

298 299
  gtk_widget_style_get (widget, "internal_padding", &ipadding, NULL);
  
Elliot Lee's avatar
Elliot Lee committed
300 301 302
  if (menu_shell->children)
    {
      child_allocation.x = (GTK_CONTAINER (menu_bar)->border_width +
303
			    widget->style->xthickness +
304
                            ipadding + 
Elliot Lee's avatar
Elliot Lee committed
305 306 307 308
			    BORDER_SPACING);
      offset = child_allocation.x; 	/* Window edge to menubar start */

      child_allocation.y = (GTK_CONTAINER (menu_bar)->border_width +
309
			    widget->style->ythickness +
310
                            ipadding +
Elliot Lee's avatar
Elliot Lee committed
311
			    BORDER_SPACING);
312
      child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
Elliot Lee's avatar
Elliot Lee committed
313 314 315 316

      children = menu_shell->children;
      while (children)
	{
317 318
          gint toggle_size;          

Elliot Lee's avatar
Elliot Lee committed
319 320 321
	  child = children->data;
	  children = children->next;

322 323
          gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child),
                                             &toggle_size);
324
	  gtk_widget_get_child_requisition (child, &child_requisition);
325 326 327

          child_requisition.width += toggle_size;
          
Elliot Lee's avatar
Elliot Lee committed
328 329
	  /* Support for the right justified help menu */
	  if ( (children == NULL) && (GTK_IS_MENU_ITEM(child))
330 331 332
	      && (GTK_MENU_ITEM(child)->right_justify)) 
	    {
	      child_allocation.x = allocation->width -
333
		  child_requisition.width - CHILD_SPACING - offset;
334
	    }
Elliot Lee's avatar
Elliot Lee committed
335 336
	  if (GTK_WIDGET_VISIBLE (child))
	    {
337
	      child_allocation.width = child_requisition.width;
Elliot Lee's avatar
Elliot Lee committed
338

339 340
              gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
                                                  toggle_size);
Elliot Lee's avatar
Elliot Lee committed
341 342 343 344 345 346 347 348 349
	      gtk_widget_size_allocate (child, &child_allocation);

	      child_allocation.x += child_allocation.width + CHILD_SPACING * 2;
	    }
	}
    }
}

static void
350
gtk_menu_bar_paint (GtkWidget *widget, GdkRectangle *area)
Elliot Lee's avatar
Elliot Lee committed
351 352 353 354 355 356
{
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_MENU_BAR (widget));

  if (GTK_WIDGET_DRAWABLE (widget))
    {
357 358 359 360
      gint border;

      border = GTK_CONTAINER (widget)->border_width;
      
361 362
      gtk_paint_box (widget->style,
		     widget->window,
363
                     GTK_WIDGET_STATE (widget),
Havoc Pennington's avatar
Havoc Pennington committed
364
                     get_shadow_type (GTK_MENU_BAR (widget)),
365
		     area, widget, "menubar",
366 367 368
		     border, border,
		     widget->allocation.width - border * 2,
                     widget->allocation.height - border * 2);
Elliot Lee's avatar
Elliot Lee committed
369 370 371 372 373 374 375 376 377 378 379 380 381
    }
}

static gint
gtk_menu_bar_expose (GtkWidget      *widget,
		     GdkEventExpose *event)
{
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_MENU_BAR (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (GTK_WIDGET_DRAWABLE (widget))
    {
382
      gtk_menu_bar_paint (widget, &event->area);
Elliot Lee's avatar
Elliot Lee committed
383

384
      (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
Elliot Lee's avatar
Elliot Lee committed
385 386 387 388
    }

  return FALSE;
}
Havoc Pennington's avatar
Havoc Pennington committed
389 390 391 392 393 394 395 396 397

static gboolean
window_key_press_handler (GtkWidget   *widget,
                          GdkEventKey *event,
                          gpointer     data)
{
  gchar *accel = NULL;
  gboolean retval = FALSE;
  
398
  g_object_get (G_OBJECT (gtk_widget_get_settings (widget)),
Havoc Pennington's avatar
Havoc Pennington committed
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 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
                "gtk-menu-bar-accel",
                &accel,
                NULL);

  if (accel)
    {
      guint keyval = 0;
      GdkModifierType mods = 0;

      gtk_accelerator_parse (accel, &keyval, &mods);

      if (keyval == 0)
        g_warning ("Failed to parse menu bar accelerator '%s'\n", accel);

      /* FIXME this is wrong, needs to be in the global accel resolution
       * thing, to properly consider i18n etc., but that probably requires
       * AccelGroup changes etc.
       */
      if (event->keyval == keyval &&
          (mods & event->state) == mods)
        {
          GtkMenuBar *menubar;
          GtkMenuShell *menushell;
          
          menubar = GTK_MENU_BAR (data);
          menushell = GTK_MENU_SHELL (menubar);

          if (menushell->children)
            {
              gtk_signal_emit_by_name (GTK_OBJECT (menushell->children->data),
                                       "activate_item");
              
              retval = TRUE;
            }
        }

      g_free (accel);
    }

  return retval;
}

static void
add_to_window (GtkWindow  *window,
               GtkMenuBar *menubar)
{
  GtkMenuBar *old_menubar;

  old_menubar = g_object_get_data (G_OBJECT (window),
                                   "gtk-menu-bar");
  
  if (old_menubar)
    return; /* ignore this case; app programmer on crack, but
             * shouldn't spew stuff, just don't support the accel
             * for menubar #2
             */

  g_object_set_data (G_OBJECT (window),
                     "gtk-menu-bar",
                     menubar);

460 461 462 463
  g_signal_connect (G_OBJECT (window),
		    "key_press_event",
		    G_CALLBACK (window_key_press_handler),
		    menubar);
Havoc Pennington's avatar
Havoc Pennington committed
464 465 466 467 468 469 470 471 472 473 474 475
}

static void
remove_from_window (GtkWindow  *window,
                    GtkMenuBar *menubar)
{
  g_signal_handlers_disconnect_by_func (G_OBJECT (window),
                                        G_CALLBACK (window_key_press_handler),
                                        menubar);
}

static void
476 477
gtk_menu_bar_hierarchy_changed (GtkWidget *widget,
				GtkWidget *old_toplevel)
Havoc Pennington's avatar
Havoc Pennington committed
478 479 480 481 482 483 484 485
{
  GtkWidget *toplevel;  
  GtkMenuBar *menubar;

  menubar = GTK_MENU_BAR (widget);

  toplevel = gtk_widget_get_toplevel (widget);

486
  if (old_toplevel)
487
    remove_from_window (old_toplevel, GTK_MENU_BAR (menubar));
Havoc Pennington's avatar
Havoc Pennington committed
488
  
489
  if (GTK_WIDGET_TOPLEVEL (toplevel))
490
    add_to_window (GTK_WINDOW (toplevel), menubar);
Havoc Pennington's avatar
Havoc Pennington committed
491 492 493 494 495 496 497 498 499 500 501 502 503 504
}

static GtkShadowType
get_shadow_type (GtkMenuBar *menubar)
{
  GtkShadowType shadow_type = GTK_SHADOW_OUT;
  
  gtk_widget_style_get (GTK_WIDGET (menubar),
			"shadow_type", &shadow_type,
			NULL);


  return shadow_type;
}