gtkmenubar.c 25.9 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
#define GTK_MENU_INTERNALS

29
#include <config.h>
30 31
#include "gdk/gdkkeysyms.h"
#include "gtkbindings.h"
Elliot Lee's avatar
Elliot Lee committed
32
#include "gtkmain.h"
33
#include "gtkmarshalers.h"
Elliot Lee's avatar
Elliot Lee committed
34 35
#include "gtkmenubar.h"
#include "gtkmenuitem.h"
Havoc Pennington's avatar
Havoc Pennington committed
36 37 38
#include "gtksettings.h"
#include "gtkintl.h"
#include "gtkwindow.h"
39
#include "gtkprivate.h"
40
#include "gtkalias.h"
Elliot Lee's avatar
Elliot Lee committed
41 42


43
#define BORDER_SPACING  0
44
#define DEFAULT_IPADDING 1
Elliot Lee's avatar
Elliot Lee committed
45

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/* Properties */
enum {
  PROP_0,
  PROP_PACK_DIRECTION,
  PROP_CHILD_PACK_DIRECTION
};

typedef struct _GtkMenuBarPrivate GtkMenuBarPrivate;
struct _GtkMenuBarPrivate
{
  GtkPackDirection pack_direction;
  GtkPackDirection child_pack_direction;
};

#define GTK_MENU_BAR_GET_PRIVATE(o)  \
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_MENU_BAR, GtkMenuBarPrivate))


64
static void gtk_menu_bar_class_init        (GtkMenuBarClass *klass);
65 66 67 68 69 70 71 72
static void gtk_menu_bar_set_property      (GObject             *object,
					    guint                prop_id,
					    const GValue        *value,
					    GParamSpec          *pspec);
static void gtk_menu_bar_get_property      (GObject             *object,
					    guint                prop_id,
					    GValue              *value,
					    GParamSpec          *pspec);
73 74 75 76 77 78 79 80 81 82
static void gtk_menu_bar_size_request      (GtkWidget       *widget,
					    GtkRequisition  *requisition);
static void gtk_menu_bar_size_allocate     (GtkWidget       *widget,
					    GtkAllocation   *allocation);
static void gtk_menu_bar_paint             (GtkWidget       *widget,
					    GdkRectangle    *area);
static gint gtk_menu_bar_expose            (GtkWidget       *widget,
					    GdkEventExpose  *event);
static void gtk_menu_bar_hierarchy_changed (GtkWidget       *widget,
					    GtkWidget       *old_toplevel);
83 84 85
static gint gtk_menu_bar_get_popup_delay   (GtkMenuShell    *menu_shell);
static void gtk_menu_bar_move_current      (GtkMenuShell     *menu_shell,
                                            GtkMenuDirectionType direction);
86 87
					    

Havoc Pennington's avatar
Havoc Pennington committed
88
static GtkShadowType get_shadow_type   (GtkMenuBar      *menubar);
Elliot Lee's avatar
Elliot Lee committed
89

90 91
static GtkMenuShellClass *parent_class = NULL;

Manish Singh's avatar
Manish Singh committed
92
GType
93
gtk_menu_bar_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
94
{
Manish Singh's avatar
Manish Singh committed
95
  static GType menu_bar_type = 0;
Elliot Lee's avatar
Elliot Lee committed
96 97 98

  if (!menu_bar_type)
    {
Manish Singh's avatar
Manish Singh committed
99
      static const GTypeInfo menu_bar_info =
Elliot Lee's avatar
Elliot Lee committed
100 101
      {
	sizeof (GtkMenuBarClass),
Manish Singh's avatar
Manish Singh committed
102 103 104 105 106 107 108 109
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_menu_bar_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkMenuBar),
	0,		/* n_preallocs */
	NULL,		/* instance_init */
Elliot Lee's avatar
Elliot Lee committed
110 111
      };

Manish Singh's avatar
Manish Singh committed
112 113
      menu_bar_type = g_type_register_static (GTK_TYPE_MENU_SHELL, "GtkMenuBar",
					      &menu_bar_info, 0);
Elliot Lee's avatar
Elliot Lee committed
114 115 116 117 118 119 120 121
    }

  return menu_bar_type;
}

