- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / gtk / one_click_signin_bubble_gtk.cc
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.
4
5 #include "chrome/browser/ui/gtk/one_click_signin_bubble_gtk.h"
6
7 #include <gtk/gtk.h>
8
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"
28
29 static const int kModalDialogMessageWidth = 400;
30
31 OneClickSigninBubbleGtk::OneClickSigninBubbleGtk(
32     BrowserWindowGtk* browser_window_gtk,
33     BrowserWindow::OneClickSigninBubbleType type,
34     const string16& email,
35     const string16& error_message,
36     const BrowserWindow::StartSyncCallback& start_sync_callback)
37     : bubble_(NULL),
38       email_(email),
39       error_message_(error_message),
40       start_sync_callback_(start_sync_callback),
41       is_sync_dialog_(type!=BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE),
42       message_label_(NULL),
43       advanced_link_(NULL),
44       ok_button_(NULL),
45       undo_button_(NULL),
46       learn_more_(NULL),
47       header_label_(NULL),
48       clicked_learn_more_(false) {
49   InitializeWidgets(browser_window_gtk);
50   ShowWidget(browser_window_gtk, LayoutWidgets());
51 }
52
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);
60   }
61
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
65   // destroyed.
66   base::MessageLoopForUI::current()->DeleteSoon(FROM_HERE,
67                                                 close_button_.release());
68
69   delete this;
70 }
71
72 void OneClickSigninBubbleGtk::OnClickAdvancedLink(GtkWidget* link) {
73   if (is_sync_dialog_) {
74     OneClickSigninHelper::LogConfirmHistogramValue(
75       clicked_learn_more_ ?
76           one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_ADVANCED :
77           one_click_signin::HISTOGRAM_CONFIRM_ADVANCED);
78
79     base::ResetAndReturn(&start_sync_callback_).Run(
80         OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST);
81   } else {
82     Browser* browser = chrome::FindBrowserWithWindow(
83       gtk_window_get_transient_for(bubble_->GetNativeWindow()));
84     DCHECK(browser);
85     chrome::NavigateParams params(browser, GURL(chrome::kChromeUISettingsURL),
86         content::PAGE_TRANSITION_LINK);
87     params.disposition = CURRENT_TAB;
88     chrome::Navigate(&params);
89   }
90   bubble_->Close();
91 }
92
93 void OneClickSigninBubbleGtk::OnClickOK(GtkWidget* link) {
94   if (is_sync_dialog_) {
95     OneClickSigninHelper::LogConfirmHistogramValue(
96       clicked_learn_more_ ?
97           one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_OK :
98           one_click_signin::HISTOGRAM_CONFIRM_OK);
99
100     base::ResetAndReturn(&start_sync_callback_).Run(
101         OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS);
102   }
103   bubble_->Close();
104 }
105
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);
112
113     base::ResetAndReturn(&start_sync_callback_).Run(
114         OneClickSigninSyncStarter::UNDO_SYNC);
115   }
116   bubble_->Close();
117 }
118
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;
125   }
126   Browser* browser = chrome::FindBrowserWithWindow(
127       gtk_window_get_transient_for(bubble_->GetNativeWindow()));
128   DCHECK(browser);
129   chrome::NavigateParams params(browser, GURL(chrome::kChromeSyncLearnMoreURL),
130       content::PAGE_TRANSITION_LINK);
131   params.disposition = NEW_WINDOW;
132   chrome::Navigate(&params);
133
134   if (!is_sync_dialog_) {
135     bubble_->Close();
136   }
137 }
138
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);
147   bubble_->Close();
148 }
149
150 OneClickSigninBubbleGtk::~OneClickSigninBubbleGtk() {
151 }
152
153 void OneClickSigninBubbleGtk::InitializeWidgets(
154     BrowserWindowGtk* browser_window_gtk) {
155   // Main dialog/bubble message.
156   std::string label_text;
157   if (is_sync_dialog_) {
158     label_text =
159         l10n_util::GetStringFUTF8(IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW,
160                                   email_);
161   } else {
162     label_text = !error_message_.empty() ? UTF16ToUTF8(error_message_):
163         l10n_util::GetStringUTF8(IDS_ONE_CLICK_SIGNIN_BUBBLE_MESSAGE);
164   }
165
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);
169   if (is_sync_dialog_)
170     gtk_widget_set_size_request(message_label_, kModalDialogMessageWidth, -1);
171
172   GtkThemeService* const theme_provider = GtkThemeService::GetFrom(
173       browser_window_gtk->browser()->profile());
174
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);
182   }
183
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);
189
190   // Make the OK and Undo buttons the same size horizontally.
191   GtkSizeGroup* size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
192   // OK Button.
193   ok_button_ = gtk_button_new_with_label(l10n_util::GetStringUTF8(
194       is_sync_dialog_ ? IDS_ONE_CLICK_SIGNIN_DIALOG_OK_BUTTON :
195                         IDS_OK).c_str());
196   g_signal_connect(ok_button_, "clicked",
197                    G_CALLBACK(OnClickOKThunk), this);
198   gtk_size_group_add_widget(size_group, ok_button_);
199
200   if (!is_sync_dialog_)
201     return;
202
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_);
209
210   g_object_unref(size_group);
211
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),
215       ui::kGdkBlack);
216
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);
225 }
226
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);
232   if (header_label_) {
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);
240   }
241
242   gtk_box_pack_start(GTK_BOX(content_widget),
243                      message_label_, FALSE, FALSE, 0);
244
245   GtkWidget* box = gtk_hbox_new(FALSE, ui::kControlSpacing);
246   GtkWidget* bottom_line = gtk_hbox_new(FALSE, ui::kControlSpacing);
247
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);
251
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);
256   } else {
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);
259
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);
266     } else {
267       gtk_box_pack_end(GTK_BOX(box), ok_button_, FALSE, FALSE, 0);
268     }
269   }
270   return content_widget;
271 }
272
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);
278   }
279
280   GtkThemeService* const theme_provider = GtkThemeService::GetFrom(
281       browser_window_gtk->browser()->profile());
282
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,
294                             flags,
295                             theme_provider, this);
296
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_);
302   } else {
303     gtk_widget_grab_focus(ok_button_);
304   }
305 }