1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/gtk/avatar_menu_bubble_gtk.h"
7 #include "base/i18n/rtl.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/profiles/avatar_menu.h"
12 #include "chrome/browser/profiles/profile_info_cache.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/gtk/avatar_menu_item_gtk.h"
16 #include "chrome/browser/ui/gtk/browser_toolbar_gtk.h"
17 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
18 #include "chrome/browser/ui/gtk/event_utils.h"
19 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
20 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
21 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
22 #include "content/public/browser/notification_source.h"
23 #include "grit/generated_resources.h"
24 #include "ui/base/gtk/gtk_hig_constants.h"
25 #include "ui/base/l10n/l10n_util.h"
29 // The minimum width in pixels of the bubble.
30 const int kBubbleMinWidth
= 175;
32 // The number of pixels of padding on the left of the 'New Profile' link at the
33 // bottom of the bubble.
34 const int kNewProfileLinkLeftPadding
= 40;
38 AvatarMenuBubbleGtk::AvatarMenuBubbleGtk(Browser
* browser
,
40 BubbleGtk::FrameStyle arrow
,
41 const gfx::Rect
* rect
)
43 inner_contents_(NULL
),
44 theme_service_(GtkThemeService::GetFrom(browser
->profile())),
45 new_profile_link_(NULL
),
46 minimum_width_(kBubbleMinWidth
),
48 avatar_menu_
.reset(new AvatarMenu(
49 &g_browser_process
->profile_manager()->GetProfileInfoCache(),
51 avatar_menu_
->RebuildMenu();
53 OnAvatarMenuChanged(avatar_menu_
.get());
55 bubble_
= BubbleGtk::Show(anchor
,
59 BubbleGtk::MATCH_SYSTEM_THEME
|
60 BubbleGtk::POPUP_WINDOW
|
61 BubbleGtk::GRAB_INPUT
,
64 g_signal_connect(contents_
, "destroy",
65 G_CALLBACK(&OnDestroyThunk
), this);
68 AvatarMenuBubbleGtk::~AvatarMenuBubbleGtk() {}
70 void AvatarMenuBubbleGtk::OnDestroy(GtkWidget
* widget
) {
71 // We are self deleting, we have a destroy signal setup to catch when we
72 // destroyed (via the BubbleGtk being destroyed), and delete ourself.
73 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
76 void AvatarMenuBubbleGtk::BubbleClosing(BubbleGtk
* bubble
,
77 bool closed_by_escape
) {
81 void AvatarMenuBubbleGtk::OnAvatarMenuChanged(
82 AvatarMenu
* avatar_menu
) {
84 minimum_width_
= kBubbleMinWidth
;
89 void AvatarMenuBubbleGtk::OpenProfile(size_t profile_index
) {
92 GdkModifierType modifier_state
;
93 gtk_get_current_event_state(&modifier_state
);
94 guint modifier_state_uint
= modifier_state
;
95 avatar_menu_
->SwitchToProfile(profile_index
,
96 event_utils::DispositionFromGdkState(modifier_state_uint
) == NEW_WINDOW
);
100 void AvatarMenuBubbleGtk::EditProfile(size_t profile_index
) {
103 avatar_menu_
->EditProfile(profile_index
);
107 void AvatarMenuBubbleGtk::OnSizeRequest(GtkWidget
* widget
,
108 GtkRequisition
* req
) {
109 // Always use the maximum width ever requested.
110 if (req
->width
< minimum_width_
)
111 req
->width
= minimum_width_
;
113 minimum_width_
= req
->width
;
116 void AvatarMenuBubbleGtk::OnNewProfileLinkClicked(GtkWidget
* link
) {
119 avatar_menu_
->AddNewProfile(ProfileMetrics::ADD_NEW_USER_ICON
);
123 void AvatarMenuBubbleGtk::OnSwitchProfileLinkClicked(GtkWidget
* link
) {
125 OnAvatarMenuChanged(avatar_menu_
.get());
128 void AvatarMenuBubbleGtk::InitMenuContents() {
129 size_t profile_count
= avatar_menu_
->GetNumberOfItems();
130 GtkWidget
* items_vbox
= gtk_vbox_new(FALSE
, ui::kContentAreaSpacing
);
131 for (size_t i
= 0; i
< profile_count
; ++i
) {
132 AvatarMenu::Item menu_item
= avatar_menu_
->GetItemAt(i
);
133 AvatarMenuItemGtk
* item
= new AvatarMenuItemGtk(
134 this, menu_item
, i
, theme_service_
);
135 items_
.push_back(item
);
137 gtk_box_pack_start(GTK_BOX(items_vbox
), item
->widget(), TRUE
, TRUE
, 0);
138 gtk_widget_set_can_focus(item
->widget(), TRUE
);
139 if (menu_item
.active
)
140 gtk_container_set_focus_child(GTK_CONTAINER(items_vbox
), item
->widget());
142 gtk_box_pack_start(GTK_BOX(inner_contents_
), items_vbox
, TRUE
, TRUE
, 0);
144 if (avatar_menu_
->ShouldShowAddNewProfileLink()) {
145 gtk_box_pack_start(GTK_BOX(inner_contents_
),
146 gtk_hseparator_new(), TRUE
, TRUE
, 0);
148 // The new profile link.
149 new_profile_link_
= theme_service_
->BuildChromeLinkButton(
150 l10n_util::GetStringUTF8(IDS_PROFILES_CREATE_NEW_PROFILE_LINK
));
151 g_signal_connect(new_profile_link_
, "clicked",
152 G_CALLBACK(OnNewProfileLinkClickedThunk
), this);
154 GtkWidget
* link_align
= gtk_alignment_new(0, 0, 0, 0);
155 gtk_alignment_set_padding(GTK_ALIGNMENT(link_align
),
156 0, 0, kNewProfileLinkLeftPadding
, 0);
157 gtk_container_add(GTK_CONTAINER(link_align
), new_profile_link_
);
159 gtk_box_pack_start(GTK_BOX(inner_contents_
), link_align
, FALSE
, FALSE
, 0);
163 void AvatarMenuBubbleGtk::InitManagedUserContents() {
164 int active_index
= avatar_menu_
->GetActiveProfileIndex();
165 AvatarMenu::Item menu_item
=
166 avatar_menu_
->GetItemAt(active_index
);
167 AvatarMenuItemGtk
* item
= new AvatarMenuItemGtk(
168 this, menu_item
, active_index
, theme_service_
);
169 items_
.push_back(item
);
171 gtk_box_pack_start(GTK_BOX(inner_contents_
), item
->widget(), TRUE
, TRUE
, 0);
172 gtk_box_pack_start(GTK_BOX(inner_contents_
),
173 gtk_hseparator_new(), TRUE
, TRUE
, 0);
175 // Add information about managed users within a hbox.
176 GtkWidget
* managed_user_info
= gtk_hbox_new(FALSE
, 5);
177 GdkPixbuf
* limited_user_pixbuf
=
178 avatar_menu_
->GetManagedUserIcon().ToGdkPixbuf();
179 GtkWidget
* limited_user_img
= gtk_image_new_from_pixbuf(limited_user_pixbuf
);
180 GtkWidget
* icon_align
= gtk_alignment_new(0, 0, 0, 0);
181 gtk_container_add(GTK_CONTAINER(icon_align
), limited_user_img
);
182 gtk_box_pack_start(GTK_BOX(managed_user_info
), icon_align
, FALSE
, FALSE
, 0);
183 GtkWidget
* status_label
=
184 theme_service_
->BuildLabel(std::string(), ui::kGdkBlack
);
185 char* markup
= g_markup_printf_escaped(
186 "<span size='small'>%s</span>",
187 base::UTF16ToUTF8(avatar_menu_
->GetManagedUserInformation()).c_str());
188 const int kLabelWidth
= 150;
189 gtk_widget_set_size_request(status_label
, kLabelWidth
, -1);
190 gtk_label_set_markup(GTK_LABEL(status_label
), markup
);
191 gtk_label_set_line_wrap(GTK_LABEL(status_label
), TRUE
);
192 gtk_misc_set_alignment(GTK_MISC(status_label
), 0, 0);
194 gtk_box_pack_start(GTK_BOX(managed_user_info
), status_label
, FALSE
, FALSE
, 0);
196 GTK_BOX(inner_contents_
), managed_user_info
, FALSE
, FALSE
, 0);
199 GTK_BOX(inner_contents_
), gtk_hseparator_new(), TRUE
, TRUE
, 0);
201 // The switch profile link.
202 GtkWidget
* switch_profile_link
= theme_service_
->BuildChromeLinkButton(
203 l10n_util::GetStringUTF8(IDS_PROFILES_SWITCH_PROFILE_LINK
));
204 g_signal_connect(switch_profile_link
, "clicked",
205 G_CALLBACK(OnSwitchProfileLinkClickedThunk
), this);
207 GtkWidget
* link_align
= gtk_alignment_new(0.5, 0, 0, 0);
208 gtk_container_add(GTK_CONTAINER(link_align
), switch_profile_link
);
210 gtk_box_pack_start(GTK_BOX(inner_contents_
), link_align
, FALSE
, FALSE
, 0);
213 void AvatarMenuBubbleGtk::InitContents() {
214 // Destroy the old inner contents to allow replacing it.
216 gtk_widget_destroy(inner_contents_
);
217 inner_contents_
= gtk_vbox_new(FALSE
, ui::kControlSpacing
);
219 contents_
= gtk_vbox_new(FALSE
, 0);
220 gtk_container_set_border_width(GTK_CONTAINER(inner_contents_
),
221 ui::kContentAreaBorder
);
222 g_signal_connect(inner_contents_
, "size-request",
223 G_CALLBACK(OnSizeRequestThunk
), this);
225 if (avatar_menu_
->GetManagedUserInformation().empty() || switching_
)
228 InitManagedUserContents();
229 gtk_box_pack_start(GTK_BOX(contents_
), inner_contents_
, TRUE
, TRUE
, 0);
231 gtk_widget_show_all(contents_
);
234 void AvatarMenuBubbleGtk::CloseBubble() {