static void
gtk_menu_bar_class_init (GtkMenuBarClass *class)
{
122
  GObjectClass *gobject_class;
123
  GtkObjectClass *object_class;
Elliot Lee's avatar
Elliot Lee committed
124 125 126
  GtkWidgetClass *widget_class;
  GtkMenuShellClass *menu_shell_class;

127 128
  GtkBindingSet *binding_set;

129 130
  parent_class = g_type_class_peek_parent (class);
  
131
  gobject_class = (GObjectClass*) class;
132
  object_class = (GtkObjectClass*) class;
Elliot Lee's avatar
Elliot Lee committed
133 134 135
  widget_class = (GtkWidgetClass*) class;
  menu_shell_class = (GtkMenuShellClass*) class;

136 137 138
  gobject_class->get_property = gtk_menu_bar_get_property;
  gobject_class->set_property = gtk_menu_bar_set_property;

Elliot Lee's avatar
Elliot Lee committed
139 140 141
  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
142 143
  widget_class->hierarchy_changed = gtk_menu_bar_hierarchy_changed;
  
Elliot Lee's avatar
Elliot Lee committed
144
  menu_shell_class->submenu_placement = GTK_TOP_BOTTOM;
145
  menu_shell_class->get_popup_delay = gtk_menu_bar_get_popup_delay;
146
  menu_shell_class->move_current = gtk_menu_bar_move_current;
147 148 149 150 151 152 153

  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);
154 155 156 157 158
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Left, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_PREV);
159 160 161 162 163
  gtk_binding_entry_add_signal (binding_set,
				GDK_Right, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_NEXT);
164 165 166 167 168
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Right, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_NEXT);
169 170 171 172 173
  gtk_binding_entry_add_signal (binding_set,
				GDK_Up, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_PARENT);
174 175 176 177 178
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Up, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_PARENT);
179 180 181 182 183
  gtk_binding_entry_add_signal (binding_set,
				GDK_Down, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_CHILD);
184 185 186 187 188
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Down, 0,
				"move_current", 1,
				GTK_TYPE_MENU_DIRECTION_TYPE,
				GTK_MENU_DIR_CHILD);
Havoc Pennington's avatar
Havoc Pennington committed
189

190 191 192 193 194 195 196 197 198 199
  /**
   * GtkMenuBar:pack-direction:
   *
   * The pack direction of the menubar. It determines how
   * menuitems are arranged in the menubar.
   *
   * Since: 2.8
   */
  g_object_class_install_property (gobject_class,
				   PROP_PACK_DIRECTION,
200
				   g_param_spec_enum ("pack-direction",
201 202 203 204
 						      P_("Pack direction"),
 						      P_("The pack direction of the menubar"),
 						      GTK_TYPE_PACK_DIRECTION,
 						      GTK_PACK_DIRECTION_LTR,
205
 						      GTK_PARAM_READWRITE));
206 207 208 209 210 211 212 213 214 215 216
  
  /**
   * GtkMenuBar:child-pack-direction:
   *
   * The pack direction of the menubar. It determines how
   * the widgets contained in child menuitems are arranged.
   *
   * Since: 2.8
   */
  g_object_class_install_property (gobject_class,
				   PROP_CHILD_PACK_DIRECTION,
217
				   g_param_spec_enum ("child-pack-direction",
218 219 220 221
 						      P_("Child Pack direction"),
 						      P_("The child pack direction of the menubar"),
 						      GTK_TYPE_PACK_DIRECTION,
 						      GTK_PACK_DIRECTION_LTR,
222
 						      GTK_PARAM_READWRITE));
223 224
  

Havoc Pennington's avatar
Havoc Pennington committed
225
  gtk_widget_class_install_style_property (widget_class,
226
					   g_param_spec_enum ("shadow-type",
227 228
                                                              P_("Shadow type"),
                                                              P_("Style of bevel around the menubar"),
Havoc Pennington's avatar
Havoc Pennington committed
229 230
                                                              GTK_TYPE_SHADOW_TYPE,
                                                              GTK_SHADOW_OUT,
231
                                                              GTK_PARAM_READABLE));
