- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / gtk / extensions / extension_installed_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/extensions/extension_installed_bubble_gtk.h"
6
7 #include <string>
8
9 #include "base/i18n/rtl.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/extensions/api/commands/command_service.h"
13 #include "chrome/browser/extensions/extension_action.h"
14 #include "chrome/browser/extensions/extension_action_manager.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_dialogs.h"
17 #include "chrome/browser/ui/gtk/browser_actions_toolbar_gtk.h"
18 #include "chrome/browser/ui/gtk/browser_toolbar_gtk.h"
19 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
20 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
21 #include "chrome/browser/ui/gtk/gtk_util.h"
22 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
23 #include "chrome/browser/ui/singleton_tabs.h"
24 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h"
25 #include "chrome/common/extensions/extension.h"
26 #include "chrome/common/url_constants.h"
27 #include "content/public/browser/notification_details.h"
28 #include "content/public/browser/notification_source.h"
29 #include "grit/chromium_strings.h"
30 #include "grit/generated_resources.h"
31 #include "grit/theme_resources.h"
32 #include "ui/base/gtk/gtk_hig_constants.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/resource/resource_bundle.h"
35 #include "ui/gfx/gtk_util.h"
36
37 using extensions::Extension;
38 using extensions::ExtensionActionManager;
39
40 namespace {
41
42 const int kHorizontalColumnSpacing = 10;
43 const int kIconPadding = 3;
44 const int kIconSize = 43;
45 const int kTextColumnVerticalSpacing = 7;
46 const int kTextColumnWidth = 350;
47
48 }  // namespace
49
50 namespace chrome {
51
52 void ShowExtensionInstalledBubble(const Extension* extension,
53                                   Browser* browser,
54                                   const SkBitmap& icon) {
55   ExtensionInstalledBubbleGtk::Show(extension, browser, icon);
56 }
57
58 }  // namespace chrome
59
60 void ExtensionInstalledBubbleGtk::Show(const Extension* extension,
61                                        Browser* browser,
62                                        const SkBitmap& icon) {
63   new ExtensionInstalledBubbleGtk(extension, browser, icon);
64 }
65
66 ExtensionInstalledBubbleGtk::ExtensionInstalledBubbleGtk(
67     const Extension* extension, Browser *browser, const SkBitmap& icon)
68     : bubble_(this, extension, browser, icon) {
69 }
70
71 ExtensionInstalledBubbleGtk::~ExtensionInstalledBubbleGtk() {}
72
73 void ExtensionInstalledBubbleGtk::OnDestroy(GtkWidget* widget) {
74   gtk_bubble_ = NULL;
75   delete this;
76 }
77
78 bool ExtensionInstalledBubbleGtk::MaybeShowNow() {
79   BrowserWindowGtk* browser_window =
80       BrowserWindowGtk::GetBrowserWindowForNativeWindow(
81           bubble_.browser()->window()->GetNativeWindow());
82
83   GtkWidget* reference_widget = NULL;
84
85   if (bubble_.type() == bubble_.BROWSER_ACTION) {
86     BrowserActionsToolbarGtk* toolbar =
87         browser_window->GetToolbar()->GetBrowserActionsToolbar();
88     if (toolbar->animating())
89       return false;
90
91     reference_widget = toolbar->GetBrowserActionWidget(bubble_.extension());
92     // glib delays recalculating layout, but we need reference_widget to know
93     // its coordinates, so we force a check_resize here.
94     gtk_container_check_resize(GTK_CONTAINER(
95         browser_window->GetToolbar()->widget()));
96     // If the widget is not visible then browser_window could be incognito
97     // with this extension disabled. Try showing it on the chevron.
98     // If that fails, fall back to default position.
99     if (reference_widget && !gtk_widget_get_visible(reference_widget)) {
100       reference_widget = gtk_widget_get_visible(toolbar->chevron()) ?
101           toolbar->chevron() : NULL;
102     }
103   } else if (bubble_.type() == bubble_.PAGE_ACTION) {
104     LocationBarViewGtk* location_bar_view =
105         browser_window->GetToolbar()->GetLocationBarView();
106     ExtensionAction* page_action =
107         ExtensionActionManager::Get(bubble_.browser()->profile())->
108         GetPageAction(*bubble_.extension());
109     location_bar_view->SetPreviewEnabledPageAction(page_action,
110                                                    true);  // preview_enabled
111     reference_widget = location_bar_view->GetPageActionWidget(page_action);
112     // glib delays recalculating layout, but we need reference_widget to know
113     // its coordinates, so we force a check_resize here.
114     gtk_container_check_resize(GTK_CONTAINER(
115         browser_window->GetToolbar()->widget()));
116     DCHECK(reference_widget);
117   } else if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) {
118     LocationBarViewGtk* location_bar_view =
119         browser_window->GetToolbar()->GetLocationBarView();
120     reference_widget = location_bar_view->location_entry_widget();
121     DCHECK(reference_widget);
122   }
123
124   // Default case.
125   if (reference_widget == NULL)
126     reference_widget = browser_window->GetToolbar()->GetAppMenuButton();
127
128   GtkThemeService* theme_provider = GtkThemeService::GetFrom(
129       bubble_.browser()->profile());
130
131   // Setup the BubbleGtk content.
132   GtkWidget* bubble_content = gtk_hbox_new(FALSE, kHorizontalColumnSpacing);
133   gtk_container_set_border_width(GTK_CONTAINER(bubble_content),
134                                  ui::kContentAreaBorder);
135
136   if (!bubble_.icon().isNull()) {
137     // Scale icon down to 43x43, but allow smaller icons (don't scale up).
138     GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(bubble_.icon());
139     gfx::Size size(bubble_.icon().width(), bubble_.icon().height());
140     if (size.width() > kIconSize || size.height() > kIconSize) {
141       if (size.width() > size.height()) {
142         size.set_height(size.height() * kIconSize / size.width());
143         size.set_width(kIconSize);
144       } else {
145         size.set_width(size.width() * kIconSize / size.height());
146         size.set_height(kIconSize);
147       }
148
149       GdkPixbuf* old = pixbuf;
150       pixbuf = gdk_pixbuf_scale_simple(pixbuf, size.width(), size.height(),
151                                        GDK_INTERP_BILINEAR);
152       g_object_unref(old);
153     }
154
155     // Put Icon in top of the left column.
156     GtkWidget* icon_column = gtk_vbox_new(FALSE, 0);
157     // Use 3 pixel padding to get visual balance with BubbleGtk border on the
158     // left.
159     gtk_box_pack_start(GTK_BOX(bubble_content), icon_column, FALSE, FALSE,
160                        kIconPadding);
161     GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);
162     g_object_unref(pixbuf);
163     gtk_box_pack_start(GTK_BOX(icon_column), image, FALSE, FALSE, 0);
164   }
165
166   // Center text column.
167   GtkWidget* text_column = gtk_vbox_new(FALSE, kTextColumnVerticalSpacing);
168   gtk_box_pack_start(GTK_BOX(bubble_content), text_column, FALSE, FALSE, 0);
169
170   // Heading label.
171   GtkWidget* heading_label = gtk_label_new(NULL);
172   string16 extension_name = UTF8ToUTF16(bubble_.extension()->name());
173   base::i18n::AdjustStringForLocaleDirection(&extension_name);
174   std::string heading_text = l10n_util::GetStringFUTF8(
175       IDS_EXTENSION_INSTALLED_HEADING, extension_name);
176   char* markup = g_markup_printf_escaped("<span size=\"larger\">%s</span>",
177       heading_text.c_str());
178   gtk_label_set_markup(GTK_LABEL(heading_label), markup);
179   g_free(markup);
180
181   gtk_util::SetLabelWidth(heading_label, kTextColumnWidth);
182   gtk_box_pack_start(GTK_BOX(text_column), heading_label, FALSE, FALSE, 0);
183
184   bool has_keybinding = false;
185
186   // Browser action label.
187   if (bubble_.type() == bubble_.BROWSER_ACTION) {
188     extensions::CommandService* command_service =
189         extensions::CommandService::Get(bubble_.browser()->profile());
190     extensions::Command browser_action_command;
191     GtkWidget* info_label;
192     if (!command_service->GetBrowserActionCommand(
193             bubble_.extension()->id(),
194             extensions::CommandService::ACTIVE_ONLY,
195             &browser_action_command,
196             NULL)) {
197       info_label = gtk_label_new(l10n_util::GetStringUTF8(
198           IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO).c_str());
199     } else {
200       info_label = gtk_label_new(l10n_util::GetStringFUTF8(
201           IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO_WITH_SHORTCUT,
202           browser_action_command.accelerator().GetShortcutText()).c_str());
203       has_keybinding = true;
204     }
205     gtk_util::SetLabelWidth(info_label, kTextColumnWidth);
206     gtk_box_pack_start(GTK_BOX(text_column), info_label, FALSE, FALSE, 0);
207   }
208
209   // Page action label.
210   if (bubble_.type() == bubble_.PAGE_ACTION) {
211     extensions::CommandService* command_service =
212         extensions::CommandService::Get(bubble_.browser()->profile());
213     extensions::Command page_action_command;
214     GtkWidget* info_label;
215     if (!command_service->GetPageActionCommand(
216             bubble_.extension()->id(),
217             extensions::CommandService::ACTIVE_ONLY,
218             &page_action_command,
219             NULL)) {
220       info_label = gtk_label_new(l10n_util::GetStringUTF8(
221           IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO).c_str());
222     } else {
223       info_label = gtk_label_new(l10n_util::GetStringFUTF8(
224           IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO_WITH_SHORTCUT,
225           page_action_command.accelerator().GetShortcutText()).c_str());
226       has_keybinding = true;
227     }
228     gtk_util::SetLabelWidth(info_label, kTextColumnWidth);
229     gtk_box_pack_start(GTK_BOX(text_column), info_label, FALSE, FALSE, 0);
230   }
231
232   // Omnibox keyword label.
233   if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) {
234     GtkWidget* info_label = gtk_label_new(l10n_util::GetStringFUTF8(
235         IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO,
236         UTF8ToUTF16(extensions::OmniboxInfo::GetKeyword(
237             bubble_.extension()))).c_str());
238     gtk_util::SetLabelWidth(info_label, kTextColumnWidth);
239     gtk_box_pack_start(GTK_BOX(text_column), info_label, FALSE, FALSE, 0);
240   }
241
242   if (has_keybinding) {
243     GtkWidget* manage_link = theme_provider->BuildChromeLinkButton(
244         l10n_util::GetStringUTF8(IDS_EXTENSION_INSTALLED_MANAGE_SHORTCUTS));
245     GtkWidget* link_hbox = gtk_hbox_new(FALSE, 0);
246     // Stick it in an hbox so it doesn't expand to the whole width.
247     gtk_box_pack_end(GTK_BOX(link_hbox), manage_link, FALSE, FALSE, 0);
248     gtk_box_pack_start(GTK_BOX(text_column), link_hbox, FALSE, FALSE, 0);
249     g_signal_connect(manage_link, "clicked",
250                      G_CALLBACK(OnLinkClickedThunk), this);
251   } else {
252     // Manage label.
253     GtkWidget* manage_label = gtk_label_new(
254         l10n_util::GetStringUTF8(IDS_EXTENSION_INSTALLED_MANAGE_INFO).c_str());
255     gtk_util::SetLabelWidth(manage_label, kTextColumnWidth);
256     gtk_box_pack_start(GTK_BOX(text_column), manage_label, FALSE, FALSE, 0);
257   }
258
259   // Create and pack the close button.
260   GtkWidget* close_column = gtk_vbox_new(FALSE, 0);
261   gtk_box_pack_start(GTK_BOX(bubble_content), close_column, FALSE, FALSE, 0);
262   close_button_.reset(CustomDrawButton::CloseButtonBubble(theme_provider));
263   g_signal_connect(close_button_->widget(), "clicked",
264                    G_CALLBACK(OnButtonClick), this);
265   gtk_box_pack_start(GTK_BOX(close_column), close_button_->widget(),
266       FALSE, FALSE, 0);
267
268   BubbleGtk::FrameStyle frame_style = BubbleGtk::ANCHOR_TOP_RIGHT;
269
270   gfx::Rect bounds = gtk_util::WidgetBounds(reference_widget);
271   if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) {
272     // Reverse the arrow for omnibox keywords, since the bubble will be on the
273     // other side of the window. We also clear the width to avoid centering
274     // the popup on the URL bar.
275     frame_style = BubbleGtk::ANCHOR_TOP_LEFT;
276     if (base::i18n::IsRTL())
277       bounds.Offset(bounds.width(), 0);
278     bounds.set_width(0);
279   }
280
281   gtk_bubble_ = BubbleGtk::Show(reference_widget,
282                                 &bounds,
283                                 bubble_content,
284                                 frame_style,
285                                 BubbleGtk::MATCH_SYSTEM_THEME |
286                                 BubbleGtk::POPUP_WINDOW |
287                                 BubbleGtk::GRAB_INPUT,
288                                 theme_provider,
289                                 this);
290   g_signal_connect(bubble_content, "destroy",
291                    G_CALLBACK(&OnDestroyThunk), this);
292
293   // gtk_bubble_ is now the owner of |this| and deletes it when the bubble
294   // goes away.
295   bubble_.IgnoreBrowserClosing();
296   return true;
297 }
298
299 // static
300 void ExtensionInstalledBubbleGtk::OnButtonClick(GtkWidget* button,
301     ExtensionInstalledBubbleGtk* bubble) {
302   if (button == bubble->close_button_->widget()) {
303     bubble->gtk_bubble_->Close();
304   } else {
305     NOTREACHED();
306   }
307 }
308
309 void ExtensionInstalledBubbleGtk::OnLinkClicked(GtkWidget* widget) {
310   gtk_bubble_->Close();
311
312   std::string configure_url = chrome::kChromeUIExtensionsURL;
313   configure_url += chrome::kExtensionConfigureCommandsSubPage;
314   chrome::NavigateParams params(
315       chrome::GetSingletonTabNavigateParams(
316           bubble_.browser(), GURL(configure_url.c_str())));
317   chrome::Navigate(&params);
318 }
319
320 void ExtensionInstalledBubbleGtk::BubbleClosing(BubbleGtk* bubble,
321                                                 bool closed_by_escape) {
322   if (bubble_.extension() && bubble_.type() == bubble_.PAGE_ACTION) {
323     // Turn the page action preview off.
324     BrowserWindowGtk* browser_window =
325           BrowserWindowGtk::GetBrowserWindowForNativeWindow(
326               bubble_.browser()->window()->GetNativeWindow());
327     LocationBarViewGtk* location_bar_view =
328         browser_window->GetToolbar()->GetLocationBarView();
329     location_bar_view->SetPreviewEnabledPageAction(
330         ExtensionActionManager::Get(bubble_.browser()->profile())->
331         GetPageAction(*bubble_.extension()),
332         false);  // preview_enabled
333   }
334 }