speech_input_bubble_gtk.cc revision bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293
1// Copyright (c) 2010 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/speech/speech_input_bubble.h" 6 7#include "app/l10n_util.h" 8#include "app/resource_bundle.h" 9#include "base/utf_string_conversions.h" 10#include "chrome/browser/gtk/gtk_theme_provider.h" 11#include "chrome/browser/gtk/gtk_util.h" 12#include "chrome/browser/gtk/info_bubble_gtk.h" 13#include "chrome/browser/gtk/owned_widget_gtk.h" 14#include "chrome/browser/tab_contents/tab_contents.h" 15#include "gfx/gtk_util.h" 16#include "gfx/rect.h" 17#include "grit/generated_resources.h" 18#include "grit/theme_resources.h" 19 20namespace { 21 22const int kBubbleControlVerticalSpacing = 10; 23const int kBubbleControlHorizontalSpacing = 20; 24const int kIconHorizontalPadding = 30; 25const int kButtonBarHorizontalSpacing = 10; 26 27// Use black for text labels since the bubble has white background. 28const GdkColor kLabelTextColor = gfx::kGdkBlack; 29 30// Implementation of SpeechInputBubble for GTK. This shows a speech input 31// info bubble on screen. 32class SpeechInputBubbleGtk 33 : public SpeechInputBubbleBase, 34 public InfoBubbleGtkDelegate { 35 public: 36 SpeechInputBubbleGtk(TabContents* tab_contents, 37 Delegate* delegate, 38 const gfx::Rect& element_rect); 39 ~SpeechInputBubbleGtk(); 40 41 private: 42 // InfoBubbleDelegate methods. 43 virtual void InfoBubbleClosing(InfoBubbleGtk* info_bubble, 44 bool closed_by_escape); 45 46 // SpeechInputBubble methods. 47 virtual void Show(); 48 virtual void Hide(); 49 virtual void UpdateLayout(); 50 virtual void SetImage(const SkBitmap& image); 51 52 CHROMEGTK_CALLBACK_0(SpeechInputBubbleGtk, void, OnCancelClicked); 53 CHROMEGTK_CALLBACK_0(SpeechInputBubbleGtk, void, OnTryAgainClicked); 54 55 Delegate* delegate_; 56 InfoBubbleGtk* info_bubble_; 57 TabContents* tab_contents_; 58 gfx::Rect element_rect_; 59 bool did_invoke_close_; 60 61 GtkWidget* label_; 62 GtkWidget* try_again_button_; 63 GtkWidget* icon_; 64 65 DISALLOW_COPY_AND_ASSIGN(SpeechInputBubbleGtk); 66}; 67 68SpeechInputBubbleGtk::SpeechInputBubbleGtk(TabContents* tab_contents, 69 Delegate* delegate, 70 const gfx::Rect& element_rect) 71 : delegate_(delegate), 72 info_bubble_(NULL), 73 tab_contents_(tab_contents), 74 element_rect_(element_rect), 75 did_invoke_close_(false), 76 label_(NULL), 77 try_again_button_(NULL), 78 icon_(NULL) { 79} 80 81SpeechInputBubbleGtk::~SpeechInputBubbleGtk() { 82 // The |Close| call below invokes our |InfoBubbleClosing| method. Since we 83 // were destroyed by the caller we don't need to call them back, hence set 84 // this flag here. 85 did_invoke_close_ = true; 86 Hide(); 87} 88 89void SpeechInputBubbleGtk::InfoBubbleClosing(InfoBubbleGtk* info_bubble, 90 bool closed_by_escape) { 91 info_bubble_ = NULL; 92 if (!did_invoke_close_) 93 delegate_->InfoBubbleFocusChanged(); 94} 95 96void SpeechInputBubbleGtk::OnCancelClicked(GtkWidget* widget) { 97 delegate_->InfoBubbleButtonClicked(BUTTON_CANCEL); 98} 99 100void SpeechInputBubbleGtk::OnTryAgainClicked(GtkWidget* widget) { 101 delegate_->InfoBubbleButtonClicked(BUTTON_TRY_AGAIN); 102} 103 104void SpeechInputBubbleGtk::Show() { 105 if (info_bubble_) 106 return; // Nothing further to do since the bubble is already visible. 107 108 // We use a vbox to arrange the controls (label, image, button bar) vertically 109 // and the button bar is a hbox holding the 2 buttons (try again and cancel). 110 // To get horizontal space around them we place this vbox with padding in a 111 // GtkAlignment below. 112 GtkWidget* vbox = gtk_vbox_new(FALSE, kBubbleControlVerticalSpacing); 113 114 label_ = gtk_label_new(NULL); 115 gtk_util::SetLabelColor(label_, &kLabelTextColor); 116 gtk_box_pack_start(GTK_BOX(vbox), label_, FALSE, FALSE, 0); 117 118 // The icon with a some padding on the left and right. 119 GtkWidget* icon_container = gtk_alignment_new(0, 0, 0, 0); 120 gtk_alignment_set_padding(GTK_ALIGNMENT(icon_container), 0, 0, 121 kIconHorizontalPadding, kIconHorizontalPadding); 122 icon_ = gtk_image_new(); 123 gtk_container_add(GTK_CONTAINER(icon_container), icon_); 124 gtk_box_pack_start(GTK_BOX(vbox), icon_container, FALSE, FALSE, 0); 125 126 GtkWidget* button_bar = gtk_hbox_new(FALSE, kButtonBarHorizontalSpacing); 127 gtk_box_pack_start(GTK_BOX(vbox), button_bar, FALSE, FALSE, 0); 128 129 GtkWidget* cancel_button = gtk_button_new_with_label( 130 l10n_util::GetStringUTF8(IDS_CANCEL).c_str()); 131 gtk_box_pack_start(GTK_BOX(button_bar), cancel_button, TRUE, FALSE, 0); 132 g_signal_connect(cancel_button, "clicked", 133 G_CALLBACK(&OnCancelClickedThunk), this); 134 135 try_again_button_ = gtk_button_new_with_label( 136 l10n_util::GetStringUTF8(IDS_SPEECH_INPUT_TRY_AGAIN).c_str()); 137 gtk_box_pack_start(GTK_BOX(button_bar), try_again_button_, TRUE, FALSE, 0); 138 g_signal_connect(try_again_button_, "clicked", 139 G_CALLBACK(&OnTryAgainClickedThunk), this); 140 141 GtkWidget* content = gtk_alignment_new(0, 0, 0, 0); 142 gtk_alignment_set_padding(GTK_ALIGNMENT(content), 143 kBubbleControlVerticalSpacing, kBubbleControlVerticalSpacing, 144 kBubbleControlHorizontalSpacing, kBubbleControlHorizontalSpacing); 145 gtk_container_add(GTK_CONTAINER(content), vbox); 146 147 GtkThemeProvider* theme_provider = GtkThemeProvider::GetFrom( 148 tab_contents_->profile()); 149 gfx::Rect rect(element_rect_.x() + kBubbleTargetOffsetX, 150 element_rect_.y() + element_rect_.height(), 1, 1); 151 info_bubble_ = InfoBubbleGtk::Show(tab_contents_->GetNativeView(), 152 &rect, 153 content, 154 InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT, 155 false, // match_system_theme 156 true, // grab_input 157 theme_provider, 158 this); 159 160 UpdateLayout(); 161} 162 163void SpeechInputBubbleGtk::Hide() { 164 if (info_bubble_) 165 info_bubble_->Close(); 166} 167 168void SpeechInputBubbleGtk::UpdateLayout() { 169 if (!info_bubble_) 170 return; 171 172 if (display_mode() == DISPLAY_MODE_MESSAGE) { 173 // Message text and the Try Again + Cancel buttons are visible, hide the 174 // icon. 175 gtk_label_set_text(GTK_LABEL(label_), 176 UTF16ToUTF8(message_text()).c_str()); 177 gtk_widget_show(try_again_button_); 178 gtk_widget_hide(icon_); 179 } else { 180 // Heading text, icon and cancel button are visible, hide the Try Again 181 // button. 182 gtk_label_set_text(GTK_LABEL(label_), 183 l10n_util::GetStringUTF8(IDS_SPEECH_INPUT_BUBBLE_HEADING).c_str()); 184 if (display_mode() == DISPLAY_MODE_RECORDING) { 185 SkBitmap* image = ResourceBundle::GetSharedInstance().GetBitmapNamed( 186 IDR_SPEECH_INPUT_MIC_EMPTY); 187 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(image); 188 gtk_image_set_from_pixbuf(GTK_IMAGE(icon_), pixbuf); 189 g_object_unref(pixbuf); 190 } 191 gtk_widget_show(icon_); 192 gtk_widget_hide(try_again_button_); 193 } 194} 195 196void SpeechInputBubbleGtk::SetImage(const SkBitmap& image) { 197 if (image.isNull()) 198 return; 199 200 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&image); 201 gtk_image_set_from_pixbuf(GTK_IMAGE(icon_), pixbuf); 202 g_object_unref(pixbuf); 203} 204 205} // namespace 206 207SpeechInputBubble* SpeechInputBubble::CreateNativeBubble( 208 TabContents* tab_contents, 209 Delegate* delegate, 210 const gfx::Rect& element_rect) { 211 return new SpeechInputBubbleGtk(tab_contents, delegate, element_rect); 212} 213 214