232 233

  gtk_widget_class_install_style_property (widget_class,
234
					   g_param_spec_int ("internal-padding",
235 236
							     P_("Internal padding"),
							     P_("Amount of border space between the menubar shadow and the menu items"),
237 238 239
							     0,
							     G_MAXINT,
                                                             DEFAULT_IPADDING,
240
                                                             GTK_PARAM_READABLE));
241

242
  gtk_settings_install_property (g_param_spec_int ("gtk-menu-bar-popup-delay",
243 244
						   P_("Delay before drop down menus appear"),
						   P_("Delay before the submenus of a menu bar appear"),
245 246 247
						   0,
						   G_MAXINT,
						   0,
248
						   GTK_PARAM_READWRITE));
249 250

  g_type_class_add_private (gobject_class, sizeof (GtkMenuBarPrivate));  
251 252
}
 
Elliot Lee's avatar
Elliot Lee committed
253
GtkWidget*
254
gtk_menu_bar_new (void)
Elliot Lee's avatar
Elliot Lee committed
255
{
Manish Singh's avatar
Manish Singh committed
256
  return g_object_new (GTK_TYPE_MENU_BAR, NULL);
Elliot Lee's avatar
Elliot Lee committed
257 258
}

259 260 261 262 263 264 265 266 267 268 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
static void
gtk_menu_bar_set_property (GObject      *object,
			   guint         prop_id,
			   const GValue *value,
			   GParamSpec   *pspec)
{
  GtkMenuBar *menubar = GTK_MENU_BAR (object);
  
  switch (prop_id)
    {
    case PROP_PACK_DIRECTION:
      gtk_menu_bar_set_pack_direction (menubar, g_value_get_enum (value));
      break;
    case PROP_CHILD_PACK_DIRECTION:
      gtk_menu_bar_set_child_pack_direction (menubar, g_value_get_enum (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_menu_bar_get_property (GObject    *object,
			   guint       prop_id,
			   GValue     *value,
			   GParamSpec *pspec)
{
  GtkMenuBar *menubar = GTK_MENU_BAR (object);
  
  switch (prop_id)
    {
    case PROP_PACK_DIRECTION:
      g_value_set_enum (value, gtk_menu_bar_get_pack_direction (menubar));
      break;
    case PROP_CHILD_PACK_DIRECTION:
      g_value_set_enum (value, gtk_menu_bar_get_child_pack_direction (menubar));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
303 304 305 306 307
static void
gtk_menu_bar_size_request (GtkWidget      *widget,
			   GtkRequisition *requisition)
{
  GtkMenuBar *menu_bar;
308
  GtkMenuBarPrivate *priv;
Elliot Lee's avatar
Elliot Lee committed
309 310 311 312
  GtkMenuShell *menu_shell;
  GtkWidget *child;
  GList *children;
  gint nchildren;
313
  GtkRequisition child_requisition;
314
  gint ipadding;
Elliot Lee's avatar
Elliot Lee committed
315 316 317 318 319 320

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

  requisition->width = 0;
  requisition->height = 0;
321
  
Elliot Lee's avatar
Elliot Lee committed
322 323 324 325
  if (GTK_WIDGET_VISIBLE (widget))
    {
      menu_bar = GTK_MENU_BAR (widget);
      menu_shell = GTK_MENU_SHELL (widget);
326
      priv = GTK_MENU_BAR_GET_PRIVATE (menu_bar);
Elliot Lee's avatar
Elliot Lee committed
327 328 329 330 331 332 333 334 335 336 337

      nchildren = 0;
      children = menu_shell->children;

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

	  if (GTK_WIDGET_VISIBLE (child))
	    {
338
              gint toggle_size;
339

Elliot Lee's avatar
Elliot Lee committed
340
	      GTK_MENU_ITEM (child)->show_submenu_indicator = FALSE;
341
	      gtk_widget_size_request (child, &child_requisition);
342 343
              gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child),
                                                 &toggle_size);
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

	      if (priv->child_pack_direction == GTK_PACK_DIRECTION_LTR ||
		  priv->child_pack_direction == GTK_PACK_DIRECTION_RTL)
		child_requisition.width += toggle_size;
	      else
		child_requisition.height += toggle_size;

              if (priv->pack_direction == GTK_PACK_DIRECTION_LTR ||
		  priv->pack_direction == GTK_PACK_DIRECTION_RTL)
		{
		  requisition->width += child_requisition.width;
		  requisition->height = MAX (requisition->height, child_requisition.height);
		}
	      else
		{
		  requisition->width = MAX (requisition->width, child_requisition.width);
		  requisition->height += child_requisition.height;
		}
Elliot Lee's avatar
Elliot Lee committed
362 363 364 365
	      nchildren += 1;
	    }
	}

366
      gtk_widget_style_get (widget, "internal-padding", &ipadding, NULL);
367
      
Elliot Lee's avatar
Elliot Lee committed
368
      requisition->width += (GTK_CONTAINER (menu_bar)->border_width +
369
                             ipadding + 
Elliot Lee's avatar
Elliot Lee committed
370 371
			     BORDER_SPACING) * 2;
      requisition->height += (GTK_CONTAINER (menu_bar)->border_width +
372
                              ipadding +
Elliot Lee's avatar
Elliot Lee committed
373 374
			      BORDER_SPACING) * 2;

375 376 377 378 379
      if (get_shadow_type (menu_bar) != GTK_SHADOW_NONE)
	{
	  requisition->width += widget->style->xthickness * 2;
	  requisition->height += widget->style->ythickness * 2;
	}
Elliot Lee's avatar
Elliot Lee committed
380 381 382 383 384 385 386 387 388
    }
}

static void
gtk_menu_bar_size_allocate (GtkWidget     *widget,
			    GtkAllocation *allocation)
{
  GtkMenuBar *menu_bar;
  GtkMenuShell *menu_shell;
389
  GtkMenuBarPrivate *priv;
Elliot Lee's avatar
Elliot Lee committed
390 391 392
  GtkWidget *child;
  GList *children;
  GtkAllocation child_allocation;
393
  GtkRequisition child_requisition;
Elliot Lee's avatar
Elliot Lee committed
394
  guint offset;
395
  GtkTextDirection direction;
396
  gint ltr_x, ltr_y;
397
  gint ipadding;
398

Elliot Lee's avatar
Elliot Lee committed
399 400 401 402 403
  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);
404
  priv = GTK_MENU_BAR_GET_PRIVATE (menu_bar);
Elliot Lee's avatar
Elliot Lee committed
405

406 407
  direction = gtk_widget_get_direction (widget);

Elliot Lee's avatar
Elliot Lee committed
408 409 410 411 412 413
  widget->allocation = *allocation;
  if (GTK_WIDGET_REALIZED (widget))
    gdk_window_move_resize (widget->window,
			    allocation->x, allocation->y,
			    allocation->width, allocation->height);

414
  gtk_widget_style_get (widget, "internal-padding", &ipadding, NULL);
415
  
Elliot Lee's avatar
Elliot Lee committed
416 417 418
  if (menu_shell->children)
    {
      child_allocation.x = (GTK_CONTAINER (menu_bar)->border_width +
419
			    ipadding + 
Elliot Lee's avatar
Elliot Lee committed
420 421 422
			    BORDER_SPACING);
      child_allocation.y = (GTK_CONTAINER (menu_bar)->border_width +
			    BORDER_SPACING);
423
      
424 425 426 427 428 429
      if (get_shadow_type (menu_bar) != GTK_SHADOW_NONE)
	{
	  child_allocation.x += widget->style->xthickness;
	  child_allocation.y += widget->style->ythickness;
	}
      
430 431
      if (priv->pack_direction == GTK_PACK_DIRECTION_LTR ||
	  priv->pack_direction == GTK_PACK_DIRECTION_RTL)
Elliot Lee's avatar
Elliot Lee committed
432
	{
433 434 435 436 437 438 439
	  child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
	  
	  offset = child_allocation.x; 	/* Window edge to menubar start */
	  ltr_x = child_allocation.x;
	  
	  children = menu_shell->children;
	  while (children)
440
	    {
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
	      gint toggle_size;          
	      
	      child = children->data;
	      children = children->next;
	      
	      gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child),
						 &toggle_size);
	      gtk_widget_get_child_requisition (child, &child_requisition);
	    
	      if (priv->child_pack_direction == GTK_PACK_DIRECTION_LTR ||
		  priv->child_pack_direction == GTK_PACK_DIRECTION_RTL)
		child_requisition.width += toggle_size;
	      else
		child_requisition.height += toggle_size;
	      
	      /* Support for the right justified help menu */
	      if ((children == NULL) && (GTK_IS_MENU_ITEM(child))
		  && (GTK_MENU_ITEM(child)->right_justify)) 
		{
		  ltr_x = allocation->width -
		    child_requisition.width - offset;
		}
	      if (GTK_WIDGET_VISIBLE (child))
		{
		  if ((direction == GTK_TEXT_DIR_LTR) == (priv->pack_direction == GTK_PACK_DIRECTION_LTR))
		    child_allocation.x = ltr_x;
		  else
		    child_allocation.x = allocation->width -
		      child_requisition.width - ltr_x; 
		  
		  child_allocation.width = child_requisition.width;
		  
		  gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
						      toggle_size);
		  gtk_widget_size_allocate (child, &child_allocation);
		  
		  ltr_x += child_allocation.width;
		}
479
	    }
480 481 482 483 484 485 486 487 488 489
	}
      else
	{
	  child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
	  
	  offset = child_allocation.y; 	/* Window edge to menubar start */
	  ltr_y = child_allocation.y;
	  
	  children = menu_shell->children;
	  while (children)
Elliot Lee's avatar
Elliot Lee committed
490
	    {
491 492 493 494 495 496 497 498 499 500 501 502
	      gint toggle_size;          
	      
	      child = children->data;
	      children = children->next;
	      
	      gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child),
						 &toggle_size);
	      gtk_widget_get_child_requisition (child, &child_requisition);
	      
	      if (priv->child_pack_direction == GTK_PACK_DIRECTION_LTR ||
		  priv->child_pack_direction == GTK_PACK_DIRECTION_RTL)
		child_requisition.width += toggle_size;
503
	      else
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
		child_requisition.height += toggle_size;
	      
	      /* Support for the right justified help menu */
	      if ((children == NULL) && (GTK_IS_MENU_ITEM(child))
		  && (GTK_MENU_ITEM(child)->right_justify)) 
		{
		  ltr_y = allocation->height -
		    child_requisition.height - offset;
		}
	      if (GTK_WIDGET_VISIBLE (child))
		{
		  if ((direction == GTK_TEXT_DIR_LTR) ==
		      (priv->pack_direction == GTK_PACK_DIRECTION_TTB))
		    child_allocation.y = ltr_y;
		  else
		    child_allocation.y = allocation->height -
		      child_requisition.height - ltr_y; 
		  child_allocation.height = child_requisition.height;
		  
		  gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
						      toggle_size);
		  gtk_widget_size_allocate (child, &child_allocation);
		  
		  ltr_y += child_allocation.height;
		}
