Home | History | Annotate | Download | only in libgtk2ui
      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 
     14 namespace libgtk2ui {
     15 
     16 AppIndicatorIconMenu::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 
     36 AppIndicatorIconMenu::~AppIndicatorIconMenu() {
     37   gtk_widget_destroy(gtk_menu_);
     38   g_object_unref(gtk_menu_);
     39 }
     40 
     41 void 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 
     79 void AppIndicatorIconMenu::Refresh() {
     80   gtk_container_foreach(
     81       GTK_CONTAINER(gtk_menu_), SetMenuItemInfo, &block_activation_);
     82 }
     83 
     84 GtkMenu* AppIndicatorIconMenu::GetGtkMenu() {
     85   return GTK_MENU(gtk_menu_);
     86 }
     87 
     88 
     89 void AppIndicatorIconMenu::OnClickActionReplacementMenuItemActivated(
     90     GtkWidget* menu_item) {
     91   click_action_replacement_callback_.Run();
     92 }
     93 
     94 void 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