speech_input_bubble_gtk.cc revision 201ade2fbba22bfb27ae029f4d23fca6ded109a0
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 = gtk_util::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    if (display_mode() == DISPLAY_MODE_RECORDING) {
183      gtk_label_set_text(GTK_LABEL(label_),
184          l10n_util::GetStringUTF8(IDS_SPEECH_INPUT_BUBBLE_HEADING).c_str());
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    } else {
191      gtk_label_set_text(GTK_LABEL(label_),
192          l10n_util::GetStringUTF8(IDS_SPEECH_INPUT_BUBBLE_WORKING).c_str());
193    }
194    gtk_widget_show(icon_);
195    gtk_widget_hide(try_again_button_);
196  }
197}
198
199void SpeechInputBubbleGtk::SetImage(const SkBitmap& image) {
200  if (image.isNull())
201    return;
202
203  GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&image);
204  gtk_image_set_from_pixbuf(GTK_IMAGE(icon_), pixbuf);
205  g_object_unref(pixbuf);
206}
207
208}  // namespace
209
210SpeechInputBubble* SpeechInputBubble::CreateNativeBubble(
211    TabContents* tab_contents,
212    Delegate* delegate,
213    const gfx::Rect& element_rect) {
214  return new SpeechInputBubbleGtk(tab_contents, delegate, element_rect);
215}
216
217