Elliot Lee's avatar
Elliot Lee committed
529 530 531 532 533 534
	    }
	}
    }
}

static void
535
gtk_menu_bar_paint (GtkWidget *widget, GdkRectangle *area)
Elliot Lee's avatar
Elliot Lee committed
536 537 538 539 540
{
  g_return_if_fail (GTK_IS_MENU_BAR (widget));

  if (GTK_WIDGET_DRAWABLE (widget))
    {
541 542 543 544
      gint border;

      border = GTK_CONTAINER (widget)->border_width;
      
545 546
      gtk_paint_box (widget->style,
		     widget->window,
547
                     GTK_WIDGET_STATE (widget),
Havoc Pennington's avatar
Havoc Pennington committed
548
                     get_shadow_type (GTK_MENU_BAR (widget)),
549
		     area, widget, "menubar",
550 551 552
		     border, border,
		     widget->allocation.width - border * 2,
                     widget->allocation.height - border * 2);
Elliot Lee's avatar
Elliot Lee committed
553 554 555 556 557 558 559 560 561 562 563 564
    }
}

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

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

567
      (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
Elliot Lee's avatar
Elliot Lee committed
568 569 570 571
    }

  return FALSE;
}
Havoc Pennington's avatar
Havoc Pennington committed
572

573 574 575 576 577 578
static GList *
get_menu_bars (GtkWindow *window)
{
  return g_object_get_data (G_OBJECT (window), "gtk-menu-bar-list");
}

