Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(640)

Side by Side Diff: chrome/browser/gtk/gtk_custom_menu_item.cc

Issue 6251001: Move chrome/browser/gtk/ to chrome/browser/ui/gtk/... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/gtk/gtk_custom_menu_item.h ('k') | chrome/browser/gtk/gtk_expanded_container.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 #include "chrome/browser/gtk/gtk_custom_menu_item.h"
6
7 #include "base/i18n/rtl.h"
8 #include "chrome/browser/gtk/gtk_custom_menu.h"
9
10 enum {
11 BUTTON_PUSHED,
12 TRY_BUTTON_PUSHED,
13 LAST_SIGNAL
14 };
15
16 static guint custom_menu_item_signals[LAST_SIGNAL] = { 0 };
17
18 G_DEFINE_TYPE(GtkCustomMenuItem, gtk_custom_menu_item, GTK_TYPE_MENU_ITEM)
19
20 static void set_selected(GtkCustomMenuItem* item, GtkWidget* selected) {
21 if (selected != item->currently_selected_button) {
22 if (item->currently_selected_button) {
23 gtk_widget_set_state(item->currently_selected_button, GTK_STATE_NORMAL);
24 gtk_widget_set_state(
25 gtk_bin_get_child(GTK_BIN(item->currently_selected_button)),
26 GTK_STATE_NORMAL);
27 }
28
29 item->currently_selected_button = selected;
30 if (item->currently_selected_button) {
31 gtk_widget_set_state(item->currently_selected_button, GTK_STATE_SELECTED);
32 gtk_widget_set_state(
33 gtk_bin_get_child(GTK_BIN(item->currently_selected_button)),
34 GTK_STATE_PRELIGHT);
35 }
36 }
37 }
38
39 // When GtkButtons set the label text, they rebuild the widget hierarchy each
40 // and every time. Therefore, we can't just fish out the label from the button
41 // and set some properties; we have to create this callback function that
42 // listens on the button's "notify" signal, which is emitted right after the
43 // label has been (re)created. (Label values can change dynamically.)
44 static void on_button_label_set(GObject* object) {
45 GtkButton* button = GTK_BUTTON(object);
46 gtk_widget_set_sensitive(GTK_BIN(button)->child, FALSE);
47 gtk_misc_set_padding(GTK_MISC(GTK_BIN(button)->child), 2, 0);
48 }
49
50 static void gtk_custom_menu_item_finalize(GObject *object);
51 static gint gtk_custom_menu_item_expose(GtkWidget* widget,
52 GdkEventExpose* event);
53 static gboolean gtk_custom_menu_item_hbox_expose(GtkWidget* widget,
54 GdkEventExpose* event,
55 GtkCustomMenuItem* menu_item);
56 static void gtk_custom_menu_item_select(GtkItem *item);
57 static void gtk_custom_menu_item_deselect(GtkItem *item);
58 static void gtk_custom_menu_item_activate(GtkMenuItem* menu_item);
59
60 static void gtk_custom_menu_item_init(GtkCustomMenuItem* item) {
61 item->all_widgets = NULL;
62 item->button_widgets = NULL;
63 item->currently_selected_button = NULL;
64 item->previously_selected_button = NULL;
65
66 GtkWidget* menu_hbox = gtk_hbox_new(FALSE, 0);
67 gtk_container_add(GTK_CONTAINER(item), menu_hbox);
68
69 item->label = gtk_label_new(NULL);
70 gtk_misc_set_alignment(GTK_MISC(item->label), 0.0, 0.5);
71 gtk_box_pack_start(GTK_BOX(menu_hbox), item->label, TRUE, TRUE, 0);
72
73 item->hbox = gtk_hbox_new(FALSE, 0);
74 gtk_box_pack_end(GTK_BOX(menu_hbox), item->hbox, FALSE, FALSE, 0);
75
76 g_signal_connect(item->hbox, "expose-event",
77 G_CALLBACK(gtk_custom_menu_item_hbox_expose),
78 item);
79
80 gtk_widget_show_all(menu_hbox);
81 }
82
83 static void gtk_custom_menu_item_class_init(GtkCustomMenuItemClass* klass) {
84 GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
85 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
86 GtkItemClass* item_class = GTK_ITEM_CLASS(klass);
87 GtkMenuItemClass* menu_item_class = GTK_MENU_ITEM_CLASS(klass);
88
89 gobject_class->finalize = gtk_custom_menu_item_finalize;
90
91 widget_class->expose_event = gtk_custom_menu_item_expose;
92
93 item_class->select = gtk_custom_menu_item_select;
94 item_class->deselect = gtk_custom_menu_item_deselect;
95
96 menu_item_class->activate = gtk_custom_menu_item_activate;
97
98 custom_menu_item_signals[BUTTON_PUSHED] =
99 g_signal_new("button-pushed",
100 G_OBJECT_CLASS_TYPE(gobject_class),
101 G_SIGNAL_RUN_FIRST,
102 0,
103 NULL, NULL,
104 gtk_marshal_NONE__INT,
105 G_TYPE_NONE, 1, GTK_TYPE_INT);
106 // TODO(erg): Change from BOOL__POINTER to BOOLEAN__INTEGER when we get to
107 // use a modern GTK+.
108 custom_menu_item_signals[TRY_BUTTON_PUSHED] =
109 g_signal_new("try-button-pushed",
110 G_OBJECT_CLASS_TYPE(gobject_class),
111 G_SIGNAL_RUN_LAST,
112 0,
113 NULL, NULL,
114 gtk_marshal_BOOL__POINTER,
115 G_TYPE_BOOLEAN, 1, GTK_TYPE_INT);
116 }
117
118 static void gtk_custom_menu_item_finalize(GObject *object) {
119 GtkCustomMenuItem* item = GTK_CUSTOM_MENU_ITEM(object);
120 g_list_free(item->all_widgets);
121 g_list_free(item->button_widgets);
122
123 G_OBJECT_CLASS(gtk_custom_menu_item_parent_class)->finalize(object);
124 }
125
126 static gint gtk_custom_menu_item_expose(GtkWidget* widget,
127 GdkEventExpose* event) {
128 if (GTK_WIDGET_VISIBLE(widget) &&
129 GTK_WIDGET_MAPPED(widget) &&
130 gtk_bin_get_child(GTK_BIN(widget))) {
131 // We skip the drawing in the GtkMenuItem class it draws the highlighted
132 // background and we don't want that.
133 gtk_container_propagate_expose(GTK_CONTAINER(widget),
134 gtk_bin_get_child(GTK_BIN(widget)),
135 event);
136 }
137
138 return FALSE;
139 }
140
141 static void gtk_custom_menu_item_expose_button(GtkWidget* hbox,
142 GdkEventExpose* event,
143 GList* button_item) {
144 // We search backwards to find the leftmost and rightmost buttons. The
145 // current button may be that button.
146 GtkWidget* current_button = GTK_WIDGET(button_item->data);
147 GtkWidget* first_button = current_button;
148 for (GList* i = button_item; i && GTK_IS_BUTTON(i->data);
149 i = g_list_previous(i)) {
150 first_button = GTK_WIDGET(i->data);
151 }
152
153 GtkWidget* last_button = current_button;
154 for (GList* i = button_item; i && GTK_IS_BUTTON(i->data);
155 i = g_list_next(i)) {
156 last_button = GTK_WIDGET(i->data);
157 }
158
159 if (base::i18n::IsRTL())
160 std::swap(first_button, last_button);
161
162 int x = first_button->allocation.x;
163 int y = first_button->allocation.y;
164 int width = last_button->allocation.width + last_button->allocation.x -
165 first_button->allocation.x;
166 int height = last_button->allocation.height;
167
168 gtk_paint_box(hbox->style, hbox->window,
169 static_cast<GtkStateType>(
170 GTK_WIDGET_STATE(current_button)),
171 GTK_SHADOW_OUT,
172 &current_button->allocation, hbox, "button",
173 x, y, width, height);
174
175 // Propagate to the button's children.
176 gtk_container_propagate_expose(
177 GTK_CONTAINER(current_button),
178 gtk_bin_get_child(GTK_BIN(current_button)),
179 event);
180 }
181
182 static gboolean gtk_custom_menu_item_hbox_expose(GtkWidget* widget,
183 GdkEventExpose* event,
184 GtkCustomMenuItem* menu_item) {
185 // First render all the buttons that aren't the currently selected item.
186 for (GList* current_item = menu_item->all_widgets;
187 current_item != NULL; current_item = g_list_next(current_item)) {
188 if (GTK_IS_BUTTON(current_item->data)) {
189 if (GTK_WIDGET(current_item->data) !=
190 menu_item->currently_selected_button) {
191 gtk_custom_menu_item_expose_button(widget, event, current_item);
192 }
193 }
194 }
195
196 // As a separate pass, draw the buton separators above. We need to draw the
197 // separators in a separate pass because we are drawing on top of the
198 // buttons. Otherwise, the vlines are overwritten by the next button.
199 for (GList* current_item = menu_item->all_widgets;
200 current_item != NULL; current_item = g_list_next(current_item)) {
201 if (GTK_IS_BUTTON(current_item->data)) {
202 // Check to see if this is the last button in a run.
203 GList* next_item = g_list_next(current_item);
204 if (next_item && GTK_IS_BUTTON(next_item->data)) {
205 GtkWidget* current_button = GTK_WIDGET(current_item->data);
206 GtkAllocation child_alloc =
207 gtk_bin_get_child(GTK_BIN(current_button))->allocation;
208 int half_offset = widget->style->xthickness / 2;
209 gtk_paint_vline(widget->style, widget->window,
210 static_cast<GtkStateType>(
211 GTK_WIDGET_STATE(current_button)),
212 &event->area, widget, "button",
213 child_alloc.y,
214 child_alloc.y + child_alloc.height,
215 current_button->allocation.x +
216 current_button->allocation.width - half_offset);
217 }
218 }
219 }
220
221 // Finally, draw the selected item on top of the separators so there are no
222 // artifacts inside the button area.
223 GList* selected = g_list_find(menu_item->all_widgets,
224 menu_item->currently_selected_button);
225 if (selected) {
226 gtk_custom_menu_item_expose_button(widget, event, selected);
227 }
228
229 return TRUE;
230 }
231
232 static void gtk_custom_menu_item_select(GtkItem* item) {
233 GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(item);
234
235 // When we are selected, the only thing we do is clear information from
236 // previous selections. Actual selection of a button is done either in the
237 // "mouse-motion-event" or is manually set from GtkCustomMenu's overridden
238 // "move-current" handler.
239 custom_item->previously_selected_button = NULL;
240
241 gtk_widget_queue_draw(GTK_WIDGET(item));
242 }
243
244 static void gtk_custom_menu_item_deselect(GtkItem* item) {
245 GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(item);
246
247 // When we are deselected, we store the item that was currently selected so
248 // that it can be acted on. Menu items are first deselected before they are
249 // activated.
250 custom_item->previously_selected_button =
251 custom_item->currently_selected_button;
252 if (custom_item->currently_selected_button)
253 set_selected(custom_item, NULL);
254
255 gtk_widget_queue_draw(GTK_WIDGET(item));
256 }
257
258 static void gtk_custom_menu_item_activate(GtkMenuItem* menu_item) {
259 GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(menu_item);
260
261 // We look at |previously_selected_button| because by the time we've been
262 // activated, we've already gone through our deselect handler.
263 if (custom_item->previously_selected_button) {
264 gpointer id_ptr = g_object_get_data(
265 G_OBJECT(custom_item->previously_selected_button), "command-id");
266 if (id_ptr != NULL) {
267 int command_id = GPOINTER_TO_INT(id_ptr);
268 g_signal_emit(custom_item, custom_menu_item_signals[BUTTON_PUSHED], 0,
269 command_id);
270 set_selected(custom_item, NULL);
271 }
272 }
273 }
274
275 GtkWidget* gtk_custom_menu_item_new(const char* title) {
276 GtkCustomMenuItem* item = GTK_CUSTOM_MENU_ITEM(
277 g_object_new(GTK_TYPE_CUSTOM_MENU_ITEM, NULL));
278 gtk_label_set_text(GTK_LABEL(item->label), title);
279 return GTK_WIDGET(item);
280 }
281
282 GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item,
283 int command_id) {
284 GtkWidget* button = gtk_button_new();
285 g_object_set_data(G_OBJECT(button), "command-id",
286 GINT_TO_POINTER(command_id));
287 gtk_box_pack_start(GTK_BOX(menu_item->hbox), button, FALSE, FALSE, 0);
288 gtk_widget_show(button);
289
290 menu_item->all_widgets = g_list_append(menu_item->all_widgets, button);
291 menu_item->button_widgets = g_list_append(menu_item->button_widgets, button);
292
293 return button;
294 }
295
296 GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item,
297 int command_id) {
298 GtkWidget* button = gtk_button_new_with_label("");
299 g_object_set_data(G_OBJECT(button), "command-id",
300 GINT_TO_POINTER(command_id));
301 gtk_box_pack_start(GTK_BOX(menu_item->hbox), button, FALSE, FALSE, 0);
302 g_signal_connect(button, "notify::label",
303 G_CALLBACK(on_button_label_set), NULL);
304 gtk_widget_show(button);
305
306 menu_item->all_widgets = g_list_append(menu_item->all_widgets, button);
307
308 return button;
309 }
310
311 void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item) {
312 GtkWidget* fixed = gtk_fixed_new();
313 gtk_widget_set_size_request(fixed, 5, -1);
314
315 gtk_box_pack_start(GTK_BOX(menu_item->hbox), fixed, FALSE, FALSE, 0);
316 gtk_widget_show(fixed);
317
318 menu_item->all_widgets = g_list_append(menu_item->all_widgets, fixed);
319 }
320
321 void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item,
322 gdouble x, gdouble y) {
323 GtkWidget* new_selected_widget = NULL;
324 GList* current = menu_item->button_widgets;
325 for (; current != NULL; current = current->next) {
326 GtkWidget* current_widget = GTK_WIDGET(current->data);
327 GtkAllocation alloc = current_widget->allocation;
328 int offset_x, offset_y;
329 gtk_widget_translate_coordinates(current_widget, GTK_WIDGET(menu_item),
330 0, 0, &offset_x, &offset_y);
331 if (x >= offset_x && x < (offset_x + alloc.width) &&
332 y >= offset_y && y < (offset_y + alloc.height)) {
333 new_selected_widget = current_widget;
334 break;
335 }
336 }
337
338 set_selected(menu_item, new_selected_widget);
339 }
340
341 gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item,
342 GtkMenuDirectionType direction) {
343 GtkWidget* current = menu_item->currently_selected_button;
344 if (menu_item->button_widgets && current) {
345 switch (direction) {
346 case GTK_MENU_DIR_PREV: {
347 if (g_list_first(menu_item->button_widgets)->data == current)
348 return FALSE;
349
350 set_selected(menu_item, GTK_WIDGET(g_list_previous(g_list_find(
351 menu_item->button_widgets, current))->data));
352 break;
353 }
354 case GTK_MENU_DIR_NEXT: {
355 if (g_list_last(menu_item->button_widgets)->data == current)
356 return FALSE;
357
358 set_selected(menu_item, GTK_WIDGET(g_list_next(g_list_find(
359 menu_item->button_widgets, current))->data));
360 break;
361 }
362 default:
363 break;
364 }
365 }
366
367 return TRUE;
368 }
369
370 void gtk_custom_menu_item_select_item_by_direction(
371 GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction) {
372 menu_item->previously_selected_button = NULL;
373
374 // If we're just told to be selected by the menu system, select the first
375 // item.
376 if (menu_item->button_widgets) {
377 switch (direction) {
378 case GTK_MENU_DIR_PREV: {
379 GtkWidget* last_button =
380 GTK_WIDGET(g_list_last(menu_item->button_widgets)->data);
381 if (last_button)
382 set_selected(menu_item, last_button);
383 break;
384 }
385 case GTK_MENU_DIR_NEXT: {
386 GtkWidget* first_button =
387 GTK_WIDGET(g_list_first(menu_item->button_widgets)->data);
388 if (first_button)
389 set_selected(menu_item, first_button);
390 break;
391 }
392 default:
393 break;
394 }
395 }
396
397 gtk_widget_queue_draw(GTK_WIDGET(menu_item));
398 }
399
400 gboolean gtk_custom_menu_item_is_in_clickable_region(
401 GtkCustomMenuItem* menu_item) {
402 return menu_item->currently_selected_button != NULL;
403 }
404
405 gboolean gtk_custom_menu_item_try_no_dismiss_command(
406 GtkCustomMenuItem* menu_item) {
407 GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(menu_item);
408 gboolean activated = TRUE;
409
410 // We work with |currently_selected_button| instead of
411 // |previously_selected_button| since we haven't been "deselect"ed yet.
412 gpointer id_ptr = g_object_get_data(
413 G_OBJECT(custom_item->currently_selected_button), "command-id");
414 if (id_ptr != NULL) {
415 int command_id = GPOINTER_TO_INT(id_ptr);
416 g_signal_emit(custom_item, custom_menu_item_signals[TRY_BUTTON_PUSHED], 0,
417 command_id, &activated);
418 }
419
420 return activated;
421 }
422
423 void gtk_custom_menu_item_foreach_button(GtkCustomMenuItem* menu_item,
424 GtkCallback callback,
425 gpointer callback_data) {
426 // Even though we're filtering |all_widgets| on GTK_IS_BUTTON(), this isn't
427 // equivalent to |button_widgets| because we also want the button-labels.
428 for (GList* i = menu_item->all_widgets; i && GTK_IS_BUTTON(i->data);
429 i = g_list_next(i)) {
430 if (GTK_IS_BUTTON(i->data)) {
431 callback(GTK_WIDGET(i->data), callback_data);
432 }
433 }
434 }
OLDNEW
« no previous file with comments | « chrome/browser/gtk/gtk_custom_menu_item.h ('k') | chrome/browser/gtk/gtk_expanded_container.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698