15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/speech/speech_recognition_bubble.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/browser_toolbar_gtk.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/browser_window_gtk.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/bubble/bubble_gtk.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/gtk_theme_service.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/gtk_util.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/resource_context.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/speech_recognition_manager.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/web_contents_view.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/theme_resources.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/gtk/gtk_hig_constants.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/gtk/owned_widget_gtk.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/gtk_util.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/rect.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::WebContents;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kBubbleControlVerticalSpacing = 5;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kBubbleControlHorizontalSpacing = 20;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kIconHorizontalPadding = 10;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kButtonBarHorizontalSpacing = 10;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use black for text labels since the bubble has white background.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const GdkColor& kLabelTextColor = ui::kGdkBlack;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Implementation of SpeechRecognitionBubble for GTK. This shows a speech
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// recognition bubble on screen.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class SpeechRecognitionBubbleGtk : public SpeechRecognitionBubbleBase,
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   public BubbleDelegateGtk {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
48f5859ba46034e02ada5ef522d9d9c09fbbddccd8Ben Murdoch  SpeechRecognitionBubbleGtk(int render_process_id, int render_view_id,
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             Delegate* delegate,
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const gfx::Rect& element_rect);
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~SpeechRecognitionBubbleGtk();
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // SpeechRecognitionBubbleBase:
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void Show() OVERRIDE;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void Hide() OVERRIDE;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void UpdateLayout() OVERRIDE;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void UpdateImage() OVERRIDE;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // BubbleDelegateGtk:
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void BubbleClosing(BubbleGtk* bubble, bool closed_by_escape) OVERRIDE;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHROMEGTK_CALLBACK_0(SpeechRecognitionBubbleGtk, void, OnCancelClicked);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHROMEGTK_CALLBACK_0(SpeechRecognitionBubbleGtk, void, OnTryAgainClicked);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHROMEGTK_CALLBACK_0(SpeechRecognitionBubbleGtk, void, OnMicSettingsClicked);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Delegate* delegate_;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BubbleGtk* bubble_;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect element_rect_;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool did_invoke_close_;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* label_;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* cancel_button_;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* try_again_button_;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* icon_;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* icon_container_;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* mic_settings_;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionBubbleGtk);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpeechRecognitionBubbleGtk::SpeechRecognitionBubbleGtk(
83f5859ba46034e02ada5ef522d9d9c09fbbddccd8Ben Murdoch    int render_process_id, int render_view_id, Delegate* delegate,
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& element_rect)
85f5859ba46034e02ada5ef522d9d9c09fbbddccd8Ben Murdoch    : SpeechRecognitionBubbleBase(render_process_id, render_process_id),
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delegate_(delegate),
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bubble_(NULL),
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      element_rect_(element_rect),
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      did_invoke_close_(false),
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      label_(NULL),
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cancel_button_(NULL),
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try_again_button_(NULL),
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_(NULL),
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_container_(NULL),
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mic_settings_(NULL) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpeechRecognitionBubbleGtk::~SpeechRecognitionBubbleGtk() {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The |Close| call below invokes our |BubbleClosing| method. Since we were
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // destroyed by the caller we don't need to call them back, hence set this
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // flag here.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  did_invoke_close_ = true;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Hide();
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpeechRecognitionBubbleGtk::OnCancelClicked(GtkWidget* widget) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delegate_->InfoBubbleButtonClicked(BUTTON_CANCEL);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpeechRecognitionBubbleGtk::OnTryAgainClicked(GtkWidget* widget) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delegate_->InfoBubbleButtonClicked(BUTTON_TRY_AGAIN);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpeechRecognitionBubbleGtk::OnMicSettingsClicked(GtkWidget* widget) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::SpeechRecognitionManager::GetInstance()->ShowAudioInputSettings();
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Hide();
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpeechRecognitionBubbleGtk::Show() {
120f5859ba46034e02ada5ef522d9d9c09fbbddccd8Ben Murdoch  if (bubble_ || !GetWebContents())
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Nothing further to do since the bubble is already visible.
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We use a vbox to arrange the controls (label, image, button bar) vertically
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and the button bar is a hbox holding the 2 buttons (try again and cancel).
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // To get horizontal space around them we place this vbox with padding in a
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // GtkAlignment below.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The icon with a some padding on the left and right.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_container_ = gtk_alignment_new(0, 0, 0, 0);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_ = gtk_image_new();
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_container_add(GTK_CONTAINER(icon_container_), icon_);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_box_pack_start(GTK_BOX(vbox), icon_container_, FALSE, FALSE,
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     kBubbleControlVerticalSpacing);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  label_ = gtk_label_new(NULL);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_util::SetLabelColor(label_, &kLabelTextColor);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_box_pack_start(GTK_BOX(vbox), label_, FALSE, FALSE,
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     kBubbleControlVerticalSpacing);
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = Profile::FromBrowserContext(
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetWebContents()->GetBrowserContext());
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(tommi): The audio_manager property can only be accessed from the
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // IO thread, so we can't call CanShowAudioInputSettings directly here if
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we can show the input settings.  For now, we always show the link (like
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we do on other platforms).
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (true) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mic_settings_ = gtk_chrome_link_button_new(
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        l10n_util::GetStringUTF8(IDS_SPEECH_INPUT_MIC_SETTINGS).c_str());
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_box_pack_start(GTK_BOX(vbox), mic_settings_, FALSE, FALSE,
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       kBubbleControlVerticalSpacing);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_signal_connect(mic_settings_, "clicked",
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     G_CALLBACK(&OnMicSettingsClickedThunk), this);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* button_bar = gtk_hbox_new(FALSE, kButtonBarHorizontalSpacing);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_box_pack_start(GTK_BOX(vbox), button_bar, FALSE, FALSE,
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     kBubbleControlVerticalSpacing);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cancel_button_ = gtk_button_new_with_label(
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      l10n_util::GetStringUTF8(IDS_CANCEL).c_str());
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_box_pack_start(GTK_BOX(button_bar), cancel_button_, TRUE, FALSE, 0);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_signal_connect(cancel_button_, "clicked",
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(&OnCancelClickedThunk), this);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try_again_button_ = gtk_button_new_with_label(
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      l10n_util::GetStringUTF8(IDS_SPEECH_INPUT_TRY_AGAIN).c_str());
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_box_pack_start(GTK_BOX(button_bar), try_again_button_, TRUE, FALSE, 0);
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_signal_connect(try_again_button_, "clicked",
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(&OnTryAgainClickedThunk), this);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* content = gtk_alignment_new(0, 0, 0, 0);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_alignment_set_padding(GTK_ALIGNMENT(content),
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kBubbleControlVerticalSpacing, kBubbleControlVerticalSpacing,
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kBubbleControlHorizontalSpacing, kBubbleControlHorizontalSpacing);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_container_add(GTK_CONTAINER(content), vbox);
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkThemeService* theme_provider = GtkThemeService::GetFrom(profile);
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GtkWidget* reference_widget = GetWebContents()->GetView()->GetNativeView();
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect container_rect;
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GetWebContents()->GetView()->GetContainerBounds(&container_rect);
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect target_rect(element_rect_.right() - kBubbleTargetOffsetX,
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      element_rect_.bottom(), 1, 1);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (target_rect.x() < 0 || target_rect.y() < 0 ||
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      target_rect.x() > container_rect.width() ||
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      target_rect.y() > container_rect.height()) {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Target is not in screen view, so point to wrench.
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(GetWebContents());
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserWindowGtk* browser_window =
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        BrowserWindowGtk::GetBrowserWindowForNativeWindow(
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            browser->window()->GetNativeWindow());
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    reference_widget = browser_window->GetToolbar()->GetLocationBarView()
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ->location_icon_widget();
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    target_rect = gtk_util::WidgetBounds(reference_widget);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bubble_ = BubbleGtk::Show(reference_widget,
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &target_rect,
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            content,
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            BubbleGtk::ANCHOR_TOP_LEFT,
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            BubbleGtk::POPUP_WINDOW | BubbleGtk::GRAB_INPUT,
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            theme_provider,
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            this);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateLayout();
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpeechRecognitionBubbleGtk::Hide() {
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (bubble_)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bubble_->Close();
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpeechRecognitionBubbleGtk::UpdateLayout() {
215f5859ba46034e02ada5ef522d9d9c09fbbddccd8Ben Murdoch  if (!bubble_ || !GetWebContents())
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (display_mode() == DISPLAY_MODE_MESSAGE) {
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Message text and the Try Again + Cancel buttons are visible, hide the
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // icon.
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_label_set_text(GTK_LABEL(label_),
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       UTF16ToUTF8(message_text()).c_str());
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_widget_show(label_);
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_widget_show(try_again_button_);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (mic_settings_)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_widget_show(mic_settings_);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_widget_hide(icon_);
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Heading text, icon and cancel button are visible, hide the Try Again
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // button.
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_label_set_text(GTK_LABEL(label_),
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        l10n_util::GetStringUTF8(IDS_SPEECH_INPUT_BUBBLE_HEADING).c_str());
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (display_mode() == DISPLAY_MODE_RECORDING) {
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_widget_show(label_);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_widget_hide(label_);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateImage();
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_widget_show(icon_);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_widget_hide(try_again_button_);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (mic_settings_)
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_widget_hide(mic_settings_);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (display_mode() == DISPLAY_MODE_WARM_UP) {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_widget_hide(cancel_button_);
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The text label and cancel button are hidden in this mode, but we want
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the popup to appear the same size as it would once recording starts,
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // so as to reduce UI jank when recording starts. So we calculate the
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // difference in size between the two sets of controls and add that as
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // padding around the icon here.
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GtkRequisition cancel_size;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_widget_get_child_requisition(cancel_button_, &cancel_size);
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GtkRequisition label_size;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_widget_get_child_requisition(label_, &label_size);
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::ImageSkia* volume = ResourceBundle::GetSharedInstance().
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetImageSkiaNamed(IDR_SPEECH_INPUT_MIC_EMPTY);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int desired_width = std::max(volume->width(), cancel_size.width) +
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          kIconHorizontalPadding * 2;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int desired_height = volume->height() + label_size.height +
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           cancel_size.height +
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           kBubbleControlVerticalSpacing * 2;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int diff_width = desired_width - icon_image().width();
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int diff_height = desired_height - icon_image().height();
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_alignment_set_padding(GTK_ALIGNMENT(icon_container_),
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                diff_height / 2, diff_height - diff_height / 2,
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                diff_width / 2, diff_width - diff_width / 2);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Reset the padding done above.
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_alignment_set_padding(GTK_ALIGNMENT(icon_container_), 0, 0,
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                kIconHorizontalPadding, kIconHorizontalPadding);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_widget_show(cancel_button_);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpeechRecognitionBubbleGtk::UpdateImage() {
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::ImageSkia image = icon_image();
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (image.isNull() || !bubble_)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(*image.bitmap());
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_image_set_from_pixbuf(GTK_IMAGE(icon_), pixbuf);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_object_unref(pixbuf);
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpeechRecognitionBubbleGtk::BubbleClosing(BubbleGtk* bubble,
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                               bool closed_by_escape) {
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bubble_ = NULL;
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!did_invoke_close_)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delegate_->InfoBubbleFocusChanged();
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpeechRecognitionBubble* SpeechRecognitionBubble::CreateNativeBubble(
296f5859ba46034e02ada5ef522d9d9c09fbbddccd8Ben Murdoch    int render_process_id, int render_view_id,
297f5859ba46034e02ada5ef522d9d9c09fbbddccd8Ben Murdoch    SpeechRecognitionBubble::Delegate* delegate,
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& element_rect) {
299f5859ba46034e02ada5ef522d9d9c09fbbddccd8Ben Murdoch  return new SpeechRecognitionBubbleGtk(render_process_id, render_view_id,
300f5859ba46034e02ada5ef522d9d9c09fbbddccd8Ben Murdoch      delegate, element_rect);
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
302