579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
static GList *
get_viewable_menu_bars (GtkWindow *window)
{
  GList *menu_bars;
  GList *viewable_menu_bars = NULL;

  for (menu_bars = get_menu_bars (window);
       menu_bars;
       menu_bars = menu_bars->next)
    {
      GtkWidget *widget = menu_bars->data;
      gboolean viewable = TRUE;
      
      while (widget)
	{
	  if (!GTK_WIDGET_MAPPED (widget))
	    viewable = FALSE;
	  
	  widget = widget->parent;
	}

      if (viewable)
	viewable_menu_bars = g_list_prepend (viewable_menu_bars, menu_bars->data);
    }

  return g_list_reverse (viewable_menu_bars);
}

607 608 609 610 611 612 613
static void
set_menu_bars (GtkWindow *window,
	       GList     *menubars)
{
  g_object_set_data (G_OBJECT (window), "gtk-menu-bar-list", menubars);
}

Havoc Pennington's avatar
Havoc Pennington committed
614 615 616 617 618 619 620 621
static gboolean
window_key_press_handler (GtkWidget   *widget,
                          GdkEventKey *event,
                          gpointer     data)
{
  gchar *accel = NULL;
  gboolean retval = FALSE;
  
622
  g_object_get (gtk_widget_get_settings (widget),
Havoc Pennington's avatar
Havoc Pennington committed
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
                "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 &&
Owen Taylor's avatar
Owen Taylor committed
642 643
          ((event->state & gtk_accelerator_get_default_mod_mask ()) ==
	   (mods & gtk_accelerator_get_default_mod_mask ())))
Havoc Pennington's avatar
Havoc Pennington committed
644
        {
645 646
	  GList *tmp_menubars = get_viewable_menu_bars (GTK_WINDOW (widget));
	  GList *menubars;
Havoc Pennington's avatar
Havoc Pennington committed
647

648
	  menubars = _gtk_container_focus_sort (GTK_CONTAINER (widget), tmp_menubars,
649
						GTK_DIR_TAB_FORWARD, NULL);
650 651
	  g_list_free (tmp_menubars);
	  
652 653
	  if (menubars)
	    {
654
	      GtkMenuShell *menu_shell = GTK_MENU_SHELL (menubars->data);
655

656
	      _gtk_menu_shell_activate (menu_shell);
657
	      gtk_menu_shell_select_first (menu_shell, FALSE);
658 659
	      
	      g_list_free (menubars);
660 661
	      
	      retval = TRUE;	      
662
	    }
Havoc Pennington's avatar
Havoc Pennington committed
663 664 665 666 667 668 669 670 671 672 673 674
        }

      g_free (accel);
    }

  return retval;
}

