OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/ui/views/chrome_to_mobile_bubble_view.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/file_util.h" |
| 9 #include "base/string16.h" |
| 10 #include "base/utf_string_conversions.h" |
| 11 #include "base/values.h" |
| 12 #include "chrome/app/chrome_command_ids.h" |
| 13 #include "chrome/browser/browser_process.h" |
| 14 #include "chrome/browser/chrome_to_mobile_service.h" |
| 15 #include "chrome/browser/chrome_to_mobile_service_factory.h" |
| 16 #include "chrome/browser/profiles/profile.h" |
| 17 #include "chrome/browser/ui/views/window.h" |
| 18 #include "grit/generated_resources.h" |
| 19 #include "grit/theme_resources.h" |
| 20 #include "ui/base/animation/throb_animation.h" |
| 21 #include "ui/base/keycodes/keyboard_codes.h" |
| 22 #include "ui/base/l10n/l10n_util.h" |
| 23 #include "ui/base/resource/resource_bundle.h" |
| 24 #include "ui/base/text/bytes_formatting.h" |
| 25 #include "ui/views/controls/button/checkbox.h" |
| 26 #include "ui/views/controls/button/radio_button.h" |
| 27 #include "ui/views/controls/button/text_button.h" |
| 28 #include "ui/views/controls/label.h" |
| 29 #include "ui/views/events/event.h" |
| 30 #include "ui/views/layout/grid_layout.h" |
| 31 #include "ui/views/layout/layout_constants.h" |
| 32 |
| 33 using views::GridLayout; |
| 34 |
| 35 namespace { |
| 36 |
| 37 // The millisecond duration of the "Sending..." progress throb animation. |
| 38 const size_t kProgressThrobDurationMS = 2400; |
| 39 |
| 40 // The bubble's margin for the "Sending..." and "Sent" states. |
| 41 const size_t kProgressMargin = 20; |
| 42 |
| 43 // The title label's color; matches the bookmark bubble's title. |
| 44 const SkColor kTitleColor = 0xFF062D75; |
| 45 |
| 46 } // namespace |
| 47 |
| 48 // Declared in browser_dialogs.h so callers don't have to depend on our header. |
| 49 |
| 50 namespace browser { |
| 51 |
| 52 void ShowChromeToMobileBubbleView(views::View* anchor_view, Profile* profile) { |
| 53 ChromeToMobileBubbleView::ShowBubble(anchor_view, profile); |
| 54 } |
| 55 |
| 56 void HideChromeToMobileBubbleView() { |
| 57 ChromeToMobileBubbleView::Hide(); |
| 58 } |
| 59 |
| 60 bool IsChromeToMobileBubbleViewShowing() { |
| 61 return ChromeToMobileBubbleView::IsShowing(); |
| 62 } |
| 63 |
| 64 } // namespace browser |
| 65 |
| 66 // ChromeToMobileBubbleView ---------------------------------------------------- |
| 67 |
| 68 ChromeToMobileBubbleView* ChromeToMobileBubbleView::bubble_ = NULL; |
| 69 |
| 70 ChromeToMobileBubbleView::~ChromeToMobileBubbleView() {} |
| 71 |
| 72 // static |
| 73 void ChromeToMobileBubbleView::ShowBubble(views::View* anchor_view, |
| 74 Profile* profile) { |
| 75 if (IsShowing()) |
| 76 return; |
| 77 |
| 78 bubble_ = new ChromeToMobileBubbleView(anchor_view, profile); |
| 79 browser::CreateViewsBubble(bubble_); |
| 80 bubble_->Show(); |
| 81 } |
| 82 |
| 83 // static |
| 84 bool ChromeToMobileBubbleView::IsShowing() { |
| 85 return bubble_ != NULL; |
| 86 } |
| 87 |
| 88 void ChromeToMobileBubbleView::Hide() { |
| 89 if (IsShowing()) |
| 90 bubble_->GetWidget()->Close(); |
| 91 } |
| 92 |
| 93 views::View* ChromeToMobileBubbleView::GetInitiallyFocusedView() { |
| 94 return send_; |
| 95 } |
| 96 |
| 97 gfx::Rect ChromeToMobileBubbleView::GetAnchorRect() { |
| 98 // Compensate for some built-in padding in the arrow image. |
| 99 gfx::Rect rect(BubbleDelegateView::GetAnchorRect()); |
| 100 rect.Inset(0, anchor_view() ? 5 : 0); |
| 101 return rect; |
| 102 } |
| 103 |
| 104 void ChromeToMobileBubbleView::WindowClosing() { |
| 105 // We have to reset |bubble_| here, not in our destructor, because we'll be |
| 106 // destroyed asynchronously and the shown state will be checked before then. |
| 107 DCHECK(bubble_ == this); |
| 108 bubble_ = NULL; |
| 109 } |
| 110 |
| 111 bool ChromeToMobileBubbleView::AcceleratorPressed( |
| 112 const ui::Accelerator& accelerator) { |
| 113 if (accelerator.key_code() == ui::VKEY_RETURN && |
| 114 (send_->HasFocus() || cancel_->HasFocus())) { |
| 115 HandleButtonPressed(send_->HasFocus() ? send_ : cancel_); |
| 116 return true; |
| 117 } |
| 118 return BubbleDelegateView::AcceleratorPressed(accelerator); |
| 119 } |
| 120 |
| 121 void ChromeToMobileBubbleView::AnimationProgressed( |
| 122 const ui::Animation* animation) { |
| 123 if (animation == progress_animation_.get()) { |
| 124 double animation_value = animation->GetCurrentValue(); |
| 125 int message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_3; |
| 126 // Show each of four messages for 1/4 of the animation. |
| 127 if (animation_value < 0.25) |
| 128 message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_0; |
| 129 else if (animation_value < 0.5) |
| 130 message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_1; |
| 131 else if (animation_value < 0.75) |
| 132 message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_2; |
| 133 progress_label_->SetText(l10n_util::GetStringUTF16(message)); |
| 134 // Run Layout but do not resize the bubble for each progress message. |
| 135 Layout(); |
| 136 return; |
| 137 } |
| 138 views::BubbleDelegateView::AnimationProgressed(animation); |
| 139 } |
| 140 |
| 141 void ChromeToMobileBubbleView::ButtonPressed(views::Button* sender, |
| 142 const views::Event& event) { |
| 143 HandleButtonPressed(sender); |
| 144 } |
| 145 |
| 146 void ChromeToMobileBubbleView::SnapshotGenerated(const FilePath& path, |
| 147 int64 bytes) { |
| 148 if (bytes > 0) { |
| 149 snapshot_path_ = path; |
| 150 send_copy_->SetText(l10n_util::GetStringFUTF16( |
| 151 IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY, ui::FormatBytes(bytes))); |
| 152 send_copy_->SetEnabled(true); |
| 153 } else { |
| 154 send_copy_->SetText(l10n_util::GetStringUTF16( |
| 155 IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_FAILED)); |
| 156 } |
| 157 Layout(); |
| 158 } |
| 159 |
| 160 void ChromeToMobileBubbleView::OnSendComplete(bool success) { |
| 161 progress_animation_->Stop(); |
| 162 progress_label_->SetText(l10n_util::GetStringUTF16(success ? |
| 163 IDS_CHROME_TO_MOBILE_BUBBLE_SENT : IDS_CHROME_TO_MOBILE_BUBBLE_ERROR)); |
| 164 SizeToContents(); |
| 165 } |
| 166 |
| 167 void ChromeToMobileBubbleView::Init() { |
| 168 GridLayout* layout = new GridLayout(this); |
| 169 SetLayoutManager(layout); |
| 170 |
| 171 const size_t single_column_set_id = 0; |
| 172 views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id); |
| 173 cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, |
| 174 GridLayout::USE_PREF, 0, 0); |
| 175 cs->AddPaddingColumn(1, 0); |
| 176 |
| 177 const size_t button_column_set_id = 1; |
| 178 cs = layout->AddColumnSet(button_column_set_id); |
| 179 cs->AddPaddingColumn(1, 0); |
| 180 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0, |
| 181 GridLayout::USE_PREF, 0, 0); |
| 182 // Subtract 2px for the natural button padding and to correspond with row |
| 183 // separation height; like BookmarkBubbleView. |
| 184 cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing - 2); |
| 185 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0, |
| 186 GridLayout::USE_PREF, 0, 0); |
| 187 |
| 188 std::vector<DictionaryValue*> mobiles = |
| 189 ChromeToMobileServiceFactory::GetForProfile(profile_)->mobiles(); |
| 190 DCHECK_GT(mobiles.size(), 0U); |
| 191 |
| 192 layout->StartRow(0, single_column_set_id); |
| 193 views::Label* title_label = new views::Label(); |
| 194 title_label->SetFont( |
| 195 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont)); |
| 196 title_label->SetEnabledColor(kTitleColor); |
| 197 layout->AddView(title_label); |
| 198 |
| 199 if (mobiles.size() == 1) { |
| 200 selected_mobile_ = mobiles[0]; |
| 201 string16 mobile_name; |
| 202 mobiles[0]->GetString("name", &mobile_name); |
| 203 title_label->SetText(l10n_util::GetStringFUTF16( |
| 204 IDS_CHROME_TO_MOBILE_BUBBLE_SINGLE_TITLE, mobile_name)); |
| 205 } else { |
| 206 title_label->SetText(l10n_util::GetStringUTF16( |
| 207 IDS_CHROME_TO_MOBILE_BUBBLE_MULTI_TITLE)); |
| 208 |
| 209 const size_t radio_column_set_id = 2; |
| 210 cs = layout->AddColumnSet(radio_column_set_id); |
| 211 cs->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); |
| 212 cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, |
| 213 GridLayout::USE_PREF, 0, 0); |
| 214 |
| 215 views::RadioButton* radio; |
| 216 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); |
| 217 for (std::vector<DictionaryValue*>::const_iterator it = mobiles.begin(); |
| 218 it != mobiles.end(); ++it) { |
| 219 string16 name; |
| 220 (*it)->GetString("name", &name); |
| 221 radio = new views::RadioButton(name, 0); |
| 222 radio->set_listener(this); |
| 223 mobile_map_[radio] = *it; |
| 224 layout->StartRow(0, radio_column_set_id); |
| 225 layout->AddView(radio); |
| 226 } |
| 227 mobile_map_.begin()->first->SetChecked(true); |
| 228 selected_mobile_ = mobile_map_.begin()->second; |
| 229 } |
| 230 |
| 231 send_copy_ = new views::Checkbox( |
| 232 l10n_util::GetStringFUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY, |
| 233 l10n_util::GetStringUTF16( |
| 234 IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_GENERATING))); |
| 235 send_copy_->SetEnabled(false); |
| 236 layout->StartRow(0, single_column_set_id); |
| 237 layout->AddView(send_copy_); |
| 238 |
| 239 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); |
| 240 send_ = new views::NativeTextButton( |
| 241 this, l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND)); |
| 242 send_->SetIsDefault(true); |
| 243 cancel_ = new views::NativeTextButton( |
| 244 this, l10n_util::GetStringUTF16(IDS_CANCEL)); |
| 245 layout->StartRow(0, button_column_set_id); |
| 246 layout->AddView(send_); |
| 247 layout->AddView(cancel_); |
| 248 |
| 249 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, 0)); |
| 250 } |
| 251 |
| 252 ChromeToMobileBubbleView::ChromeToMobileBubbleView(views::View* anchor_view, |
| 253 Profile* profile) |
| 254 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), |
| 255 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), |
| 256 profile_(profile), |
| 257 selected_mobile_(NULL), |
| 258 send_copy_(NULL), |
| 259 send_(NULL), |
| 260 cancel_(NULL), |
| 261 progress_label_(NULL) { |
| 262 // Generate the MHTML snapshot now to report its size in the bubble. |
| 263 ChromeToMobileServiceFactory::GetForProfile(profile)-> |
| 264 GenerateSnapshot(weak_ptr_factory_.GetWeakPtr()); |
| 265 } |
| 266 |
| 267 void ChromeToMobileBubbleView::HandleButtonPressed(views::Button* sender) { |
| 268 if (sender == send_) { |
| 269 Send(); |
| 270 } else if (sender == cancel_) { |
| 271 GetWidget()->Close(); |
| 272 } else { |
| 273 // The sender is a mobile radio button |
| 274 views::RadioButton* radio = static_cast<views::RadioButton*>(sender); |
| 275 DCHECK(mobile_map_.find(radio) != mobile_map_.end()); |
| 276 selected_mobile_ = mobile_map_.find(radio)->second; |
| 277 } |
| 278 } |
| 279 |
| 280 void ChromeToMobileBubbleView::Send() { |
| 281 string16 mobile_id; |
| 282 selected_mobile_->GetString("id", &mobile_id); |
| 283 ChromeToMobileServiceFactory::GetForProfile(profile_)->SendToMobile( |
| 284 mobile_id, send_copy_->checked() ? snapshot_path_ : FilePath(), |
| 285 weak_ptr_factory_.GetWeakPtr()); |
| 286 |
| 287 // Re-initialize the view's contents to show progress sending the page. |
| 288 RemoveAllChildViews(true); |
| 289 send_copy_ = NULL; |
| 290 send_ = NULL; |
| 291 cancel_ = NULL; |
| 292 |
| 293 GridLayout* layout = new GridLayout(this); |
| 294 SetLayoutManager(layout); |
| 295 |
| 296 const size_t single_column_set_id = 0; |
| 297 views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id); |
| 298 cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, |
| 299 GridLayout::USE_PREF, 0, 0); |
| 300 set_margin(kProgressMargin); |
| 301 |
| 302 // Use the final (longest) progress label string to resize the bubble. |
| 303 layout->StartRow(0, single_column_set_id); |
| 304 progress_label_ = new views::Label( |
| 305 l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_3)); |
| 306 progress_label_->SetFont( |
| 307 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont)); |
| 308 progress_label_->SetEnabledColor(kTitleColor); |
| 309 layout->AddView(progress_label_); |
| 310 SizeToContents(); |
| 311 |
| 312 progress_animation_.reset(new ui::ThrobAnimation(this)); |
| 313 progress_animation_->SetDuration(kProgressThrobDurationMS); |
| 314 progress_animation_->StartThrobbing(-1); |
| 315 } |
OLD | NEW |