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