summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-22 00:44:41 +0000
committermsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-22 00:44:41 +0000
commite4ae8fc0a52d5553f3341b831bf200445047998c (patch)
tree4ce8ca5eca798cbb5a433f7a9bfd3d2b39e4af6f /chrome
parentd5f7f81c19d0481a61554f465ed7ec367054a005 (diff)
downloadchromium_src-e4ae8fc0a52d5553f3341b831bf200445047998c.zip
chromium_src-e4ae8fc0a52d5553f3341b831bf200445047998c.tar.gz
chromium_src-e4ae8fc0a52d5553f3341b831bf200445047998c.tar.bz2
Add Chrome To Mobile GTK page action and bubble.
Implements the Chrome To Mobile extension in Chrome. Consume ChromeToMobileService as done in Views: See http://codereview.chromium.org/9443007/ and follow-ups. Add a page action icon when the service reports 1+ devices. Show a bubble to send the current page URL / MHTML snapshot. BUG=102709 TEST=Chrome To Mobile GTK UI works as expected :) Review URL: http://codereview.chromium.org/9689087 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@128115 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/ui/gtk/browser_window_gtk.cc2
-rw-r--r--chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.cc302
-rw-r--r--chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.h114
-rw-r--r--chrome/browser/ui/gtk/location_bar_view_gtk.cc78
-rw-r--r--chrome/browser/ui/gtk/location_bar_view_gtk.h22
-rw-r--r--chrome/browser/ui/gtk/view_id_util.cc5
-rw-r--r--chrome/browser/ui/gtk/view_id_util_browsertest.cc3
-rw-r--r--chrome/chrome_browser.gypi2
8 files changed, 520 insertions, 8 deletions
diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc
index a456d24..66d1b818 100644
--- a/chrome/browser/ui/gtk/browser_window_gtk.cc
+++ b/chrome/browser/ui/gtk/browser_window_gtk.cc
@@ -1075,7 +1075,7 @@ void BrowserWindowGtk::ShowBookmarkBubble(const GURL& url,
}
void BrowserWindowGtk::ShowChromeToMobileBubble() {
- NOTIMPLEMENTED();
+ toolbar_->GetLocationBarView()->ShowChromeToMobileBubble();
}
bool BrowserWindowGtk::IsDownloadShelfVisible() const {
diff --git a/chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.cc b/chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.cc
new file mode 100644
index 0000000..2047468
--- /dev/null
+++ b/chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.cc
@@ -0,0 +1,302 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.h"
+
+#include <gtk/gtk.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/i18n/rtl.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chrome_to_mobile_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/gtk/gtk_util.h"
+#include "chrome/browser/ui/gtk/theme_service_gtk.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "content/public/browser/notification_source.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources_standard.h"
+#include "ui/base/animation/throb_animation.h"
+#include "ui/base/gtk/gtk_hig_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/text/bytes_formatting.h"
+#include "ui/gfx/image/image.h"
+
+namespace {
+
+// The currently open app-modal bubble singleton, or NULL if none is open.
+ChromeToMobileBubbleGtk* g_bubble = NULL;
+
+// Padding between the content and the edge of bubble; from BookmarkBubbleGtk.
+const int kContentBorder = 7;
+
+// Horizontal padding that preceeds mobile device radio buttons.
+const int kRadioPadding = 20;
+
+// The millisecond duration of the "Sending..." progress throb animation.
+const size_t kProgressThrobDurationMS = 2400;
+
+// The seconds to delay before automatically closing the bubble after sending.
+const int kAutoCloseDelay = 3;
+
+// The color of the error label.
+const GdkColor kErrorColor = GDK_COLOR_RGB(0xFF, 0x00, 0x00);
+
+} // namespace
+
+// static
+void ChromeToMobileBubbleGtk::Show(GtkImage* anchor_image, Profile* profile) {
+ // Do not construct a new bubble if one is already being shown.
+ if (!g_bubble)
+ g_bubble = new ChromeToMobileBubbleGtk(anchor_image, profile);
+}
+
+void ChromeToMobileBubbleGtk::BubbleClosing(BubbleGtk* bubble,
+ bool closed_by_escape) {
+ DCHECK_EQ(bubble, bubble_);
+
+ gtk_image_set_from_pixbuf(GTK_IMAGE(anchor_image_),
+ theme_service_->GetImageNamed(IDR_MOBILE)->ToGdkPixbuf());
+
+ labels_.clear();
+ anchor_image_ = NULL;
+ send_copy_ = NULL;
+ cancel_ = NULL;
+ send_ = NULL;
+ error_ = NULL;
+ bubble_ = NULL;
+ progress_animation_.reset();
+}
+
+void ChromeToMobileBubbleGtk::AnimationProgressed(
+ const ui::Animation* animation) {
+ DCHECK(animation == progress_animation_.get());
+ double animation_value = animation->GetCurrentValue();
+ int id = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_3;
+ // Show each of four messages for 1/4 of the animation.
+ if (animation_value < 0.25)
+ id = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_0;
+ else if (animation_value < 0.5)
+ id = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_1;
+ else if (animation_value < 0.75)
+ id = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_2;
+ gtk_button_set_label(GTK_BUTTON(send_), l10n_util::GetStringUTF8(id).c_str());
+}
+
+void ChromeToMobileBubbleGtk::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK(type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED);
+
+ // Update the tracked labels to match the new theme, as in BookmarkBubbleGtk.
+ const GdkColor* color =
+ theme_service_->UsingNativeTheme() ? NULL : &ui::kGdkBlack;
+ std::vector<GtkWidget*>::iterator it;
+ for (it = labels_.begin(); it != labels_.end(); ++it)
+ gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, color);
+}
+
+void ChromeToMobileBubbleGtk::SnapshotGenerated(const FilePath& path,
+ int64 bytes) {
+ if (bytes > 0) {
+ snapshot_path_ = path;
+ gtk_button_set_label(GTK_BUTTON(send_copy_), l10n_util::GetStringFUTF8(
+ IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY, ui::FormatBytes(bytes)).c_str());
+ gtk_widget_set_sensitive(send_copy_, TRUE);
+ } else {
+ gtk_button_set_label(GTK_BUTTON(send_copy_), l10n_util::GetStringUTF8(
+ IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_FAILED).c_str());
+ }
+}
+
+void ChromeToMobileBubbleGtk::OnSendComplete(bool success) {
+ progress_animation_->Stop();
+ gtk_button_set_alignment(GTK_BUTTON(send_), 0.5, 0.5);
+
+ if (success) {
+ gtk_button_set_label(GTK_BUTTON(send_),
+ l10n_util::GetStringUTF8(IDS_CHROME_TO_MOBILE_BUBBLE_SENT).c_str());
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ base::Bind(&ChromeToMobileBubbleGtk::OnCancelClicked,
+ weak_ptr_factory_.GetWeakPtr(), GTK_WIDGET(NULL)),
+ base::TimeDelta::FromSeconds(kAutoCloseDelay));
+ } else {
+ gtk_button_set_label(GTK_BUTTON(send_),
+ l10n_util::GetStringUTF8(IDS_CHROME_TO_MOBILE_BUBBLE_ERROR).c_str());
+ gtk_widget_set_visible(error_, TRUE);
+ }
+}
+
+ChromeToMobileBubbleGtk::ChromeToMobileBubbleGtk(GtkImage* anchor_image,
+ Profile* profile)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
+ profile_(profile),
+ theme_service_(ThemeServiceGtk::GetFrom(profile_)),
+ selected_mobile_(NULL),
+ anchor_image_(anchor_image),
+ send_copy_(NULL),
+ cancel_(NULL),
+ send_(NULL),
+ error_(NULL),
+ bubble_(NULL) {
+ ChromeToMobileService* service =
+ ChromeToMobileServiceFactory::GetForProfile(profile);
+
+ // Generate the MHTML snapshot now to report its size in the bubble.
+ service->GenerateSnapshot(weak_ptr_factory_.GetWeakPtr());
+
+ // Get the list of mobile devices.
+ std::vector<DictionaryValue*> mobiles = service->mobiles();
+ DCHECK_GT(mobiles.size(), 0U);
+ selected_mobile_ = mobiles[0];
+
+ GtkWidget* content = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(content), kContentBorder);
+
+ // Create and pack the title label; init the selected mobile device.
+ GtkWidget* title = NULL;
+ if (mobiles.size() == 1) {
+ string16 mobile_name;
+ mobiles[0]->GetString("name", &mobile_name);
+ title = gtk_label_new(l10n_util::GetStringFUTF8(
+ IDS_CHROME_TO_MOBILE_BUBBLE_SINGLE_TITLE, mobile_name).c_str());
+ } else {
+ title = gtk_label_new(l10n_util::GetStringUTF8(
+ IDS_CHROME_TO_MOBILE_BUBBLE_MULTI_TITLE).c_str());
+ }
+ gtk_misc_set_alignment(GTK_MISC(title), 0, 1);
+ gtk_box_pack_start(GTK_BOX(content), title, FALSE, FALSE, 0);
+ labels_.push_back(title);
+
+ // Create and pack the device radio group; init the selected mobile device.
+ if (mobiles.size() > 1) {
+ GtkWidget* radio = NULL;
+ for (std::vector<DictionaryValue*>::const_iterator it = mobiles.begin();
+ it != mobiles.end(); ++it) {
+ std::string name;
+ (*it)->GetStringASCII("name", &name);
+ radio = gtk_radio_button_new_with_label_from_widget(
+ GTK_RADIO_BUTTON(radio), name.c_str());
+
+ // Activate the default radio button before attaching its signal handler.
+ if (mobile_map_.empty()) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
+ DCHECK_EQ(selected_mobile_, *it);
+ }
+
+ mobile_map_[radio] = *it;
+ // Pack each radio item into a horizontal box padding.
+ GtkWidget* row = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(row), radio, FALSE, FALSE, kRadioPadding);
+ gtk_box_pack_start(GTK_BOX(content), row, FALSE, FALSE, 0);
+ g_signal_connect(radio, "toggled", G_CALLBACK(OnRadioToggledThunk), this);
+ }
+ }
+
+ // Create and pack the offline copy check box.
+ send_copy_ = gtk_check_button_new_with_label(
+ l10n_util::GetStringFUTF8(IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY,
+ l10n_util::GetStringUTF16(
+ IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_GENERATING)).c_str());
+ gtk_widget_set_sensitive(send_copy_, FALSE);
+ gtk_box_pack_start(GTK_BOX(content), send_copy_, FALSE, FALSE, 0);
+
+ // Set the send button requested size from its final (presumed longest) string
+ // to avoid resizes during animation. Use the same size for the cancel button.
+ send_ = gtk_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_3).c_str());
+ GtkRequisition button_size;
+ gtk_widget_size_request(send_, &button_size);
+ button_size.width *= 1.25;
+ gtk_widget_set_size_request(send_, button_size.width, button_size.height);
+ gtk_button_set_label(GTK_BUTTON(send_),
+ l10n_util::GetStringUTF8(IDS_CHROME_TO_MOBILE_BUBBLE_SEND).c_str());
+ cancel_ = gtk_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_CANCEL).c_str());
+ gtk_widget_set_size_request(cancel_, button_size.width, button_size.height);
+
+ // Pack the buttons with an expanding label for right-justification.
+ GtkWidget* buttons = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(buttons), gtk_label_new(""), TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(buttons), cancel_, FALSE, FALSE, 4);
+ gtk_box_pack_start(GTK_BOX(buttons), send_, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(content), buttons, FALSE, FALSE, 0);
+
+ // Pack the error label, but ensure it doesn't show initially with the bubble.
+ error_ = gtk_label_new(l10n_util::GetStringUTF8(
+ IDS_CHROME_TO_MOBILE_BUBBLE_ERROR_MESSAGE).c_str());
+ gtk_misc_set_alignment(GTK_MISC(error_), 0, 1);
+ gtk_util::SetLabelColor(error_, &kErrorColor);
+ gtk_box_pack_start(GTK_BOX(content), error_, FALSE, FALSE, 0);
+ gtk_widget_set_no_show_all(error_, TRUE);
+
+ // Initialize focus to the send button.
+ gtk_container_set_focus_child(GTK_CONTAINER(content), send_);
+
+ BubbleGtk::ArrowLocationGtk arrow_location = base::i18n::IsRTL() ?
+ BubbleGtk::ARROW_LOCATION_TOP_LEFT : BubbleGtk::ARROW_LOCATION_TOP_RIGHT;
+ bubble_ = BubbleGtk::Show(GTK_WIDGET(anchor_image_), NULL, content,
+ arrow_location, true /*match_system_theme*/,
+ true /*grab_input*/, theme_service_, this /*delegate*/);
+ if (!bubble_) {
+ NOTREACHED();
+ return;
+ }
+
+ g_signal_connect(content, "destroy", G_CALLBACK(&OnDestroyThunk), this);
+ g_signal_connect(cancel_, "clicked", G_CALLBACK(&OnCancelClickedThunk), this);
+ g_signal_connect(send_, "clicked", G_CALLBACK(&OnSendClickedThunk), this);
+
+ registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+ content::Source<ThemeService>(theme_service_));
+ theme_service_->InitThemesFor(this);
+
+ gtk_image_set_from_pixbuf(GTK_IMAGE(anchor_image_),
+ theme_service_->GetImageNamed(IDR_MOBILE_LIT)->ToGdkPixbuf());
+}
+
+ChromeToMobileBubbleGtk::~ChromeToMobileBubbleGtk() {
+ DCHECK(g_bubble);
+ g_bubble = NULL;
+}
+
+void ChromeToMobileBubbleGtk::OnDestroy(GtkWidget* widget) {
+ // We are self deleting, we have a destroy signal setup to catch when we
+ // destroyed (via the BubbleGtk being destroyed), and delete ourself.
+ delete this;
+}
+
+void ChromeToMobileBubbleGtk::OnRadioToggled(GtkWidget* widget) {
+ DCHECK(mobile_map_.find(widget) != mobile_map_.end());
+ selected_mobile_ = mobile_map_.find(widget)->second;
+}
+
+void ChromeToMobileBubbleGtk::OnCancelClicked(GtkWidget* widget) {
+ bubble_->Close();
+}
+
+void ChromeToMobileBubbleGtk::OnSendClicked(GtkWidget* widget) {
+ string16 mobile_id;
+ selected_mobile_->GetString("id", &mobile_id);
+ bool send_copy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(send_copy_));
+ ChromeToMobileServiceFactory::GetForProfile(profile_)->SendToMobile(
+ mobile_id, send_copy ? snapshot_path_ : FilePath(),
+ weak_ptr_factory_.GetWeakPtr());
+
+ // Update the view's contents to show the "Sending..." progress animation.
+ gtk_widget_set_sensitive(cancel_, FALSE);
+ gtk_widget_set_sensitive(send_, FALSE);
+ gtk_button_set_alignment(GTK_BUTTON(send_), 0, 0.5);
+ progress_animation_.reset(new ui::ThrobAnimation(this));
+ progress_animation_->SetDuration(kProgressThrobDurationMS);
+ progress_animation_->StartThrobbing(-1);
+}
diff --git a/chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.h b/chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.h
new file mode 100644
index 0000000..0da98d0
--- /dev/null
+++ b/chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is the GTK implementation of the Chrome To Mobile bubble, the transient
+// bubble presented to send URLs and MHTML snapshots to a mobile device. There
+// can only ever be a single bubble open, so the class presents only static
+// methods, and handles the singleton behavior for you. It also handles the
+// object and widget lifetimes, destroying everything when the bubble is closed.
+
+#ifndef CHROME_BROWSER_UI_GTK_CHROME_TO_MOBILE_BUBBLE_GTK_H_
+#define CHROME_BROWSER_UI_GTK_CHROME_TO_MOBILE_BUBBLE_GTK_H_
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chrome_to_mobile_service.h"
+#include "chrome/browser/ui/gtk/bubble/bubble_gtk.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "ui/base/animation/animation_delegate.h"
+#include "ui/base/gtk/gtk_signal.h"
+
+class Profile;
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace ui {
+class ThrobAnimation;
+}
+
+typedef struct _GtkWidget GtkWidget;
+
+class ChromeToMobileBubbleGtk : public BubbleDelegateGtk,
+ public ui::AnimationDelegate,
+ public content::NotificationObserver,
+ public ChromeToMobileService::Observer {
+ public:
+ // Shows the Chrome to Mobile bubble, pointing at |anchor_widget|.
+ // |anchor_image| is updated to show the lit icon during the bubble lifetime.
+ static void Show(GtkImage* anchor_image, Profile* profile);
+
+ // BubbleDelegateGtk:
+ virtual void BubbleClosing(BubbleGtk* bubble, bool closed_by_escape) OVERRIDE;
+
+ // ui::AnimationDelegate:
+ virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
+
+ // content::NotificationObserver:
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // ChromeToMobileService::Observer:
+ virtual void SnapshotGenerated(const FilePath& path, int64 bytes) OVERRIDE;
+ virtual void OnSendComplete(bool success) OVERRIDE;
+
+ private:
+ ChromeToMobileBubbleGtk(GtkImage* anchor_image, Profile* profile);
+ virtual ~ChromeToMobileBubbleGtk();
+
+ // Notified when |content_| is destroyed so we can delete our instance.
+ CHROMEGTK_CALLBACK_0(ChromeToMobileBubbleGtk, void, OnDestroy);
+ CHROMEGTK_CALLBACK_0(ChromeToMobileBubbleGtk, void, OnRadioToggled);
+ CHROMEGTK_CALLBACK_0(ChromeToMobileBubbleGtk, void, OnCancelClicked);
+ CHROMEGTK_CALLBACK_0(ChromeToMobileBubbleGtk, void, OnSendClicked);
+
+ base::WeakPtrFactory<ChromeToMobileBubbleGtk> weak_ptr_factory_;
+
+ Profile* profile_;
+
+ // Support members for getting theme colors and theme change notifications.
+ ThemeServiceGtk* theme_service_;
+ content::NotificationRegistrar registrar_;
+
+ // The file path for the MHTML page snapshot.
+ FilePath snapshot_path_;
+
+ // A map of radio buttons for each mobile device to the device's information.
+ typedef std::map<GtkWidget*, base::DictionaryValue*> MobileMap;
+ MobileMap mobile_map_;
+
+ // The currently selected (or solitary) mobile device's info.
+ base::DictionaryValue* selected_mobile_;
+
+ // The anchor image, updated to show the lit icon during the bubble lifetime.
+ GtkImage* anchor_image_;
+
+ // The labels in the bubble; tracked for theme changes.
+ std::vector<GtkWidget*> labels_;
+
+ GtkWidget* send_copy_;
+ GtkWidget* cancel_;
+ GtkWidget* send_;
+ GtkWidget* error_;
+
+ // The actual BubbleGtk shown by this class and its content.
+ BubbleGtk* bubble_;
+
+ // An animation used to cycle through the "Sending..." status messages.
+ scoped_ptr<ui::ThrobAnimation> progress_animation_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeToMobileBubbleGtk);
+};
+
+#endif // CHROME_BROWSER_UI_GTK_CHROME_TO_MOBILE_BUBBLE_GTK_H_
diff --git a/chrome/browser/ui/gtk/location_bar_view_gtk.cc b/chrome/browser/ui/gtk/location_bar_view_gtk.cc
index 135153c..d276e4b 100644
--- a/chrome/browser/ui/gtk/location_bar_view_gtk.cc
+++ b/chrome/browser/ui/gtk/location_bar_view_gtk.cc
@@ -19,6 +19,8 @@
#include "chrome/browser/accessibility/accessibility_events.h"
#include "chrome/browser/alternate_nav_url_fetcher.h"
#include "chrome/browser/autocomplete/autocomplete_popup_model.h"
+#include "chrome/browser/chrome_to_mobile_service.h"
+#include "chrome/browser/chrome_to_mobile_service_factory.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/content_settings/tab_specific_content_settings.h"
#include "chrome/browser/defaults.h"
@@ -39,6 +41,7 @@
#include "chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.h"
#include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h"
#include "chrome/browser/ui/gtk/browser_window_gtk.h"
+#include "chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.h"
#include "chrome/browser/ui/gtk/content_setting_bubble_gtk.h"
#include "chrome/browser/ui/gtk/extensions/extension_popup_gtk.h"
#include "chrome/browser/ui/gtk/first_run_bubble.h"
@@ -152,6 +155,7 @@ const GdkColor LocationBarViewGtk::kBackgroundColor =
LocationBarViewGtk::LocationBarViewGtk(Browser* browser)
: star_image_(NULL),
starred_(false),
+ chrome_to_mobile_image_(NULL),
site_type_alignment_(NULL),
site_type_event_box_(NULL),
location_icon_image_(NULL),
@@ -183,8 +187,9 @@ LocationBarViewGtk::LocationBarViewGtk(Browser* browser)
}
LocationBarViewGtk::~LocationBarViewGtk() {
- // All of our widgets should have be children of / owned by the alignment.
+ // All of our widgets should be children of / owned by the alignment.
star_.Destroy();
+ chrome_to_mobile_view_.Destroy();
hbox_.Destroy();
content_setting_hbox_.Destroy();
page_action_hbox_.Destroy();
@@ -301,10 +306,19 @@ void LocationBarViewGtk::Init(bool popup_window_mode) {
// doesn't work, someone is probably calling show_all on our parent box.
gtk_box_pack_end(GTK_BOX(entry_box_), tab_to_search_hint_, FALSE, FALSE, 0);
- // We don't show the star in popups, app windows, etc.
+ // Hide the star and Chrome To Mobile icons in popups, app windows, etc.
if (browser_defaults::bookmarks_enabled && !ShouldOnlyShowLocation()) {
CreateStarButton();
gtk_box_pack_end(GTK_BOX(hbox_.get()), star_.get(), FALSE, FALSE, 0);
+
+ // Also disable Chrome To Mobile for off-the-record and non-synced profiles,
+ // or if the feature is disabled by a command line flag or chrome://flags.
+ if (!profile->IsOffTheRecord() && profile->IsSyncAccessible() &&
+ ChromeToMobileService::IsChromeToMobileEnabled()) {
+ CreateChromeToMobileButton();
+ gtk_box_pack_end(GTK_BOX(hbox_.get()), chrome_to_mobile_view_.get(),
+ FALSE, FALSE, 0);
+ }
}
content_setting_hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding + 1));
@@ -452,6 +466,7 @@ GtkWidget* LocationBarViewGtk::GetPageActionWidget(
void LocationBarViewGtk::Update(const WebContents* contents) {
UpdateStarIcon();
+ UpdateChromeToMobileIcon();
UpdateSiteTypeArea();
UpdateContentSettingsIcons();
UpdatePageActions();
@@ -540,6 +555,28 @@ void LocationBarViewGtk::CreateStarButton() {
G_CALLBACK(OnStarButtonPressThunk), this);
}
+void LocationBarViewGtk::CreateChromeToMobileButton() {
+ chrome_to_mobile_image_ = gtk_image_new_from_pixbuf(
+ theme_service_->GetImageNamed(IDR_MOBILE)->ToGdkPixbuf());
+ chrome_to_mobile_view_.Own(gtk_event_box_new());
+ gtk_event_box_set_visible_window(
+ GTK_EVENT_BOX(chrome_to_mobile_view_.get()), FALSE);
+ gtk_container_add(GTK_CONTAINER(chrome_to_mobile_view_.get()),
+ chrome_to_mobile_image_);
+ ViewIDUtil::SetID(chrome_to_mobile_view_.get(),
+ VIEW_ID_CHROME_TO_MOBILE_BUTTON);
+ gtk_widget_set_tooltip_text(chrome_to_mobile_view_.get(),
+ l10n_util::GetStringUTF8(IDS_CHROME_TO_MOBILE_BUBBLE_TOOLTIP).c_str());
+ gtk_widget_set_visible(chrome_to_mobile_view_.get(), FALSE);
+ g_signal_connect(chrome_to_mobile_view_.get(), "button-press-event",
+ G_CALLBACK(OnChromeToMobileButtonPressThunk), this);
+
+ command_updater_->AddCommandObserver(IDC_CHROME_TO_MOBILE_PAGE, this);
+
+ ChromeToMobileServiceFactory::GetForProfile(browser_->profile())->
+ RequestMobileListUpdate();
+}
+
void LocationBarViewGtk::OnInputInProgress(bool in_progress) {
// This is identical to the Windows code, except that we don't proxy the call
// back through the Toolbar, and just access the model here.
@@ -766,6 +803,7 @@ void LocationBarViewGtk::Observe(int type,
const content::NotificationDetails& details) {
if (type == chrome::NOTIFICATION_PREF_CHANGED) {
UpdateStarIcon();
+ UpdateChromeToMobileIcon();
return;
}
@@ -820,6 +858,7 @@ void LocationBarViewGtk::Observe(int type,
}
UpdateStarIcon();
+ UpdateChromeToMobileIcon();
UpdateSiteTypeArea();
UpdateContentSettingsIcons();
}
@@ -1127,6 +1166,16 @@ gboolean LocationBarViewGtk::OnStarButtonPress(GtkWidget* widget,
return TRUE;
}
+gboolean LocationBarViewGtk::OnChromeToMobileButtonPress(
+ GtkWidget* widget,
+ GdkEventButton* event) {
+ if (event->button == 1) {
+ browser_->ExecuteCommand(IDC_CHROME_TO_MOBILE_PAGE);
+ return FALSE;
+ }
+ return TRUE;
+}
+
void LocationBarViewGtk::ShowStarBubble(const GURL& url,
bool newly_bookmarked) {
if (!star_.get())
@@ -1136,6 +1185,13 @@ void LocationBarViewGtk::ShowStarBubble(const GURL& url,
newly_bookmarked);
}
+void LocationBarViewGtk::ShowChromeToMobileBubble() {
+ Profile* profile = browser_->profile();
+ ChromeToMobileServiceFactory::GetForProfile(profile)->
+ RequestMobileListUpdate();
+ ChromeToMobileBubbleGtk::Show(GTK_IMAGE(chrome_to_mobile_image_), profile);
+}
+
void LocationBarViewGtk::SetStarred(bool starred) {
if (starred == starred_)
return;
@@ -1161,6 +1217,18 @@ void LocationBarViewGtk::UpdateStarIcon() {
}
}
+void LocationBarViewGtk::UpdateChromeToMobileIcon() {
+ if (!chrome_to_mobile_view_.get())
+ return;
+
+ Profile* profile = browser_->profile();
+ bool enabled = !toolbar_model_->input_in_progress() &&
+ profile->IsSyncAccessible() &&
+ !ChromeToMobileServiceFactory::GetForProfile(profile)->mobiles().empty();
+ gtk_widget_set_visible(chrome_to_mobile_view_.get(), enabled);
+ command_updater_->UpdateCommandEnabled(IDC_CHROME_TO_MOBILE_PAGE, enabled);
+}
+
bool LocationBarViewGtk::ShouldOnlyShowLocation() {
return !browser_->is_type_tabbed();
}
@@ -1610,6 +1678,12 @@ void LocationBarViewGtk::PageActionViewGtk::Observe(
DisconnectPageActionAccelerator();
}
+void LocationBarViewGtk::EnabledStateChangedForCommand(int id, bool enabled) {
+ DCHECK_EQ(id, IDC_CHROME_TO_MOBILE_PAGE);
+ if (enabled != gtk_widget_get_visible(chrome_to_mobile_view_.get()))
+ UpdateChromeToMobileIcon();
+}
+
void LocationBarViewGtk::PageActionViewGtk::InspectPopup(
ExtensionAction* action) {
ShowPopup(true);
diff --git a/chrome/browser/ui/gtk/location_bar_view_gtk.h b/chrome/browser/ui/gtk/location_bar_view_gtk.h
index 7fada5b..a089c3f 100644
--- a/chrome/browser/ui/gtk/location_bar_view_gtk.h
+++ b/chrome/browser/ui/gtk/location_bar_view_gtk.h
@@ -17,6 +17,7 @@
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "chrome/browser/command_updater.h"
#include "chrome/browser/extensions/extension_context_menu_model.h"
#include "chrome/browser/extensions/image_loading_tracker.h"
#include "chrome/browser/prefs/pref_member.h"
@@ -55,7 +56,8 @@ class AcceleratorGtk;
class LocationBarViewGtk : public AutocompleteEditController,
public LocationBar,
public LocationBarTesting,
- public content::NotificationObserver {
+ public content::NotificationObserver,
+ public CommandUpdater::CommandObserver {
public:
explicit LocationBarViewGtk(Browser* browser);
virtual ~LocationBarViewGtk();
@@ -96,6 +98,9 @@ class LocationBarViewGtk : public AutocompleteEditController,
// Show the bookmark bubble.
void ShowStarBubble(const GURL& url, bool newly_boomkarked);
+ // Shows the Chrome To Mobile bubble.
+ void ShowChromeToMobileBubble();
+
// Set the starred state of the bookmark star.
void SetStarred(bool starred);
@@ -145,6 +150,9 @@ class LocationBarViewGtk : public AutocompleteEditController,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
+ // Implement the CommandUpdater::CommandObserver interface.
+ virtual void EnabledStateChangedForCommand(int id, bool enabled) OVERRIDE;
+
// Edit background color.
static const GdkColor kBackgroundColor;
@@ -354,6 +362,8 @@ class LocationBarViewGtk : public AutocompleteEditController,
GtkAllocation*);
CHROMEGTK_CALLBACK_1(LocationBarViewGtk, gboolean, OnStarButtonPress,
GdkEventButton*);
+ CHROMEGTK_CALLBACK_1(LocationBarViewGtk, gboolean,
+ OnChromeToMobileButtonPress, GdkEventButton*);
// Updates the site type area: changes the icon and shows/hides the EV
// certificate information.
@@ -380,12 +390,16 @@ class LocationBarViewGtk : public AutocompleteEditController,
// available horizontal space in the location bar.
void AdjustChildrenVisibility();
- // Build the star icon.
+ // Build the star and Chrome To Mobile icons.
void CreateStarButton();
+ void CreateChromeToMobileButton();
// Update the star icon after it is toggled or the theme changes.
void UpdateStarIcon();
+ // Update the chrome to mobile icon after the theme changes, etc.
+ void UpdateChromeToMobileIcon();
+
// Returns true if we should only show the URL and none of the extras like
// the star button or page actions.
bool ShouldOnlyShowLocation();
@@ -398,6 +412,10 @@ class LocationBarViewGtk : public AutocompleteEditController,
GtkWidget* star_image_;
bool starred_;
+ // The Chrome To Mobile button and image.
+ ui::OwnedWidgetGtk chrome_to_mobile_view_;
+ GtkWidget* chrome_to_mobile_image_;
+
// An icon to the left of the address bar.
GtkWidget* site_type_alignment_;
GtkWidget* site_type_event_box_;
diff --git a/chrome/browser/ui/gtk/view_id_util.cc b/chrome/browser/ui/gtk/view_id_util.cc
index 2b033c5..c9ad81d 100644
--- a/chrome/browser/ui/gtk/view_id_util.cc
+++ b/chrome/browser/ui/gtk/view_id_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -80,6 +80,9 @@ const char* GetNameFromID(ViewID id) {
case VIEW_ID_STAR_BUTTON:
return "chrome-toolbar-star-button";
+ case VIEW_ID_CHROME_TO_MOBILE_BUTTON:
+ return "chrome-toolbar-chrome-to-mobile-button";
+
case VIEW_ID_LOCATION_BAR:
return "chrome-location-bar";
diff --git a/chrome/browser/ui/gtk/view_id_util_browsertest.cc b/chrome/browser/ui/gtk/view_id_util_browsertest.cc
index a961043..d460cf2 100644
--- a/chrome/browser/ui/gtk/view_id_util_browsertest.cc
+++ b/chrome/browser/ui/gtk/view_id_util_browsertest.cc
@@ -43,8 +43,7 @@ IN_PROC_BROWSER_TEST_F(ViewIDTest, Basic) {
i == VIEW_ID_BOOKMARK_BAR_ELEMENT ||
i == VIEW_ID_TAB ||
i == VIEW_ID_FEEDBACK_BUTTON ||
- i == VIEW_ID_OMNIBOX ||
- i == VIEW_ID_CHROME_TO_MOBILE_BUTTON) {
+ i == VIEW_ID_OMNIBOX) {
continue;
}
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 97ac453..128288d 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2845,6 +2845,8 @@
'browser/ui/gtk/certificate_viewer_gtk.cc',
'browser/ui/gtk/chrome_gtk_frame.cc',
'browser/ui/gtk/chrome_gtk_frame.h',
+ 'browser/ui/gtk/chrome_to_mobile_bubble_gtk.cc',
+ 'browser/ui/gtk/chrome_to_mobile_bubble_gtk.h',
'browser/ui/gtk/collected_cookies_gtk.cc',
'browser/ui/gtk/collected_cookies_gtk.h',
'browser/ui/gtk/confirm_bubble_view.cc',