1// Copyright 2014 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/ui/libgtk2ui/app_indicator_icon_menu.h"
6
7#include <gtk/gtk.h>
8
9#include "base/bind.h"
10#include "base/debug/leak_annotations.h"
11#include "chrome/browser/ui/libgtk2ui/menu_util.h"
12#include "ui/base/models/menu_model.h"
13
14namespace libgtk2ui {
15
16AppIndicatorIconMenu::AppIndicatorIconMenu(ui::MenuModel* model)
17    : menu_model_(model),
18      click_action_replacement_menu_item_added_(false),
19      gtk_menu_(NULL),
20      block_activation_(false) {
21  {
22    ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/378770
23    gtk_menu_ = gtk_menu_new();
24  }
25  g_object_ref_sink(gtk_menu_);
26  if (menu_model_) {
27    BuildSubmenuFromModel(menu_model_,
28                          gtk_menu_,
29                          G_CALLBACK(OnMenuItemActivatedThunk),
30                          &block_activation_,
31                          this);
32    Refresh();
33  }
34}
35
36AppIndicatorIconMenu::~AppIndicatorIconMenu() {
37  gtk_widget_destroy(gtk_menu_);
38  g_object_unref(gtk_menu_);
39}
40
41void AppIndicatorIconMenu::UpdateClickActionReplacementMenuItem(
42    const char* label,
43    const base::Closure& callback) {
44  click_action_replacement_callback_ = callback;
45
46  if (click_action_replacement_menu_item_added_) {
47    GList* children = gtk_container_get_children(GTK_CONTAINER(gtk_menu_));
48    for (GList* child = children; child; child = g_list_next(child)) {
49      if (g_object_get_data(G_OBJECT(child->data), "click-action-item") !=
50          NULL) {
51        gtk_menu_item_set_label(GTK_MENU_ITEM(child->data), label);
52        break;
53      }
54    }
55    g_list_free(children);
56  } else {
57    click_action_replacement_menu_item_added_ = true;
58
59    // If |menu_model_| is non empty, add a separator to separate the
60    // "click action replacement menu item" from the other menu items.
61    if (menu_model_ && menu_model_->GetItemCount() > 0) {
62      GtkWidget* menu_item = gtk_separator_menu_item_new();
63      gtk_widget_show(menu_item);
64      gtk_menu_shell_prepend(GTK_MENU_SHELL(gtk_menu_), menu_item);
65    }
66
67    GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic(label);
68    g_object_set_data(
69        G_OBJECT(menu_item), "click-action-item", GINT_TO_POINTER(1));
70    g_signal_connect(menu_item,
71                     "activate",
72                     G_CALLBACK(OnClickActionReplacementMenuItemActivatedThunk),
73                     this);
74    gtk_widget_show(menu_item);
75    gtk_menu_shell_prepend(GTK_MENU_SHELL(gtk_menu_), menu_item);
76  }
77}
78
79void AppIndicatorIconMenu::Refresh() {
80  gtk_container_foreach(
81      GTK_CONTAINER(gtk_menu_), SetMenuItemInfo, &block_activation_);
82}
83
84GtkMenu* AppIndicatorIconMenu::GetGtkMenu() {
85  return GTK_MENU(gtk_menu_);
86}
87
88
89void AppIndicatorIconMenu::OnClickActionReplacementMenuItemActivated(
90    GtkWidget* menu_item) {
91  click_action_replacement_callback_.Run();
92}
93
94void AppIndicatorIconMenu::OnMenuItemActivated(GtkWidget* menu_item) {
95  if (block_activation_)
96    return;
97
98  ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(menu_item));
99  if (!model) {
100    // There won't be a model for "native" submenus like the "Input Methods"
101    // context menu. We don't need to handle activation messages for submenus
102    // anyway, so we can just return here.
103    DCHECK(gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item)));
104    return;
105  }
106
107  // The activate signal is sent to radio items as they get deselected;
108  // ignore it in this case.
109  if (GTK_IS_RADIO_MENU_ITEM(menu_item) &&
110      !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item))) {
111    return;
112  }
113
114  int id;
115  if (!GetMenuItemID(menu_item, &id))
116    return;
117
118  // The menu item can still be activated by hotkeys even if it is disabled.
119  if (menu_model_->IsEnabledAt(id))
120    ExecuteCommand(model, id);
121}
122
123}  // namespace libgtk2ui
124