diff options
author | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-22 00:44:41 +0000 |
---|---|---|
committer | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-22 00:44:41 +0000 |
commit | e4ae8fc0a52d5553f3341b831bf200445047998c (patch) | |
tree | 4ce8ca5eca798cbb5a433f7a9bfd3d2b39e4af6f /chrome | |
parent | d5f7f81c19d0481a61554f465ed7ec367054a005 (diff) | |
download | chromium_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.cc | 2 | ||||
-rw-r--r-- | chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.cc | 302 | ||||
-rw-r--r-- | chrome/browser/ui/gtk/chrome_to_mobile_bubble_gtk.h | 114 | ||||
-rw-r--r-- | chrome/browser/ui/gtk/location_bar_view_gtk.cc | 78 | ||||
-rw-r--r-- | chrome/browser/ui/gtk/location_bar_view_gtk.h | 22 | ||||
-rw-r--r-- | chrome/browser/ui/gtk/view_id_util.cc | 5 | ||||
-rw-r--r-- | chrome/browser/ui/gtk/view_id_util_browsertest.cc | 3 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 |
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', |