static void
add_to_window (GtkWindow  *window,
               GtkMenuBar *menubar)
{
675
  GList *menubars = get_menu_bars (window);
Havoc Pennington's avatar
Havoc Pennington committed
676

677 678
  if (!menubars)
    {
Manish Singh's avatar
Manish Singh committed
679
      g_signal_connect (window,
680 681 682 683 684 685
			"key_press_event",
			G_CALLBACK (window_key_press_handler),
			NULL);
    }

  set_menu_bars (window, g_list_prepend (menubars, menubar));
Havoc Pennington's avatar
Havoc Pennington committed
686 687 688 689 690 691
}

static void
remove_from_window (GtkWindow  *window,
                    GtkMenuBar *menubar)
{
692 693 694 695 696 697 698 699 700
  GList *menubars = get_menu_bars (window);

  menubars = g_object_get_data (G_OBJECT (window),
				    "gtk-menu-bar-list");

  menubars = g_list_remove (menubars, menubar);

  if (!menubars)
    {
Manish Singh's avatar
Manish Singh committed
701 702
      g_signal_handlers_disconnect_by_func (window,
					    window_key_press_handler,
703 704 705 706
					    NULL);
    }

  set_menu_bars (window, menubars);
Havoc Pennington's avatar
Havoc Pennington committed
707 708 709
}

static void
710 711
gtk_menu_bar_hierarchy_changed (GtkWidget *widget,
				GtkWidget *old_toplevel)
