| Index: chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc
|
| diff --git a/chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc b/chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..68d57b7f3cdc7309b97246b3ca7f09186bf8bece
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc
|
| @@ -0,0 +1,315 @@
|
| +// 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/views/chrome_to_mobile_bubble_view.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/file_util.h"
|
| +#include "base/string16.h"
|
| +#include "base/utf_string_conversions.h"
|
| +#include "base/values.h"
|
| +#include "chrome/app/chrome_command_ids.h"
|
| +#include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/chrome_to_mobile_service.h"
|
| +#include "chrome/browser/chrome_to_mobile_service_factory.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/ui/views/window.h"
|
| +#include "grit/generated_resources.h"
|
| +#include "grit/theme_resources.h"
|
| +#include "ui/base/animation/throb_animation.h"
|
| +#include "ui/base/keycodes/keyboard_codes.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +#include "ui/base/resource/resource_bundle.h"
|
| +#include "ui/base/text/bytes_formatting.h"
|
| +#include "ui/views/controls/button/checkbox.h"
|
| +#include "ui/views/controls/button/radio_button.h"
|
| +#include "ui/views/controls/button/text_button.h"
|
| +#include "ui/views/controls/label.h"
|
| +#include "ui/views/events/event.h"
|
| +#include "ui/views/layout/grid_layout.h"
|
| +#include "ui/views/layout/layout_constants.h"
|
| +
|
| +using views::GridLayout;
|
| +
|
| +namespace {
|
| +
|
| +// The millisecond duration of the "Sending..." progress throb animation.
|
| +const size_t kProgressThrobDurationMS = 2400;
|
| +
|
| +// The bubble's margin for the "Sending..." and "Sent" states.
|
| +const size_t kProgressMargin = 20;
|
| +
|
| +// The title label's color; matches the bookmark bubble's title.
|
| +const SkColor kTitleColor = 0xFF062D75;
|
| +
|
| +} // namespace
|
| +
|
| +// Declared in browser_dialogs.h so callers don't have to depend on our header.
|
| +
|
| +namespace browser {
|
| +
|
| +void ShowChromeToMobileBubbleView(views::View* anchor_view, Profile* profile) {
|
| + ChromeToMobileBubbleView::ShowBubble(anchor_view, profile);
|
| +}
|
| +
|
| +void HideChromeToMobileBubbleView() {
|
| + ChromeToMobileBubbleView::Hide();
|
| +}
|
| +
|
| +bool IsChromeToMobileBubbleViewShowing() {
|
| + return ChromeToMobileBubbleView::IsShowing();
|
| +}
|
| +
|
| +} // namespace browser
|
| +
|
| +// ChromeToMobileBubbleView ----------------------------------------------------
|
| +
|
| +ChromeToMobileBubbleView* ChromeToMobileBubbleView::bubble_ = NULL;
|
| +
|
| +ChromeToMobileBubbleView::~ChromeToMobileBubbleView() {}
|
| +
|
| +// static
|
| +void ChromeToMobileBubbleView::ShowBubble(views::View* anchor_view,
|
| + Profile* profile) {
|
| + if (IsShowing())
|
| + return;
|
| +
|
| + bubble_ = new ChromeToMobileBubbleView(anchor_view, profile);
|
| + browser::CreateViewsBubble(bubble_);
|
| + bubble_->Show();
|
| +}
|
| +
|
| +// static
|
| +bool ChromeToMobileBubbleView::IsShowing() {
|
| + return bubble_ != NULL;
|
| +}
|
| +
|
| +void ChromeToMobileBubbleView::Hide() {
|
| + if (IsShowing())
|
| + bubble_->GetWidget()->Close();
|
| +}
|
| +
|
| +views::View* ChromeToMobileBubbleView::GetInitiallyFocusedView() {
|
| + return send_;
|
| +}
|
| +
|
| +gfx::Rect ChromeToMobileBubbleView::GetAnchorRect() {
|
| + // Compensate for some built-in padding in the arrow image.
|
| + gfx::Rect rect(BubbleDelegateView::GetAnchorRect());
|
| + rect.Inset(0, anchor_view() ? 5 : 0);
|
| + return rect;
|
| +}
|
| +
|
| +void ChromeToMobileBubbleView::WindowClosing() {
|
| + // We have to reset |bubble_| here, not in our destructor, because we'll be
|
| + // destroyed asynchronously and the shown state will be checked before then.
|
| + DCHECK(bubble_ == this);
|
| + bubble_ = NULL;
|
| +}
|
| +
|
| +bool ChromeToMobileBubbleView::AcceleratorPressed(
|
| + const ui::Accelerator& accelerator) {
|
| + if (accelerator.key_code() == ui::VKEY_RETURN &&
|
| + (send_->HasFocus() || cancel_->HasFocus())) {
|
| + HandleButtonPressed(send_->HasFocus() ? send_ : cancel_);
|
| + return true;
|
| + }
|
| + return BubbleDelegateView::AcceleratorPressed(accelerator);
|
| +}
|
| +
|
| +void ChromeToMobileBubbleView::AnimationProgressed(
|
| + const ui::Animation* animation) {
|
| + if (animation == progress_animation_.get()) {
|
| + double animation_value = animation->GetCurrentValue();
|
| + int message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_3;
|
| + // Show each of four messages for 1/4 of the animation.
|
| + if (animation_value < 0.25)
|
| + message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_0;
|
| + else if (animation_value < 0.5)
|
| + message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_1;
|
| + else if (animation_value < 0.75)
|
| + message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_2;
|
| + progress_label_->SetText(l10n_util::GetStringUTF16(message));
|
| + // Run Layout but do not resize the bubble for each progress message.
|
| + Layout();
|
| + return;
|
| + }
|
| + views::BubbleDelegateView::AnimationProgressed(animation);
|
| +}
|
| +
|
| +void ChromeToMobileBubbleView::ButtonPressed(views::Button* sender,
|
| + const views::Event& event) {
|
| + HandleButtonPressed(sender);
|
| +}
|
| +
|
| +void ChromeToMobileBubbleView::SnapshotGenerated(const FilePath& path,
|
| + int64 bytes) {
|
| + if (bytes > 0) {
|
| + snapshot_path_ = path;
|
| + send_copy_->SetText(l10n_util::GetStringFUTF16(
|
| + IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY, ui::FormatBytes(bytes)));
|
| + send_copy_->SetEnabled(true);
|
| + } else {
|
| + send_copy_->SetText(l10n_util::GetStringUTF16(
|
| + IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_FAILED));
|
| + }
|
| + Layout();
|
| +}
|
| +
|
| +void ChromeToMobileBubbleView::OnSendComplete(bool success) {
|
| + progress_animation_->Stop();
|
| + progress_label_->SetText(l10n_util::GetStringUTF16(success ?
|
| + IDS_CHROME_TO_MOBILE_BUBBLE_SENT : IDS_CHROME_TO_MOBILE_BUBBLE_ERROR));
|
| + SizeToContents();
|
| +}
|
| +
|
| +void ChromeToMobileBubbleView::Init() {
|
| + GridLayout* layout = new GridLayout(this);
|
| + SetLayoutManager(layout);
|
| +
|
| + const size_t single_column_set_id = 0;
|
| + views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id);
|
| + cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
|
| + GridLayout::USE_PREF, 0, 0);
|
| + cs->AddPaddingColumn(1, 0);
|
| +
|
| + const size_t button_column_set_id = 1;
|
| + cs = layout->AddColumnSet(button_column_set_id);
|
| + cs->AddPaddingColumn(1, 0);
|
| + cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
|
| + GridLayout::USE_PREF, 0, 0);
|
| + // Subtract 2px for the natural button padding and to correspond with row
|
| + // separation height; like BookmarkBubbleView.
|
| + cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing - 2);
|
| + cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
|
| + GridLayout::USE_PREF, 0, 0);
|
| +
|
| + std::vector<DictionaryValue*> mobiles =
|
| + ChromeToMobileServiceFactory::GetForProfile(profile_)->mobiles();
|
| + DCHECK_GT(mobiles.size(), 0U);
|
| +
|
| + layout->StartRow(0, single_column_set_id);
|
| + views::Label* title_label = new views::Label();
|
| + title_label->SetFont(
|
| + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont));
|
| + title_label->SetEnabledColor(kTitleColor);
|
| + layout->AddView(title_label);
|
| +
|
| + if (mobiles.size() == 1) {
|
| + selected_mobile_ = mobiles[0];
|
| + string16 mobile_name;
|
| + mobiles[0]->GetString("name", &mobile_name);
|
| + title_label->SetText(l10n_util::GetStringFUTF16(
|
| + IDS_CHROME_TO_MOBILE_BUBBLE_SINGLE_TITLE, mobile_name));
|
| + } else {
|
| + title_label->SetText(l10n_util::GetStringUTF16(
|
| + IDS_CHROME_TO_MOBILE_BUBBLE_MULTI_TITLE));
|
| +
|
| + const size_t radio_column_set_id = 2;
|
| + cs = layout->AddColumnSet(radio_column_set_id);
|
| + cs->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
|
| + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
|
| + GridLayout::USE_PREF, 0, 0);
|
| +
|
| + views::RadioButton* radio;
|
| + layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
|
| + for (std::vector<DictionaryValue*>::const_iterator it = mobiles.begin();
|
| + it != mobiles.end(); ++it) {
|
| + string16 name;
|
| + (*it)->GetString("name", &name);
|
| + radio = new views::RadioButton(name, 0);
|
| + radio->set_listener(this);
|
| + mobile_map_[radio] = *it;
|
| + layout->StartRow(0, radio_column_set_id);
|
| + layout->AddView(radio);
|
| + }
|
| + mobile_map_.begin()->first->SetChecked(true);
|
| + selected_mobile_ = mobile_map_.begin()->second;
|
| + }
|
| +
|
| + send_copy_ = new views::Checkbox(
|
| + l10n_util::GetStringFUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY,
|
| + l10n_util::GetStringUTF16(
|
| + IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_GENERATING)));
|
| + send_copy_->SetEnabled(false);
|
| + layout->StartRow(0, single_column_set_id);
|
| + layout->AddView(send_copy_);
|
| +
|
| + layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
|
| + send_ = new views::NativeTextButton(
|
| + this, l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND));
|
| + send_->SetIsDefault(true);
|
| + cancel_ = new views::NativeTextButton(
|
| + this, l10n_util::GetStringUTF16(IDS_CANCEL));
|
| + layout->StartRow(0, button_column_set_id);
|
| + layout->AddView(send_);
|
| + layout->AddView(cancel_);
|
| +
|
| + AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, 0));
|
| +}
|
| +
|
| +ChromeToMobileBubbleView::ChromeToMobileBubbleView(views::View* anchor_view,
|
| + Profile* profile)
|
| + : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
|
| + profile_(profile),
|
| + selected_mobile_(NULL),
|
| + send_copy_(NULL),
|
| + send_(NULL),
|
| + cancel_(NULL),
|
| + progress_label_(NULL) {
|
| + // Generate the MHTML snapshot now to report its size in the bubble.
|
| + ChromeToMobileServiceFactory::GetForProfile(profile)->
|
| + GenerateSnapshot(weak_ptr_factory_.GetWeakPtr());
|
| +}
|
| +
|
| +void ChromeToMobileBubbleView::HandleButtonPressed(views::Button* sender) {
|
| + if (sender == send_) {
|
| + Send();
|
| + } else if (sender == cancel_) {
|
| + GetWidget()->Close();
|
| + } else {
|
| + // The sender is a mobile radio button
|
| + views::RadioButton* radio = static_cast<views::RadioButton*>(sender);
|
| + DCHECK(mobile_map_.find(radio) != mobile_map_.end());
|
| + selected_mobile_ = mobile_map_.find(radio)->second;
|
| + }
|
| +}
|
| +
|
| +void ChromeToMobileBubbleView::Send() {
|
| + string16 mobile_id;
|
| + selected_mobile_->GetString("id", &mobile_id);
|
| + ChromeToMobileServiceFactory::GetForProfile(profile_)->SendToMobile(
|
| + mobile_id, send_copy_->checked() ? snapshot_path_ : FilePath(),
|
| + weak_ptr_factory_.GetWeakPtr());
|
| +
|
| + // Re-initialize the view's contents to show progress sending the page.
|
| + RemoveAllChildViews(true);
|
| + send_copy_ = NULL;
|
| + send_ = NULL;
|
| + cancel_ = NULL;
|
| +
|
| + GridLayout* layout = new GridLayout(this);
|
| + SetLayoutManager(layout);
|
| +
|
| + const size_t single_column_set_id = 0;
|
| + views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id);
|
| + cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
|
| + GridLayout::USE_PREF, 0, 0);
|
| + set_margin(kProgressMargin);
|
| +
|
| + // Use the final (longest) progress label string to resize the bubble.
|
| + layout->StartRow(0, single_column_set_id);
|
| + progress_label_ = new views::Label(
|
| + l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_3));
|
| + progress_label_->SetFont(
|
| + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont));
|
| + progress_label_->SetEnabledColor(kTitleColor);
|
| + layout->AddView(progress_label_);
|
| + SizeToContents();
|
| +
|
| + progress_animation_.reset(new ui::ThrobAnimation(this));
|
| + progress_animation_->SetDuration(kProgressThrobDurationMS);
|
| + progress_animation_->StartThrobbing(-1);
|
| +}
|
|
|