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/one_click_signin_bubble_gtk.h"
9 #include "base/callback_helpers.h"
10 #include "base/i18n/rtl.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_finder.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/gtk_theme_service.h"
19 #include "chrome/browser/ui/gtk/gtk_util.h"
20 #include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h"
21 #include "chrome/browser/ui/sync/one_click_signin_helper.h"
22 #include "chrome/browser/ui/sync/one_click_signin_histogram.h"
23 #include "chrome/common/url_constants.h"
24 #include "grit/chromium_strings.h"
25 #include "grit/generated_resources.h"
26 #include "ui/base/gtk/gtk_hig_constants.h"
27 #include "ui/base/l10n/l10n_util.h"
29 static const int kModalDialogMessageWidth
= 400;
31 OneClickSigninBubbleGtk::OneClickSigninBubbleGtk(
32 BrowserWindowGtk
* browser_window_gtk
,
33 BrowserWindow::OneClickSigninBubbleType type
,
34 const base::string16
& email
,
35 const base::string16
& error_message
,
36 const BrowserWindow::StartSyncCallback
& start_sync_callback
)
39 error_message_(error_message
),
40 start_sync_callback_(start_sync_callback
),
41 is_sync_dialog_(type
!=BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE
),
48 clicked_learn_more_(false) {
49 InitializeWidgets(browser_window_gtk
);
50 ShowWidget(browser_window_gtk
, LayoutWidgets());
53 void OneClickSigninBubbleGtk::BubbleClosing(
54 BubbleGtk
* bubble
, bool closed_by_escape
) {
55 // If we get here and |start_sync_callback_| is not null, act like this is
56 // an undo. All actions that start sign in are explicitly handled below.
57 if (is_sync_dialog_
&& !start_sync_callback_
.is_null()) {
58 base::ResetAndReturn(&start_sync_callback_
).Run(
59 OneClickSigninSyncStarter::UNDO_SYNC
);
62 // The bubble needs to close and remove the widgets from the window before
63 // |close_button_| (which is a CustomDrawButton) can be destroyed, because it
64 // depends on all references being cleared for the GtkWidget before it is
66 base::MessageLoopForUI::current()->DeleteSoon(FROM_HERE
,
67 close_button_
.release());
72 void OneClickSigninBubbleGtk::OnClickAdvancedLink(GtkWidget
* link
) {
73 if (is_sync_dialog_
) {
74 OneClickSigninHelper::LogConfirmHistogramValue(
76 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_ADVANCED
:
77 one_click_signin::HISTOGRAM_CONFIRM_ADVANCED
);
79 base::ResetAndReturn(&start_sync_callback_
).Run(
80 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST
);
82 Browser
* browser
= chrome::FindBrowserWithWindow(
83 gtk_window_get_transient_for(bubble_
->GetNativeWindow()));
85 chrome::NavigateParams
params(browser
, GURL(chrome::kChromeUISettingsURL
),
86 content::PAGE_TRANSITION_LINK
);
87 params
.disposition
= CURRENT_TAB
;
88 chrome::Navigate(¶ms
);
93 void OneClickSigninBubbleGtk::OnClickOK(GtkWidget
* link
) {
94 if (is_sync_dialog_
) {
95 OneClickSigninHelper::LogConfirmHistogramValue(
97 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_OK
:
98 one_click_signin::HISTOGRAM_CONFIRM_OK
);
100 base::ResetAndReturn(&start_sync_callback_
).Run(
101 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS
);
106 void OneClickSigninBubbleGtk::OnClickUndo(GtkWidget
* link
) {
107 if (is_sync_dialog_
) {
108 OneClickSigninHelper::LogConfirmHistogramValue(
109 clicked_learn_more_
?
110 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_UNDO
:
111 one_click_signin::HISTOGRAM_CONFIRM_UNDO
);
113 base::ResetAndReturn(&start_sync_callback_
).Run(
114 OneClickSigninSyncStarter::UNDO_SYNC
);
119 void OneClickSigninBubbleGtk::OnClickLearnMoreLink(GtkWidget
* link
) {
120 // We only want to log the Learn More click once per modal dialog instance.
121 if (is_sync_dialog_
&& !clicked_learn_more_
) {
122 OneClickSigninHelper::LogConfirmHistogramValue(
123 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE
);
124 clicked_learn_more_
= true;
126 Browser
* browser
= chrome::FindBrowserWithWindow(
127 gtk_window_get_transient_for(bubble_
->GetNativeWindow()));
129 chrome::NavigateParams
params(browser
, GURL(chrome::kChromeSyncLearnMoreURL
),
130 content::PAGE_TRANSITION_LINK
);
131 params
.disposition
= NEW_WINDOW
;
132 chrome::Navigate(¶ms
);
134 if (!is_sync_dialog_
) {
139 void OneClickSigninBubbleGtk::OnClickCloseButton(GtkWidget
* button
) {
140 DCHECK(is_sync_dialog_
);
141 OneClickSigninHelper::LogConfirmHistogramValue(
142 clicked_learn_more_
?
143 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_CLOSE
:
144 one_click_signin::HISTOGRAM_CONFIRM_CLOSE
);
145 base::ResetAndReturn(&start_sync_callback_
).Run(
146 OneClickSigninSyncStarter::UNDO_SYNC
);
150 OneClickSigninBubbleGtk::~OneClickSigninBubbleGtk() {
153 void OneClickSigninBubbleGtk::InitializeWidgets(
154 BrowserWindowGtk
* browser_window_gtk
) {
155 // Main dialog/bubble message.
156 std::string label_text
;
157 if (is_sync_dialog_
) {
159 l10n_util::GetStringFUTF8(IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW
,
162 label_text
= !error_message_
.empty() ? base::UTF16ToUTF8(error_message_
):
163 l10n_util::GetStringUTF8(IDS_ONE_CLICK_SIGNIN_BUBBLE_MESSAGE
);
166 message_label_
= gtk_label_new(label_text
.c_str());
167 gtk_label_set_line_wrap(GTK_LABEL(message_label_
), TRUE
);
168 gtk_misc_set_alignment(GTK_MISC(message_label_
), 0.0, 0.5);
170 gtk_widget_set_size_request(message_label_
, kModalDialogMessageWidth
, -1);
172 GtkThemeService
* const theme_provider
= GtkThemeService::GetFrom(
173 browser_window_gtk
->browser()->profile());
175 // Advanced link. Will not be displayed in the error bubble.
176 if (is_sync_dialog_
|| error_message_
.empty()) {
177 advanced_link_
= theme_provider
->BuildChromeLinkButton(
178 l10n_util::GetStringUTF8(
179 IDS_ONE_CLICK_SIGNIN_DIALOG_ADVANCED
));
180 g_signal_connect(advanced_link_
, "clicked",
181 G_CALLBACK(OnClickAdvancedLinkThunk
), this);
184 // The 'Learn More...' link.
185 learn_more_
= theme_provider
->BuildChromeLinkButton(
186 l10n_util::GetStringUTF8(IDS_LEARN_MORE
));
187 g_signal_connect(learn_more_
, "clicked",
188 G_CALLBACK(OnClickLearnMoreLinkThunk
), this);
190 // Make the OK and Undo buttons the same size horizontally.
191 GtkSizeGroup
* size_group
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
193 ok_button_
= gtk_button_new_with_label(l10n_util::GetStringUTF8(
194 is_sync_dialog_
? IDS_ONE_CLICK_SIGNIN_DIALOG_OK_BUTTON
:
196 g_signal_connect(ok_button_
, "clicked",
197 G_CALLBACK(OnClickOKThunk
), this);
198 gtk_size_group_add_widget(size_group
, ok_button_
);
200 if (!is_sync_dialog_
)
203 // The undo button is only in the modal dialog
204 undo_button_
= gtk_button_new_with_label(l10n_util::GetStringUTF8(
205 IDS_ONE_CLICK_SIGNIN_DIALOG_UNDO_BUTTON
).c_str());
206 g_signal_connect(undo_button_
, "clicked",
207 G_CALLBACK(OnClickUndoThunk
), this);
208 gtk_size_group_add_widget(size_group
, undo_button_
);
210 g_object_unref(size_group
);
212 // The email is always set for the sync dialog.
213 header_label_
= theme_provider
->BuildLabel(
214 l10n_util::GetStringUTF8(IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW
),
217 PangoAttrList
* attributes
= pango_attr_list_new();
218 pango_attr_list_insert(attributes
,
219 pango_attr_weight_new(PANGO_WEIGHT_BOLD
));
220 gtk_label_set_attributes(GTK_LABEL(header_label_
), attributes
);
221 pango_attr_list_unref(attributes
);
222 close_button_
.reset(CustomDrawButton::CloseButtonBubble(theme_provider
));
223 g_signal_connect(close_button_
->widget(), "clicked",
224 G_CALLBACK(OnClickCloseButtonThunk
), this);
227 GtkWidget
* OneClickSigninBubbleGtk::LayoutWidgets() {
228 // Setup the BubbleGtk content.
229 GtkWidget
* content_widget
= gtk_vbox_new(FALSE
, ui::kContentAreaSpacing
);
230 gtk_container_set_border_width(GTK_CONTAINER(content_widget
),
231 ui::kContentAreaBorder
);
233 GtkWidget
* top_line
= gtk_hbox_new(FALSE
, ui::kControlSpacing
);
234 gtk_box_pack_start(GTK_BOX(top_line
),
235 header_label_
, FALSE
, FALSE
, 0);
236 gtk_box_pack_end(GTK_BOX(top_line
),
237 close_button_
->widget(), FALSE
, FALSE
, 0);
238 gtk_box_pack_start(GTK_BOX(content_widget
),
239 top_line
, FALSE
, FALSE
, 0);
242 gtk_box_pack_start(GTK_BOX(content_widget
),
243 message_label_
, FALSE
, FALSE
, 0);
245 GtkWidget
* box
= gtk_hbox_new(FALSE
, ui::kControlSpacing
);
246 GtkWidget
* bottom_line
= gtk_hbox_new(FALSE
, ui::kControlSpacing
);
248 if (is_sync_dialog_
) {
249 gtk_box_pack_end(GTK_BOX(box
), learn_more_
, FALSE
, FALSE
, 0);
250 gtk_box_pack_start(GTK_BOX(content_widget
), box
, TRUE
, TRUE
, 0);
252 gtk_box_pack_start(GTK_BOX(content_widget
), bottom_line
, FALSE
, FALSE
, 0);
253 gtk_box_pack_start(GTK_BOX(bottom_line
), advanced_link_
, FALSE
, FALSE
, 0);
254 gtk_box_pack_end(GTK_BOX(bottom_line
), ok_button_
, FALSE
, FALSE
, 0);
255 gtk_box_pack_end(GTK_BOX(bottom_line
), undo_button_
, FALSE
, FALSE
, 0);
257 gtk_box_pack_start(GTK_BOX(box
), learn_more_
, FALSE
, FALSE
, 0);
258 gtk_box_pack_start(GTK_BOX(content_widget
), box
, TRUE
, TRUE
, 0);
260 // Don't display the Advanced link for the error bubble, and
261 // in this case align the OK button with the Learn More link.
262 if (error_message_
.empty()) {
263 gtk_box_pack_start(GTK_BOX(bottom_line
), advanced_link_
, FALSE
, FALSE
, 0);
264 gtk_box_pack_end(GTK_BOX(bottom_line
), ok_button_
, FALSE
, FALSE
, 0);
265 gtk_box_pack_start(GTK_BOX(content_widget
), bottom_line
, FALSE
, FALSE
, 0);
267 gtk_box_pack_end(GTK_BOX(box
), ok_button_
, FALSE
, FALSE
, 0);
270 return content_widget
;
273 void OneClickSigninBubbleGtk::ShowWidget(BrowserWindowGtk
* browser_window_gtk
,
274 GtkWidget
* content_widget
) {
275 if (is_sync_dialog_
) {
276 OneClickSigninHelper::LogConfirmHistogramValue(
277 one_click_signin::HISTOGRAM_CONFIRM_SHOWN
);
280 GtkThemeService
* const theme_provider
= GtkThemeService::GetFrom(
281 browser_window_gtk
->browser()->profile());
283 GtkWidget
* parent_widget
= is_sync_dialog_
?
284 browser_window_gtk
->GetToolbar()->widget() :
285 browser_window_gtk
->GetToolbar()->GetAppMenuButton();
286 gfx::Rect bounds
= gtk_util::WidgetBounds(parent_widget
);
287 int flags
= (is_sync_dialog_
? BubbleGtk::NO_ACCELERATORS
:
288 BubbleGtk::GRAB_INPUT
) |
289 BubbleGtk::MATCH_SYSTEM_THEME
|
290 BubbleGtk::POPUP_WINDOW
;
291 bubble_
= BubbleGtk::Show(parent_widget
, &bounds
, content_widget
,
292 is_sync_dialog_
? BubbleGtk::CENTER_OVER_RECT
:
293 BubbleGtk::ANCHOR_TOP_RIGHT
,
295 theme_provider
, this);
297 gtk_window_set_transient_for(bubble_
->GetNativeWindow(),
298 browser_window_gtk
->GetNativeWindow());
299 if (is_sync_dialog_
) {
300 gtk_window_set_modal(bubble_
->GetNativeWindow(), true);
301 gtk_window_set_focus(bubble_
->GetNativeWindow(), ok_button_
);
303 gtk_widget_grab_focus(ok_button_
);