Havoc Pennington's avatar
Havoc Pennington committed
712 713 714 715 716 717 718 719
{
  GtkWidget *toplevel;  
  GtkMenuBar *menubar;

  menubar = GTK_MENU_BAR (widget);

  toplevel = gtk_widget_get_toplevel (widget);

720
  if (old_toplevel)
721
    remove_from_window (GTK_WINDOW (old_toplevel), menubar);
Havoc Pennington's avatar
Havoc Pennington committed
722
  
723
  if (GTK_WIDGET_TOPLEVEL (toplevel))
724
    add_to_window (GTK_WINDOW (toplevel), menubar);
Havoc Pennington's avatar
Havoc Pennington committed
725 726
}

727 728 729 730 731 732 733 734 735 736
/**
 * _gtk_menu_bar_cycle_focus:
 * @menubar: a #GtkMenuBar
 * @dir: direction in which to cycle the focus
 * 
 * Move the focus between menubars in the toplevel.
 **/
void
_gtk_menu_bar_cycle_focus (GtkMenuBar       *menubar,
			   GtkDirectionType  dir)
737 738
{
  GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menubar));
739
  GtkMenuItem *to_activate = NULL;
740 741 742

  if (GTK_WIDGET_TOPLEVEL (toplevel))
    {
743 744
      GList *tmp_menubars = get_viewable_menu_bars (GTK_WINDOW (toplevel));
      GList *menubars;
745 746
      GList *current;

747
      menubars = _gtk_container_focus_sort (GTK_CONTAINER (toplevel), tmp_menubars,
748
					    dir, GTK_WIDGET (menubar));
749
      g_list_free (tmp_menubars);
750 751 752 753

      if (menubars)
	{
	  current = g_list_find (menubars, menubar);
754

755 756
	  if (current && current->next)
	    {
757
	      GtkMenuShell *new_menushell = GTK_MENU_SHELL (current->next->data);
758
	      if (new_menushell->children)
759
		to_activate = new_menushell->children->data;
760 761 762 763 764
	    }
	}
	  
      g_list_free (menubars);
    }
765

766
  gtk_menu_shell_cancel (GTK_MENU_SHELL (menubar));
767 768 769

  if (to_activate)
    g_signal_emit_by_name (to_activate, "activate_item");
770 771
}

Havoc Pennington's avatar
Havoc Pennington committed
772 773 774 775 776 777
static GtkShadowType
get_shadow_type (GtkMenuBar *menubar)
{
  GtkShadowType shadow_type = GTK_SHADOW_OUT;
  
  gtk_widget_style_get (GTK_WIDGET (menubar),
778
			"shadow-type", &shadow_type,
Havoc Pennington's avatar
Havoc Pennington committed
779 780 781 782
			NULL);

  return shadow_type;
}
783 784 785 786 787 788

static gint
gtk_menu_bar_get_popup_delay (GtkMenuShell *menu_shell)
{
  gint popup_delay;
  
789
  g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
790 791 792 793 794
		"gtk-menu-bar-popup-delay", &popup_delay,
		NULL);

  return popup_delay;
}
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958

static void
gtk_menu_bar_move_current (GtkMenuShell         *menu_shell,
			   GtkMenuDirectionType direction)
{
  GtkMenuBar *menubar = GTK_MENU_BAR (menu_shell);
  GtkTextDirection text_dir;
  GtkPackDirection pack_dir;

  text_dir = gtk_widget_get_direction (GTK_WIDGET (menubar));
  pack_dir = gtk_menu_bar_get_pack_direction (menubar);
  
  if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL)
     {
      if ((text_dir == GTK_TEXT_DIR_RTL) == (pack_dir == GTK_PACK_DIRECTION_LTR))
	{
	  switch (direction) 
	    {      
	    case GTK_MENU_DIR_PREV:
	      direction = GTK_MENU_DIR_NEXT;
	      break;
	    case GTK_MENU_DIR_NEXT:
	      direction = GTK_MENU_DIR_PREV;
	      break;
	    default: ;
	    }
	}
    }
  else
    {
      switch (direction) 
	{
	case GTK_MENU_DIR_PARENT:
	  if ((text_dir == GTK_TEXT_DIR_LTR) == (pack_dir == GTK_PACK_DIRECTION_TTB))
	    direction = GTK_MENU_DIR_PREV;
	  else
	    direction = GTK_MENU_DIR_NEXT;
	  break;
	case GTK_MENU_DIR_CHILD:
	  if ((text_dir == GTK_TEXT_DIR_LTR) == (pack_dir == GTK_PACK_DIRECTION_TTB))
	    direction = GTK_MENU_DIR_NEXT;
	  else
	    direction = GTK_MENU_DIR_PREV;
	  break;
	case GTK_MENU_DIR_PREV:
	  if (text_dir == GTK_TEXT_DIR_RTL)	  
	    direction = GTK_MENU_DIR_CHILD;
	  else
	    direction = GTK_MENU_DIR_PARENT;
	  break;
	case GTK_MENU_DIR_NEXT:
	  if (text_dir == GTK_TEXT_DIR_RTL)	  
	    direction = GTK_MENU_DIR_PARENT;
	  else
	    direction = GTK_MENU_DIR_CHILD;
	  break;
	default: ;
	}
    }
  
  GTK_MENU_SHELL_CLASS (parent_class)->move_current (menu_shell, direction);
}

