1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
6#define CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
7
8// GtkCustomMenuItem is a GtkMenuItem subclass that has buttons in it and acts
9// to support this. GtkCustomMenuItems only render properly when put in a
10// GtkCustomMenu; there's a lot of collaboration between these two classes
11// necessary to work around how gtk normally does menus.
12//
13// We can't rely on the normal event infrastructure. While a menu is up, the
14// GtkMenu has a grab on all events. Instead of trying to pump events through
15// the normal channels, we have the GtkCustomMenu selectively forward mouse
16// motion through a back channel. The GtkCustomMenu only listens for button
17// press information so it can block the effects of the click if the cursor
18// isn't in a button in the menu item.
19//
20// A GtkCustomMenuItem doesn't try to take these signals and forward them to
21// the buttons it owns. The GtkCustomMenu class keeps track of which button is
22// selected (due to key events and mouse movement) and otherwise acts like a
23// normal GtkItem. The buttons are only for sizing and rendering; they don't
24// respond to events. Instead, when the GtkCustomMenuItem is activated by the
25// GtkMenu, it uses which button was selected as a signal of what to do.
26//
27// Users should connect to the "button-pushed" signal to be notified when a
28// button was pushed. We don't go through the normal "activate" signal because
29// we need to communicate additional information, namely which button was
30// activated.
31
32#include <gtk/gtk.h>
33
34G_BEGIN_DECLS
35
36#define GTK_TYPE_CUSTOM_MENU_ITEM                                       \
37  (gtk_custom_menu_item_get_type())
38#define GTK_CUSTOM_MENU_ITEM(obj)                                       \
39  (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CUSTOM_MENU_ITEM,         \
40                              GtkCustomMenuItem))
41#define GTK_CUSTOM_MENU_ITEM_CLASS(klass)                               \
42  (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CUSTOM_MENU_ITEM,          \
43                           GtkCustomMenuItemClass))
44#define GTK_IS_CUSTOM_MENU_ITEM(obj)                                    \
45  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CUSTOM_MENU_ITEM))
46#define GTK_IS_CUSTOM_MENU_ITEM_CLASS(klass)                            \
47  (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CUSTOM_MENU_ITEM))
48#define GTK_CUSTOM_MENU_ITEM_GET_CLASS(obj)                             \
49  (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CUSTOM_MENU_ITEM,          \
50                             GtkCustomMenuItemClass))
51
52typedef struct _GtkCustomMenuItem GtkCustomMenuItem;
53typedef struct _GtkCustomMenuItemClass GtkCustomMenuItemClass;
54
55struct _GtkCustomMenuItem {
56  GtkMenuItem menu_item;
57
58  // Container for button widgets.
59  GtkWidget* hbox;
60
61  // Label on left side of menu item.
62  GtkWidget* label;
63
64  // List of all widgets we added. Used to find the leftmost and rightmost
65  // continuous buttons.
66  GList* all_widgets;
67
68  // Possible button widgets. Used for keyboard navigation.
69  GList* button_widgets;
70
71  // The widget that currently has highlight.
72  GtkWidget* currently_selected_button;
73
74  // The widget that was selected *before* |currently_selected_button|. Why do
75  // we hang on to this? Because the menu system sends us a deselect signal
76  // right before activating us. We need to listen to deselect since that's
77  // what we receive when the mouse cursor leaves us entirely.
78  GtkWidget* previously_selected_button;
79};
80
81struct _GtkCustomMenuItemClass {
82  GtkMenuItemClass parent_class;
83};
84
85GType gtk_custom_menu_item_get_type(void) G_GNUC_CONST;
86GtkWidget* gtk_custom_menu_item_new(const char* title);
87
88// Adds a button to our list of items in the |hbox|.
89GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item,
90                                           int command_id);
91
92// Adds a button to our list of items in the |hbox|, but that isn't part of
93// |button_widgets| to prevent it from being activatable.
94GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item,
95                                                 int command_id);
96
97// Adds a vertical space in the |hbox|.
98void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item);
99
100// Receives a motion event from the GtkCustomMenu that contains us. We can't
101// just subscribe to motion-event or the individual widget enter/leave events
102// because the top level GtkMenu has an event grab.
103void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item,
104                                               gdouble x, gdouble y);
105
106// Notification that the menu got a cursor key event. Used to move up/down
107// within the menu buttons. Returns TRUE to stop the default signal handler
108// from running.
109gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item,
110                                          GtkMenuDirectionType direction);
111
112// Because we only get a generic "selected" signal when we've changed, we need
113// to have a way for the GtkCustomMenu to tell us that we were just
114// selected.
115void gtk_custom_menu_item_select_item_by_direction(
116    GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction);
117
118// Whether we are currently hovering over a clickable region on the menu
119// item. Used by GtkCustomMenu to determine whether it should discard click
120// events.
121gboolean gtk_custom_menu_item_is_in_clickable_region(
122    GtkCustomMenuItem* menu_item);
123
124// If the button is released while the |currently_selected_button| isn't
125// supposed to dismiss the menu, this signals to our listeners that we want to
126// run this command if it doesn't dismiss the menu.  Returns TRUE if we acted
127// on this button click (and should prevent the normal GtkMenu machinery from
128// firing an "activate" signal).
129gboolean gtk_custom_menu_item_try_no_dismiss_command(
130    GtkCustomMenuItem* menu_item);
131
132// Calls |callback| with every button and button-label in the container.
133void gtk_custom_menu_item_foreach_button(GtkCustomMenuItem* menu_item,
134                                         GtkCallback callback,
135                                         gpointer callback_data);
136
137G_END_DECLS
138
139#endif  // CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
140