1 // Copyright (c) 2013 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/validation_message_bubble.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "chrome/browser/profiles/profile.h" 9 #include "chrome/browser/ui/gtk/bubble/bubble_gtk.h" 10 #include "chrome/browser/ui/gtk/gtk_theme_service.h" 11 #include "chrome/browser/ui/gtk/gtk_util.h" 12 #include "content/public/browser/render_view_host.h" 13 #include "content/public/browser/render_widget_host_view.h" 14 #include "content/public/browser/web_contents.h" 15 #include "grit/theme_resources.h" 16 #include "ui/base/resource/resource_bundle.h" 17 #include "ui/gfx/gtk_util.h" 18 #include "ui/gfx/image/image_skia.h" 19 #include "ui/gfx/rect.h" 20 21 namespace { 22 23 const int kPadding = 4; 24 const int kIconTextMargin = 8; 25 const int kTextMargin = 4; 26 const int kTextMaxWidth = 256; 27 28 // A GTK implementation of ValidationMessageBubble. 29 class ValidationMessageBubbleGtk : public chrome::ValidationMessageBubble, 30 public BubbleDelegateGtk { 31 public: 32 ValidationMessageBubbleGtk(content::RenderWidgetHost* widget_host, 33 const gfx::Rect& anchor_in_screen, 34 const string16& main_text, 35 const string16& sub_text); 36 virtual ~ValidationMessageBubbleGtk(); 37 virtual void SetPositionRelativeToAnchor( 38 content::RenderWidgetHost* widget_host, 39 const gfx::Rect& anchor_in_root_view) OVERRIDE; 40 41 // BubbleDelegateGtk override: 42 virtual void BubbleClosing(BubbleGtk*, bool) OVERRIDE; 43 44 private: 45 static GtkWidget* ConstructContent(const string16& main_text, 46 const string16& sub_text); 47 48 BubbleGtk* bubble_; 49 }; 50 51 ValidationMessageBubbleGtk::ValidationMessageBubbleGtk( 52 content::RenderWidgetHost* widget_host, 53 const gfx::Rect& anchor_in_root_view, 54 const string16& main_text, 55 const string16& sub_text) 56 : bubble_(NULL) { 57 if (!widget_host->IsRenderView()) 58 return; 59 Profile* profile = Profile::FromBrowserContext( 60 content::WebContents::FromRenderViewHost( 61 content::RenderViewHost::From(widget_host))->GetBrowserContext()); 62 bubble_ = BubbleGtk::Show(widget_host->GetView()->GetNativeView(), 63 &anchor_in_root_view, 64 ConstructContent(main_text, sub_text), 65 BubbleGtk::ANCHOR_TOP_LEFT, 66 BubbleGtk::POPUP_WINDOW, 67 GtkThemeService::GetFrom(profile), 68 this); 69 } 70 71 ValidationMessageBubbleGtk::~ValidationMessageBubbleGtk() { 72 if (bubble_) 73 bubble_->Close(); 74 } 75 76 void ValidationMessageBubbleGtk::SetPositionRelativeToAnchor( 77 content::RenderWidgetHost* widget_host, 78 const gfx::Rect& anchor_in_root_view) { 79 if (bubble_) 80 bubble_->SetPositionRelativeToAnchor(&anchor_in_root_view); 81 } 82 83 void ValidationMessageBubbleGtk::BubbleClosing(BubbleGtk*, bool) { 84 bubble_ = NULL; 85 } 86 87 // static 88 GtkWidget* ValidationMessageBubbleGtk::ConstructContent( 89 const string16& main_text, const string16& sub_text) { 90 GtkWidget* icon = gtk_image_new(); 91 gtk_misc_set_alignment(GTK_MISC(icon), 0.5, 0); 92 gtk_misc_set_padding(GTK_MISC(icon), kPadding, kPadding); 93 ResourceBundle& bundle = ResourceBundle::GetSharedInstance(); 94 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap( 95 *bundle.GetImageSkiaNamed(IDR_INPUT_ALERT)->bitmap()); 96 gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pixbuf); 97 g_object_unref(pixbuf); 98 GtkWidget* icon_text_box = gtk_hbox_new(FALSE, kIconTextMargin); 99 gtk_container_add(GTK_CONTAINER(icon_text_box), icon); 100 101 GtkWidget* text_box = gtk_vbox_new(FALSE, kTextMargin); 102 GtkWidget* label = gtk_label_new(UTF16ToUTF8(main_text).c_str()); 103 const gfx::Font& main_font = bundle.GetFont(ResourceBundle::MediumFont); 104 gtk_util::ForceFontSizePixels(label, main_font.GetHeight()); 105 gtk_box_pack_start( 106 GTK_BOX(text_box), gtk_util::LeftAlignMisc(label), TRUE, TRUE, 0); 107 108 if (!sub_text.empty()) { 109 GtkWidget* sub_label = gtk_label_new(UTF16ToUTF8(sub_text).c_str()); 110 const gfx::Font& sub_font = bundle.GetFont(ResourceBundle::BaseFont); 111 gtk_util::ForceFontSizePixels(sub_label, sub_font.GetHeight()); 112 int max_characters = kTextMaxWidth / sub_font.GetAverageCharacterWidth(); 113 if (sub_text.length() > static_cast<size_t>(max_characters)) 114 gtk_util::SetLabelWidth(sub_label, kTextMaxWidth); 115 gtk_box_pack_start( 116 GTK_BOX(text_box), gtk_util::LeftAlignMisc(sub_label), TRUE, TRUE, 0); 117 } 118 gtk_container_add(GTK_CONTAINER(icon_text_box), text_box); 119 120 GtkWidget* content = gtk_alignment_new(0, 0, 0, 0); 121 gtk_alignment_set_padding( 122 GTK_ALIGNMENT(content), kPadding, kPadding, kPadding, kPadding); 123 gtk_container_add(GTK_CONTAINER(content), icon_text_box); 124 return content; 125 } 126 127 } // namespace 128 129 namespace chrome { 130 131 scoped_ptr<ValidationMessageBubble> ValidationMessageBubble::CreateAndShow( 132 content::RenderWidgetHost* widget_host, 133 const gfx::Rect& anchor_in_root_view, 134 const string16& main_text, 135 const string16& sub_text) { 136 return scoped_ptr<ValidationMessageBubble>(new ValidationMessageBubbleGtk( 137 widget_host, anchor_in_root_view, main_text, sub_text)).Pass(); 138 } 139 140 } // namespace chrome 141