/**
 * gtk_menu_bar_get_pack_direction:
 * @menubar: a #GtkMenuBar
 * 
 * Retrieves the current pack direction of the menubar. See
 * gtk_menu_bar_set_pack_direction().
 *
 * Return value: the pack direction
 *
 * Since: 2.8
 **/
GtkPackDirection
gtk_menu_bar_get_pack_direction (GtkMenuBar *menubar)
{
  GtkMenuBarPrivate *priv;

  g_return_val_if_fail (GTK_IS_MENU_BAR (menubar), 
			GTK_PACK_DIRECTION_LTR);
  
  priv = GTK_MENU_BAR_GET_PRIVATE (menubar);

  return priv->pack_direction;
}

/**
 * gtk_menu_bar_set_pack_direction:
 * @menubar: a #GtkMenuBar.
 * @pack_dir: a new #GtkPackDirection.
 * 
 * Sets how items should be packed inside a menubar.
 * 
 * Since: 2.8
 **/
void gtk_menu_bar_set_pack_direction (GtkMenuBar       *menubar,
				      GtkPackDirection  pack_dir)
{
  GtkMenuBarPrivate *priv;

  g_return_if_fail (GTK_IS_MENU_BAR (menubar));

  priv = GTK_MENU_BAR_GET_PRIVATE (menubar);

  if (priv->pack_direction != pack_dir)
    {
      priv->pack_direction = pack_dir;

      gtk_widget_queue_resize (GTK_WIDGET (menubar));
      g_object_notify (G_OBJECT (menubar), "pack-direction");
    }
}

/**
 * gtk_menu_bar_get_child_pack_direction:
 * @menubar: a #GtkMenuBar
 * 
 * Retrieves the current child pack direction of the menubar. See
 * gtk_menu_bar_set_child_pack_direction().
 *
 * Return value: the child pack direction
 *
 * Since: 2.8
 **/
GtkPackDirection
gtk_menu_bar_get_child_pack_direction (GtkMenuBar *menubar)
{
  GtkMenuBarPrivate *priv;

  g_return_val_if_fail (GTK_IS_MENU_BAR (menubar), 
			GTK_PACK_DIRECTION_LTR);
  
  priv = GTK_MENU_BAR_GET_PRIVATE (menubar);

  return priv->child_pack_direction;
}

/**
 * gtk_menu_bar_set_child_pack_direction:
 * @menubar: a #GtkMenuBar.
 * @child_pack_dir: a new #GtkPackDirection.
 * 
 * Sets how widgets should be packed inside the children of a menubar.
 * 
 * Since: 2.8
 **/
void gtk_menu_bar_set_child_pack_direction (GtkMenuBar       *menubar,
					    GtkPackDirection  child_pack_dir)
{
  GtkMenuBarPrivate *priv;

  g_return_if_fail (GTK_IS_MENU_BAR (menubar));

  priv = GTK_MENU_BAR_GET_PRIVATE (menubar);

  if (priv->child_pack_direction != child_pack_dir)
    {
      priv->child_pack_direction = child_pack_dir;

      gtk_widget_queue_resize (GTK_WIDGET (menubar));
      g_object_notify (G_OBJECT (menubar), "child-pack-direction");
    }
}
959 960 961

#define __GTK_MENU_BAR_C__
#include "gtkaliasdef.c"