Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: chrome/browser/ui/gtk/web_intent_picker_gtk.cc

Issue 12225076: Delete most web intents code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/web_intent_picker_gtk.h"
6
7 #include <algorithm>
8
9 #include "base/i18n/rtl.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/favicon/favicon_service.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/tab_contents/tab_util.h"
14 #include "chrome/browser/ui/browser_finder.h"
15 #include "chrome/browser/ui/browser_window.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/custom_button.h"
19 #include "chrome/browser/ui/gtk/event_utils.h"
20 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
21 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
22 #include "chrome/browser/ui/gtk/gtk_util.h"
23 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
24 #include "chrome/browser/ui/gtk/throbber_gtk.h"
25 #include "chrome/browser/ui/intents/web_intent_picker_controller.h"
26 #include "chrome/browser/ui/intents/web_intent_picker_delegate.h"
27 #include "chrome/browser/ui/intents/web_intent_picker_model.h"
28 #include "chrome/common/chrome_notification_types.h"
29 #include "content/public/browser/notification_source.h"
30 #include "content/public/browser/notification_types.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "content/public/browser/render_widget_host_view.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/browser/web_contents_view.h"
35 #include "googleurl/src/gurl.h"
36 #include "grit/chromium_strings.h"
37 #include "grit/generated_resources.h"
38 #include "grit/google_chrome_strings.h"
39 #include "grit/theme_resources.h"
40 #include "ui/base/gtk/gtk_hig_constants.h"
41 #include "ui/base/gtk/gtk_signal_registrar.h"
42 #include "ui/base/l10n/l10n_util.h"
43 #include "ui/base/resource/resource_bundle.h"
44 #include "ui/base/text/text_elider.h"
45 #include "ui/gfx/gtk_util.h"
46 #include "ui/gfx/image/image.h"
47
48 using content::WebContents;
49
50 namespace {
51
52 // The pixel size of the header label when using a non-native theme.
53 const int kHeaderLabelPixelSize = 15;
54
55 // The pixel size of the font of the main content of the dialog.
56 const int kMainContentPixelSize = 13;
57
58 // Indices of the extension row widgets.
59 enum {
60 kIconIndex,
61 kTitleLinkIndex,
62 kStarsIndex,
63 kInstallButtonIndex,
64 };
65
66 GtkThemeService *GetThemeService(WebContents* web_contents) {
67 Profile* profile =
68 Profile::FromBrowserContext(web_contents->GetBrowserContext());
69 return GtkThemeService::GetFrom(profile);
70 }
71
72 // Set the image of |button| to |pixbuf|.
73 void SetServiceButtonImage(GtkWidget* button, GdkPixbuf* pixbuf) {
74 gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_pixbuf(pixbuf));
75 gtk_button_set_image_position(GTK_BUTTON(button), GTK_POS_LEFT);
76 }
77
78 void SetWidgetFontSizeCallback(GtkWidget* widget, gpointer data) {
79 if (GTK_IS_LABEL(widget)) {
80 int* size = static_cast<int*>(data);
81 gtk_util::ForceFontSizePixels(widget, *size);
82 return;
83 }
84
85 if (GTK_IS_CONTAINER(widget))
86 gtk_container_forall(GTK_CONTAINER(widget), SetWidgetFontSizeCallback,
87 data);
88 }
89
90 void SetWidgetFontSize(GtkWidget* widget, int size) {
91 gtk_container_forall(GTK_CONTAINER(widget), SetWidgetFontSizeCallback, &size);
92 }
93
94 // Get the index of the row containing |widget|. Assume the widget is the child
95 // of an hbox, which is a child of a vbox. The hbox represents a row, and the
96 // vbox the full table.
97 size_t GetExtensionWidgetRow(GtkWidget* widget) {
98 GtkWidget* hbox = gtk_widget_get_parent(widget);
99 DCHECK(hbox);
100 GtkWidget* vbox = gtk_widget_get_parent(hbox);
101 DCHECK(vbox);
102 GList* hbox_list = gtk_container_get_children(GTK_CONTAINER(vbox));
103 gint index = g_list_index(hbox_list, hbox);
104 DCHECK(index != -1);
105 g_list_free(hbox_list);
106
107 return index;
108 }
109
110 // A gtk_container_foreach callback to enable/disable a widget.
111 void EnableWidgetCallback(GtkWidget* widget, gpointer data) {
112 gtk_widget_set_sensitive(widget, *static_cast<gboolean*>(data));
113 }
114
115 // Create a new widget displaying |rating| as |kNumStarsPerRating| star images.
116 // Rating should be in the range [0, kNumStarsPerRating].
117 GtkWidget* CreateStarsWidget(double rating) {
118 const int kNumStarsPerRating = 5; // Number of stars in a rating.
119 const int kStarSpacing = 1; // Spacing between stars in pixels.
120 GtkWidget* hbox = gtk_hbox_new(FALSE, kStarSpacing);
121
122 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
123 for (int i = 0; i < kNumStarsPerRating; ++i) {
124 GdkPixbuf* star = rb.GetNativeImageNamed(
125 WebIntentPicker::GetNthStarImageIdFromCWSRating(rating, i),
126 ui::ResourceBundle::RTL_ENABLED).ToGdkPixbuf();
127 gtk_box_pack_start(GTK_BOX(hbox), gtk_image_new_from_pixbuf(star),
128 FALSE, FALSE, 0);
129 }
130
131 return hbox;
132 }
133
134 } // namespace
135
136 // Create a new Widget for the "waiting for CWS" display.
137
138 class WaitingDialog {
139 public:
140 explicit WaitingDialog(GtkThemeService* theme_service);
141 ~WaitingDialog();
142
143 GtkWidget* widget() const { return widget_.get(); }
144 private:
145 // Initialize the widget.
146 void Init();
147
148 // The actual GtkWidget
149 ui::OwnedWidgetGtk widget_;
150
151 // Waiting throbber.
152 scoped_ptr<ThrobberGtk> throbber_;
153
154 // Weak pointer to theme service.
155 GtkThemeService* theme_service_;
156 };
157
158 WaitingDialog::WaitingDialog(GtkThemeService* theme_service)
159 : theme_service_(theme_service) {
160 DCHECK(theme_service_);
161 Init();
162 }
163
164 WaitingDialog::~WaitingDialog() {
165 widget_.Destroy();
166 }
167
168 void WaitingDialog::Init() {
169 const int kDialogSpacing = 30;
170
171 widget_.Own(gtk_vbox_new(FALSE, 0));
172 GtkWidget* vbox = widget_.get();
173
174 // Create throbber
175 ThrobberGtk* throbber = new ThrobberGtk(theme_service_);
176 GtkWidget* throbber_alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
177 gtk_alignment_set_padding(GTK_ALIGNMENT(throbber_alignment), kDialogSpacing,
178 kMainContentPixelSize, 0, 0);
179 gtk_container_add(GTK_CONTAINER(throbber_alignment), throbber->widget());
180 gtk_box_pack_start(GTK_BOX(vbox), throbber_alignment, TRUE, TRUE, 0);
181
182 // Add the message text.
183 GtkWidget* message_label = theme_service_->BuildLabel(
184 l10n_util::GetStringUTF8(IDS_INTENT_PICKER_WAIT_FOR_CWS).c_str(),
185 ui::kGdkBlack);
186
187 GtkWidget* label_alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
188 gtk_alignment_set_padding(GTK_ALIGNMENT(label_alignment),
189 kMainContentPixelSize, kDialogSpacing, 0, 0);
190 gtk_container_add(GTK_CONTAINER(label_alignment), message_label);
191 gtk_box_pack_start(GTK_BOX(vbox), label_alignment, TRUE, TRUE, 0);
192
193 // TODO(groby): use IDR_SPEECH_INPUT_SPINNER. Pending fix for ThrobberGtk.
194 // Animate throbber
195 throbber->Start();
196 }
197
198 // static
199 WebIntentPicker* WebIntentPicker::Create(WebContents* web_contents,
200 WebIntentPickerDelegate* delegate,
201 WebIntentPickerModel* model) {
202 return new WebIntentPickerGtk(web_contents, delegate, model);
203 }
204
205 WebIntentPickerGtk::WebIntentPickerGtk(WebContents* web_contents,
206 WebIntentPickerDelegate* delegate,
207 WebIntentPickerModel* model)
208 : web_contents_(web_contents),
209 delegate_(delegate),
210 model_(model),
211 contents_(NULL),
212 header_label_(NULL),
213 button_vbox_(NULL),
214 cws_label_(NULL),
215 extensions_vbox_(NULL),
216 service_hbox_(NULL),
217 window_(NULL) {
218 DCHECK(delegate_ != NULL);
219
220 model_->set_observer(this);
221 InitContents();
222 UpdateInstalledServices();
223 UpdateCWSLabel();
224 UpdateSuggestedExtensions();
225
226 GtkThemeService* theme_service = GetThemeService(web_contents);
227 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
228 content::Source<ThemeService>(theme_service));
229 theme_service->InitThemesFor(this);
230
231 window_ = new ConstrainedWindowGtk(web_contents, this);
232
233 if (model_->IsInlineDisposition())
234 OnInlineDisposition(string16(), model_->inline_disposition_url());
235 }
236
237 WebIntentPickerGtk::~WebIntentPickerGtk() {
238 }
239
240 void WebIntentPickerGtk::Close() {
241 window_->CloseWebContentsModalDialog();
242 if (inline_disposition_web_contents_.get())
243 inline_disposition_web_contents_->OnCloseStarted();
244 }
245
246 void WebIntentPickerGtk::SetActionString(const string16& action) {
247 header_label_text_ = action;
248 if (header_label_)
249 gtk_label_set_text(GTK_LABEL(header_label_), UTF16ToUTF8(action).c_str());
250 }
251
252 void WebIntentPickerGtk::OnExtensionInstallSuccess(const std::string& id) {
253 RemoveThrobber();
254 }
255
256 void WebIntentPickerGtk::OnExtensionInstallFailure(const std::string& id) {
257 // The throbber has an alignment as its parent, so it must be used instead of
258 // the throbber to find the extension row.
259 size_t index =
260 GetExtensionWidgetRow(gtk_widget_get_parent(throbber_->widget()));
261 GList* vbox_list =
262 gtk_container_get_children(GTK_CONTAINER(extensions_vbox_));
263 GtkWidget* hbox = static_cast<GtkWidget*>(g_list_nth_data(vbox_list, index));
264
265 RemoveThrobber();
266 gtk_widget_show_all(hbox);
267 g_list_free(vbox_list);
268 SetWidgetsEnabled(true);
269 }
270
271 void WebIntentPickerGtk::OnModelChanged(WebIntentPickerModel* model) {
272 if (waiting_dialog_.get() && !model->IsWaitingForSuggestions()) {
273 waiting_dialog_.reset();
274 InitMainContents();
275 }
276 UpdateInstalledServices();
277 UpdateCWSLabel();
278 UpdateSuggestedExtensions();
279 SetActionString(header_label_text_);
280 }
281
282 void WebIntentPickerGtk::OnFaviconChanged(WebIntentPickerModel* model,
283 size_t index) {
284 UpdateInstalledServices();
285 }
286
287 void WebIntentPickerGtk::OnExtensionIconChanged(
288 WebIntentPickerModel* model,
289 const std::string& extension_id) {
290 UpdateSuggestedExtensions();
291 }
292
293 void WebIntentPickerGtk::OnInlineDisposition(const string16&,
294 const GURL& url) {
295 DCHECK(delegate_);
296 Profile* profile =
297 Profile::FromBrowserContext(web_contents_->GetBrowserContext());
298 inline_disposition_web_contents_.reset(
299 delegate_->CreateWebContentsForInlineDisposition(profile, url));
300 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
301 inline_disposition_delegate_.reset(
302 new WebIntentInlineDispositionDelegate(
303 this, inline_disposition_web_contents_.get(), browser));
304
305 inline_disposition_web_contents_->GetController().LoadURL(
306 url, content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL,
307 std::string());
308
309 // Replace the picker contents with the inline disposition.
310 ClearContents();
311 gtk_widget_set_size_request(contents_, -1, -1);
312 window_->BackgroundColorChanged();
313
314 GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
315 GtkThemeService* theme_service = GetThemeService(web_contents_);
316
317 service_hbox_ = gtk_hbox_new(FALSE, ui::kControlSpacing);
318 // TODO(gbillock): Eventually get the service icon button here.
319
320 // Intent action label.
321 const WebIntentPickerModel::InstalledService* service =
322 model_->GetInstalledServiceWithURL(url);
323 GtkWidget* action_label = gtk_label_new(UTF16ToUTF8(service->title).c_str());
324 gtk_util::ForceFontSizePixels(action_label, kMainContentPixelSize);
325 // Hardcode color; don't allow theming.
326 gtk_util::SetLabelColor(action_label, &ui::kGdkBlack);
327
328 GtkWidget* label_alignment = gtk_alignment_new(0, 0.5f, 0, 0);
329 gtk_container_add(GTK_CONTAINER(label_alignment), action_label);
330 GtkWidget* indent_label = gtk_util::IndentWidget(label_alignment);
331
332 gtk_box_pack_start(GTK_BOX(service_hbox_), indent_label, FALSE, TRUE, 0);
333 g_signal_connect(service_hbox_, "destroy", G_CALLBACK(gtk_widget_destroyed),
334 &service_hbox_);
335
336 // Add link for "choose another service" if other suggestions are available
337 // or if more than one (the current) service is installed.
338 if (model_->show_use_another_service() &&
339 (model_->GetInstalledServiceCount() > 1 ||
340 model_->GetSuggestedExtensionCount())) {
341 GtkWidget* use_alternate_link = theme_service->BuildChromeLinkButton(
342 l10n_util::GetStringUTF8(
343 IDS_INTENT_PICKER_USE_ALTERNATE_SERVICE).c_str());
344 gtk_chrome_link_button_set_use_gtk_theme(
345 GTK_CHROME_LINK_BUTTON(use_alternate_link),
346 theme_service->UsingNativeTheme());
347 gtk_util::ForceFontSizePixels(
348 GTK_CHROME_LINK_BUTTON(use_alternate_link)->label,
349 kMainContentPixelSize);
350 g_signal_connect(use_alternate_link, "clicked",
351 G_CALLBACK(OnChooseAnotherServiceClickThunk), this);
352 GtkWidget* link_alignment = gtk_alignment_new(0, 0.5f, 0, 0);
353 gtk_container_add(GTK_CONTAINER(link_alignment), use_alternate_link);
354 gtk_box_pack_start(GTK_BOX(service_hbox_), link_alignment, TRUE, TRUE, 0);
355 }
356 AddCloseButton(service_hbox_);
357
358 // The header box
359 gtk_container_add(GTK_CONTAINER(vbox), service_hbox_);
360
361 // hbox for the web contents, so we can have spacing on the borders.
362 GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
363 gtk_alignment_set_padding(
364 GTK_ALIGNMENT(alignment), 0, ui::kContentAreaBorder,
365 ui::kContentAreaBorder, ui::kContentAreaBorder);
366 gfx::NativeView web_contents_widget =
367 inline_disposition_web_contents_->GetView()->GetNativeView();
368 gtk_container_add(GTK_CONTAINER(alignment), web_contents_widget);
369 gtk_container_add(GTK_CONTAINER(vbox), alignment);
370 gtk_container_add(GTK_CONTAINER(contents_), vbox);
371
372 gfx::Size size = GetMinInlineDispositionSize();
373 gtk_widget_set_size_request(web_contents_widget,
374 size.width(), size.height());
375 gtk_widget_show_all(contents_);
376
377 inline_disposition_delegate_->SetRenderViewSizeLimits();
378 inline_disposition_web_contents_->GetView()->SetInitialFocus();
379 host_signals_.reset(new ui::GtkSignalRegistrar());
380 host_signals_->Connect(
381 web_contents_->GetRenderViewHost()->GetView()->GetNativeView(),
382 "size-allocate",
383 G_CALLBACK(OnHostContentsSizeAllocateThunk), this);
384 }
385
386 void WebIntentPickerGtk::OnInlineDispositionAutoResize(const gfx::Size& size) {
387 gfx::NativeView web_contents_widget =
388 inline_disposition_web_contents_->GetView()->GetNativeView();
389 gtk_widget_set_size_request(web_contents_widget, size.width(), size.height());
390 }
391
392 gfx::Size WebIntentPickerGtk::GetMaxInlineDispositionSize() {
393 gfx::Rect tab_bounds(web_contents_->GetRenderViewHost()->
394 GetView()->GetNativeView()->allocation);
395 GtkRequisition req = {};
396 if (service_hbox_)
397 gtk_widget_size_request(service_hbox_, &req);
398
399 tab_bounds.Inset(2 * ui::kContentAreaBorder,
400 2 * ui::kContentAreaBorder + req.height);
401 return tab_bounds.size();
402 }
403
404 void WebIntentPickerGtk::OnPendingAsyncCompleted() {
405 // Requests to both the WebIntentService and the Chrome Web Store have
406 // completed. If there are any services, installed or suggested, there's
407 // nothing to do.
408 if (model_->GetInstalledServiceCount() ||
409 model_->GetSuggestedExtensionCount())
410 return;
411
412 // If there are no installed or suggested services at this point,
413 // inform the user about it.
414
415 // Replace the picker contents with dialog box.
416 ClearContents();
417 GtkWidget* sub_contents = CreateSubContents(contents_);
418
419 AddCloseButton(contents_);
420 AddTitle(sub_contents);
421
422 // Replace the dialog header.
423 DCHECK(header_label_);
424 gtk_label_set_text(
425 GTK_LABEL(header_label_),
426 l10n_util::GetStringUTF8(IDS_INTENT_PICKER_NO_SERVICES_TITLE).c_str());
427
428 // Add the message text.
429 GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
430 gtk_box_pack_start(GTK_BOX(sub_contents), hbox, TRUE, TRUE, 0);
431 GtkThemeService* theme_service = GetThemeService(web_contents_);
432 GtkWidget* no_service_label = theme_service->BuildLabel(
433 l10n_util::GetStringUTF8(IDS_INTENT_PICKER_NO_SERVICES).c_str(),
434 ui::kGdkBlack);
435 gtk_label_set_line_wrap(GTK_LABEL(no_service_label), TRUE);
436 gtk_misc_set_alignment(GTK_MISC(no_service_label), 0, 0);
437 // Set the label width to the size of |sub_contents|, which we don't have
438 // access to yet, by calculating the main content width minus borders.
439 gtk_util::SetLabelWidth(no_service_label,
440 kWindowMinWidth - 2 * ui::kContentAreaBorder);
441 gtk_box_pack_start(GTK_BOX(hbox), no_service_label, TRUE, TRUE, 0);
442
443 gtk_widget_show_all(contents_);
444 }
445
446 void WebIntentPickerGtk::InvalidateDelegate() {
447 delegate_ = NULL;
448 }
449
450 GtkWidget* WebIntentPickerGtk::GetWidgetRoot() {
451 return contents_;
452 }
453
454 GtkWidget* WebIntentPickerGtk::GetFocusWidget() {
455 return contents_;
456 }
457
458 void WebIntentPickerGtk::DeleteDelegate() {
459 // The delegate is deleted when the contents widget is destroyed. See
460 // OnDestroy.
461 if (delegate_)
462 delegate_->OnClosing();
463 }
464
465 bool WebIntentPickerGtk::GetBackgroundColor(GdkColor* color) {
466 if (inline_disposition_web_contents_.get()) {
467 *color = ui::kGdkWhite;
468 return true;
469 }
470
471 return false;
472 }
473
474 bool WebIntentPickerGtk::ShouldHaveBorderPadding() const {
475 return false;
476 }
477
478 void WebIntentPickerGtk::Observe(int type,
479 const content::NotificationSource& source,
480 const content::NotificationDetails& details) {
481 DCHECK_EQ(type, chrome::NOTIFICATION_BROWSER_THEME_CHANGED);
482 if (header_label_) {
483 GtkThemeService* theme_service = GetThemeService(web_contents_);
484 if (theme_service->UsingNativeTheme())
485 gtk_util::UndoForceFontSize(header_label_);
486 else
487 gtk_util::ForceFontSizePixels(header_label_, kHeaderLabelPixelSize);
488 }
489 UpdateInstalledServices();
490 UpdateSuggestedExtensions();
491 }
492
493 void WebIntentPickerGtk::OnDestroy(GtkWidget* button) {
494 // Destroy this object when the contents widget is destroyed. It can't be
495 // "delete this" because this function happens in a callback.
496 MessageLoop::current()->DeleteSoon(FROM_HERE, this);
497 model_->set_observer(NULL);
498 window_ = NULL;
499 }
500
501 void WebIntentPickerGtk::OnCloseButtonClick(GtkWidget* button) {
502 DCHECK(delegate_);
503 delegate_->OnUserCancelledPickerDialog();
504 }
505
506 void WebIntentPickerGtk::OnExtensionLinkClick(GtkWidget* link) {
507 DCHECK(delegate_);
508 size_t index = GetExtensionWidgetRow(link);
509 const WebIntentPickerModel::SuggestedExtension& extension =
510 model_->GetSuggestedExtensionAt(index);
511 delegate_->OnExtensionLinkClicked(extension.id,
512 event_utils::DispositionForCurrentButtonPressEvent());
513 }
514
515 void WebIntentPickerGtk::OnExtensionInstallButtonClick(GtkWidget* button) {
516 DCHECK(delegate_);
517 size_t index = GetExtensionWidgetRow(button);
518 const WebIntentPickerModel::SuggestedExtension& extension =
519 model_->GetSuggestedExtensionAt(index);
520
521 delegate_->OnExtensionInstallRequested(extension.id);
522 SetWidgetsEnabled(false);
523
524 // Re-enable the clicked extension row.
525 GList* vbox_list =
526 gtk_container_get_children(GTK_CONTAINER(extensions_vbox_));
527 GtkWidget* hbox = static_cast<GtkWidget*>(g_list_nth_data(vbox_list, index));
528 gtk_widget_set_sensitive(hbox, TRUE);
529
530 // Hide the install button.
531 GList* hbox_list = gtk_container_get_children(GTK_CONTAINER(hbox));
532 GtkWidget* install_button =
533 static_cast<GtkWidget*>(g_list_nth_data(hbox_list, kInstallButtonIndex));
534 GtkAllocation allocation;
535 gtk_widget_get_allocation(install_button, &allocation);
536 gtk_widget_hide(install_button);
537 g_list_free(hbox_list);
538 g_list_free(vbox_list);
539
540 // Show the throbber with the same size as the install button.
541 GtkWidget* throbber = AddThrobberToExtensionAt(index);
542 gtk_widget_set_size_request(throbber, allocation.width, allocation.height);
543 gtk_widget_show_all(throbber);
544 }
545
546 void WebIntentPickerGtk::OnMoreSuggestionsLinkClick(GtkWidget* link) {
547 DCHECK(delegate_);
548 delegate_->OnSuggestionsLinkClicked(
549 event_utils::DispositionForCurrentButtonPressEvent());
550 }
551
552 void WebIntentPickerGtk::OnChooseAnotherServiceClick(GtkWidget* link) {
553 DCHECK(delegate_);
554 delegate_->OnChooseAnotherService();
555 ResetContents();
556 }
557
558 void WebIntentPickerGtk::OnHostContentsSizeAllocate(GtkWidget* widget,
559 GdkRectangle* rectangle) {
560 if (!inline_disposition_delegate_.get())
561 return;
562 inline_disposition_delegate_->SetRenderViewSizeLimits();
563 }
564
565 void WebIntentPickerGtk::OnServiceButtonClick(GtkWidget* button) {
566 DCHECK(delegate_);
567 GList* button_list = gtk_container_get_children(GTK_CONTAINER(button_vbox_));
568 gint index = g_list_index(button_list, button);
569 DCHECK(index != -1);
570 g_list_free(button_list);
571
572 const WebIntentPickerModel::InstalledService& installed_service =
573 model_->GetInstalledServiceAt(index);
574
575 delegate_->OnServiceChosen(installed_service.url,
576 installed_service.disposition,
577 WebIntentPickerDelegate::kEnableDefaults);
578 }
579
580 void WebIntentPickerGtk::InitContents() {
581 GtkThemeService* theme_service = GetThemeService(web_contents_);
582
583 // Main contents vbox.
584 if (!contents_) {
585 contents_ = gtk_vbox_new(FALSE, 0);
586 g_signal_connect(contents_, "destroy", G_CALLBACK(&OnDestroyThunk), this);
587 }
588
589 gtk_widget_set_size_request(contents_, kWindowMinWidth, -1);
590
591 if (model_ && model_->IsWaitingForSuggestions()) {
592 ClearContents();
593 AddCloseButton(contents_);
594 waiting_dialog_.reset(new WaitingDialog(theme_service));
595 gtk_box_pack_start(GTK_BOX(contents_), waiting_dialog_->widget(),
596 TRUE, TRUE, 0);
597 } else {
598 InitMainContents();
599 }
600 }
601
602 void WebIntentPickerGtk::InitMainContents() {
603 GtkThemeService* theme_service = GetThemeService(web_contents_);
604
605 ClearContents();
606
607 AddCloseButton(contents_);
608 GtkWidget* sub_contents = CreateSubContents(contents_);
609
610 AddTitle(sub_contents);
611
612 // Add separation between the installed services list and the app suggestions.
613 GtkWidget* button_alignment = gtk_alignment_new(0.5, 0, 0, 0);
614 gtk_alignment_set_padding(GTK_ALIGNMENT(button_alignment), 0,
615 kMainContentPixelSize * 2, 0, 0);
616
617 // Vbox containing all service buttons.
618 button_vbox_ = gtk_vbox_new(FALSE, ui::kControlSpacing);
619 gtk_container_add(GTK_CONTAINER(button_alignment), button_vbox_);
620 gtk_box_pack_start(GTK_BOX(sub_contents), button_alignment, TRUE, TRUE, 0);
621
622 // Chrome Web Store label.
623 cws_label_ = theme_service->BuildLabel(
624 l10n_util::GetStringUTF8(IDS_INTENT_PICKER_GET_MORE_SERVICES).c_str(),
625 ui::kGdkBlack);
626 gtk_box_pack_start(GTK_BOX(sub_contents), cws_label_, TRUE, TRUE, 0);
627 gtk_misc_set_alignment(GTK_MISC(cws_label_), 0, 0);
628 gtk_widget_set_no_show_all(cws_label_, TRUE);
629
630 // Set the label width to the size of |sub_contents|, which we don't have
631 // access to yet, by calculating the main content width minus borders.
632 gtk_util::SetLabelWidth(cws_label_,
633 kWindowMinWidth - 2 * ui::kContentAreaBorder);
634 gtk_util::ForceFontSizePixels(cws_label_, kMainContentPixelSize);
635
636 // Suggested extensions vbox.
637 extensions_vbox_ = gtk_vbox_new(FALSE, ui::kControlSpacing);
638 GtkWidget* indent_extensions = gtk_alignment_new(0.0, 0.5, 1.0, 1.0);
639 gtk_alignment_set_padding(GTK_ALIGNMENT(indent_extensions), 0, 0,
640 ui::kGroupIndent, ui::kGroupIndent);
641 gtk_container_add(GTK_CONTAINER(indent_extensions), extensions_vbox_);
642 gtk_widget_set_no_show_all(indent_extensions, TRUE);
643 gtk_box_pack_start(GTK_BOX(sub_contents), indent_extensions, TRUE, TRUE, 0);
644
645 // CWS 'More Suggestions' link.
646 GtkWidget* link_alignment = gtk_alignment_new(0, 0.5f, 0, 0);
647 GtkWidget* more_hbox = gtk_hbox_new(FALSE, ui::kControlSpacing);
648 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
649 GdkPixbuf* cws_icon =
650 rb.GetNativeImageNamed(IDR_WEBSTORE_ICON_16).ToGdkPixbuf();
651 gtk_box_pack_start(GTK_BOX(more_hbox), gtk_image_new_from_pixbuf(cws_icon),
652 FALSE, FALSE, 0);
653 GtkWidget* more_suggestions_link = theme_service->BuildChromeLinkButton(
654 l10n_util::GetStringUTF8(IDS_FIND_MORE_INTENT_HANDLER_MESSAGE).c_str());
655 gtk_box_pack_start(GTK_BOX(more_hbox), more_suggestions_link,
656 FALSE, FALSE, 0);
657 gtk_container_add(GTK_CONTAINER(link_alignment), more_hbox);
658 gtk_chrome_link_button_set_use_gtk_theme(
659 GTK_CHROME_LINK_BUTTON(more_suggestions_link),
660 theme_service->UsingNativeTheme());
661 gtk_util::ForceFontSizePixels(
662 GTK_CHROME_LINK_BUTTON(more_suggestions_link)->label,
663 kMainContentPixelSize);
664 g_signal_connect(more_suggestions_link, "clicked",
665 G_CALLBACK(OnMoreSuggestionsLinkClickThunk), this);
666
667 GtkWidget* indent_link = gtk_util::IndentWidget(link_alignment);
668 gtk_box_pack_start(GTK_BOX(sub_contents), indent_link, TRUE, TRUE, 0);
669
670 // Throbber, which will be added to the hierarchy when necessary.
671 throbber_.reset(new ThrobberGtk(theme_service));
672
673 gtk_widget_show_all(contents_);
674 }
675
676 void WebIntentPickerGtk::ClearContents() {
677 // Wipe out all currently displayed widgets.
678 gtk_util::RemoveAllChildren(contents_);
679 header_label_ = NULL;
680 button_vbox_ = NULL;
681 cws_label_ = NULL;
682 extensions_vbox_ = NULL;
683 }
684
685 void WebIntentPickerGtk::ResetContents() {
686 ClearContents();
687
688 // Reset potential inline disposition data.
689 inline_disposition_web_contents_.reset();
690 inline_disposition_delegate_.reset();
691 window_->BackgroundColorChanged();
692
693 // Re-initialize picker widgets and data.
694 InitMainContents();
695 UpdateInstalledServices();
696 UpdateCWSLabel();
697 UpdateSuggestedExtensions();
698 SetActionString(header_label_text_);
699
700 gtk_widget_show_all(contents_);
701 }
702
703 GtkWidget* WebIntentPickerGtk::CreateSubContents(GtkWidget* box) {
704 GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
705 gtk_alignment_set_padding(
706 GTK_ALIGNMENT(alignment), 0, ui::kContentAreaBorder,
707 ui::kContentAreaBorder, ui::kContentAreaBorder);
708 gtk_box_pack_end(GTK_BOX(box), alignment, TRUE, TRUE, 0);
709
710 GtkWidget* sub_contents = gtk_vbox_new(FALSE, ui::kContentAreaSpacing);
711 gtk_container_add(GTK_CONTAINER(alignment), sub_contents);
712 return sub_contents;
713 }
714
715 void WebIntentPickerGtk::AddCloseButton(GtkWidget* containingBox) {
716
717 // Hbox containing the close button.
718 GtkWidget* close_hbox = gtk_hbox_new(FALSE, 0);
719 gtk_box_pack_start(GTK_BOX(containingBox), close_hbox, TRUE, TRUE, 0);
720
721 close_button_.reset(
722 CustomDrawButton::CloseButton(GetThemeService(web_contents_)));
723 g_signal_connect(close_button_->widget(), "clicked",
724 G_CALLBACK(OnCloseButtonClickThunk), this);
725 gtk_widget_set_can_focus(close_button_->widget(), FALSE);
726 gtk_box_pack_end(GTK_BOX(close_hbox), close_button_->widget(),
727 FALSE, FALSE, 0);
728 }
729
730 void WebIntentPickerGtk::AddTitle(GtkWidget* containingBox) {
731 // Hbox containing the header label.
732 GtkWidget* header_hbox = gtk_hbox_new(FALSE, 0);
733 gtk_box_pack_start(GTK_BOX(containingBox), header_hbox, TRUE, TRUE, 0);
734
735 // Label text will be set in the call to SetActionString().
736 header_label_ = GetThemeService(web_contents_)->BuildLabel(
737 std::string(), ui::kGdkBlack);
738 gtk_util::ForceFontSizePixels(header_label_, kHeaderLabelPixelSize);
739 gtk_box_pack_start(GTK_BOX(header_hbox), header_label_, TRUE, TRUE, 0);
740 gtk_misc_set_alignment(GTK_MISC(header_label_), 0, 0);
741 }
742
743 void WebIntentPickerGtk::UpdateInstalledServices() {
744 if (!button_vbox_)
745 return;
746
747 gtk_util::RemoveAllChildren(button_vbox_);
748
749 if (model_->GetInstalledServiceCount() == 0) {
750 gtk_widget_hide(gtk_widget_get_parent(button_vbox_));
751 return;
752 }
753
754 for (size_t i = 0; i < model_->GetInstalledServiceCount(); ++i) {
755 const WebIntentPickerModel::InstalledService& installed_service =
756 model_->GetInstalledServiceAt(i);
757
758 GtkWidget* button = gtk_button_new();
759 gtk_widget_set_tooltip_text(button, installed_service.url.spec().c_str());
760 gtk_button_set_label(GTK_BUTTON(button),
761 UTF16ToUTF8(installed_service.title).c_str());
762 gtk_button_set_alignment(GTK_BUTTON(button), 0, 0);
763
764 gtk_container_add(GTK_CONTAINER(button_vbox_), button);
765 g_signal_connect(button, "clicked", G_CALLBACK(OnServiceButtonClickThunk),
766 this);
767
768 SetServiceButtonImage(button, installed_service.favicon.ToGdkPixbuf());
769
770 // Must be called after SetServiceButtonImage as the internal label widget
771 // is replaced in that call.
772 SetWidgetFontSize(button, kMainContentPixelSize);
773 }
774
775 gtk_widget_show_all(button_vbox_);
776 gtk_widget_show(gtk_widget_get_parent(button_vbox_));
777 }
778
779 void WebIntentPickerGtk::UpdateCWSLabel() {
780 if (!button_vbox_)
781 return;
782
783 gtk_widget_set_visible(gtk_widget_get_parent(button_vbox_),
784 model_->GetInstalledServiceCount() != 0);
785
786 std::string label_text = UTF16ToUTF8(model_->GetSuggestionsLinkText());
787 gtk_label_set_text(GTK_LABEL(cws_label_), label_text.c_str());
788 gtk_widget_set_visible(cws_label_, !label_text.empty());
789 }
790
791 void WebIntentPickerGtk::UpdateSuggestedExtensions() {
792 if (!extensions_vbox_)
793 return;
794
795 GtkThemeService* theme_service = GetThemeService(web_contents_);
796
797 gtk_util::RemoveAllChildren(extensions_vbox_);
798
799 if (model_->GetSuggestedExtensionCount() == 0) {
800 gtk_widget_hide(gtk_widget_get_parent(extensions_vbox_));
801 return;
802 }
803
804 gtk_widget_show(gtk_widget_get_parent(extensions_vbox_));
805
806 for (size_t i = 0; i < model_->GetSuggestedExtensionCount(); ++i) {
807 const WebIntentPickerModel::SuggestedExtension& extension =
808 model_->GetSuggestedExtensionAt(i);
809
810 GtkWidget* hbox = gtk_hbox_new(FALSE, ui::kControlSpacing);
811 gtk_box_pack_start(GTK_BOX(extensions_vbox_), hbox, FALSE, FALSE, 0);
812
813 // Icon.
814 GtkWidget* icon = gtk_image_new_from_pixbuf(extension.icon.ToGdkPixbuf());
815 gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0);
816
817 // Title link.
818 string16 elided_title = ui::ElideText(extension.title, gfx::Font(),
819 kTitleLinkMaxWidth, ui::ELIDE_AT_END);
820 GtkWidget* title_link = theme_service->BuildChromeLinkButton(
821 UTF16ToUTF8(elided_title).c_str());
822 gtk_chrome_link_button_set_use_gtk_theme(GTK_CHROME_LINK_BUTTON(title_link),
823 theme_service->UsingNativeTheme());
824 gtk_util::ForceFontSizePixels(GTK_CHROME_LINK_BUTTON(title_link)->label,
825 kMainContentPixelSize);
826 g_signal_connect(title_link, "clicked",
827 G_CALLBACK(OnExtensionLinkClickThunk), this);
828 gtk_box_pack_start(GTK_BOX(hbox), title_link, FALSE, FALSE, 0);
829
830 // Stars.
831 GtkWidget* stars = CreateStarsWidget(extension.average_rating);
832 gtk_box_pack_start(GTK_BOX(hbox), stars, FALSE, FALSE, 0);
833
834 // Install button.
835 GtkWidget* install_button = gtk_button_new();
836 gtk_button_set_label(
837 GTK_BUTTON(install_button),
838 l10n_util::GetStringUTF8(IDS_INTENT_PICKER_INSTALL_EXTENSION).c_str());
839 GtkWidget* label = gtk_bin_get_child(GTK_BIN(install_button));
840 gtk_util::ForceFontSizePixels(label, kMainContentPixelSize);
841 g_signal_connect(install_button, "clicked",
842 G_CALLBACK(OnExtensionInstallButtonClickThunk), this);
843 gtk_box_pack_end(GTK_BOX(hbox), install_button, FALSE, FALSE, 0);
844 }
845
846 gtk_widget_show_all(extensions_vbox_);
847 gtk_widget_show(gtk_widget_get_parent(extensions_vbox_));
848 }
849
850 void WebIntentPickerGtk::SetWidgetsEnabled(bool enabled) {
851 gboolean data = enabled;
852 if (button_vbox_)
853 gtk_container_foreach(GTK_CONTAINER(button_vbox_), EnableWidgetCallback,
854 &data);
855 if (extensions_vbox_)
856 gtk_container_foreach(GTK_CONTAINER(extensions_vbox_), EnableWidgetCallback,
857 &data);
858 }
859
860 GtkWidget* WebIntentPickerGtk::AddThrobberToExtensionAt(size_t index) {
861 // The throbber should be unparented.
862 DCHECK(!gtk_widget_get_parent(throbber_->widget()));
863 GList* vbox_list =
864 gtk_container_get_children(GTK_CONTAINER(extensions_vbox_));
865 GtkWidget* hbox = static_cast<GtkWidget*>(g_list_nth_data(vbox_list, index));
866 GtkWidget* alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
867 gtk_container_add(GTK_CONTAINER(alignment), throbber_->widget());
868 gtk_box_pack_end(GTK_BOX(hbox), alignment, FALSE, FALSE, 0);
869 g_list_free(vbox_list);
870 throbber_->Start();
871 return alignment;
872 }
873
874 void WebIntentPickerGtk::RemoveThrobber() {
875 GtkWidget* alignment = gtk_widget_get_parent(throbber_->widget());
876 DCHECK(alignment);
877 gtk_container_remove(GTK_CONTAINER(alignment), throbber_->widget());
878 gtk_widget_destroy(alignment);
879 throbber_->Stop();
880 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698