message_box_view.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
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 "ui/views/controls/message_box_view.h" 6 7#include "base/i18n/rtl.h" 8#include "base/message_loop.h" 9#include "base/strings/string_split.h" 10#include "base/utf_string_conversions.h" 11#include "ui/base/accessibility/accessible_view_state.h" 12#include "ui/base/clipboard/scoped_clipboard_writer.h" 13#include "ui/views/controls/button/checkbox.h" 14#include "ui/views/controls/image_view.h" 15#include "ui/views/controls/label.h" 16#include "ui/views/controls/textfield/textfield.h" 17#include "ui/views/layout/grid_layout.h" 18#include "ui/views/layout/layout_constants.h" 19#include "ui/views/widget/widget.h" 20#include "ui/views/window/client_view.h" 21 22namespace { 23 24const int kDefaultMessageWidth = 320; 25 26// Paragraph separators are defined in 27// http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBidiClass.txt 28// 29// # Bidi_Class=Paragraph_Separator 30// 31// 000A ; B # Cc <control-000A> 32// 000D ; B # Cc <control-000D> 33// 001C..001E ; B # Cc [3] <control-001C>..<control-001E> 34// 0085 ; B # Cc <control-0085> 35// 2029 ; B # Zp PARAGRAPH SEPARATOR 36bool IsParagraphSeparator(char16 c) { 37 return ( c == 0x000A || c == 0x000D || c == 0x001C || c == 0x001D || 38 c == 0x001E || c == 0x0085 || c == 0x2029); 39} 40 41// Splits |text| into a vector of paragraphs. 42// Given an example "\nabc\ndef\n\n\nhij\n", the split results should be: 43// "", "abc", "def", "", "", "hij", and "". 44void SplitStringIntoParagraphs(const string16& text, 45 std::vector<string16>* paragraphs) { 46 paragraphs->clear(); 47 48 size_t start = 0; 49 for (size_t i = 0; i < text.length(); ++i) { 50 if (IsParagraphSeparator(text[i])) { 51 paragraphs->push_back(text.substr(start, i - start)); 52 start = i + 1; 53 } 54 } 55 paragraphs->push_back(text.substr(start, text.length() - start)); 56} 57 58} // namespace 59 60namespace views { 61 62/////////////////////////////////////////////////////////////////////////////// 63// MessageBoxView, public: 64 65MessageBoxView::InitParams::InitParams(const string16& message) 66 : options(NO_OPTIONS), 67 message(message), 68 message_width(kDefaultMessageWidth), 69 inter_row_vertical_spacing(kRelatedControlVerticalSpacing), 70 clipboard_source_tag() {} 71 72MessageBoxView::InitParams::~InitParams() { 73} 74 75MessageBoxView::MessageBoxView(const InitParams& params) 76 : prompt_field_(NULL), 77 icon_(NULL), 78 checkbox_(NULL), 79 message_width_(params.message_width) { 80 Init(params); 81} 82 83MessageBoxView::~MessageBoxView() {} 84 85string16 MessageBoxView::GetInputText() { 86 return prompt_field_ ? prompt_field_->text() : string16(); 87} 88 89bool MessageBoxView::IsCheckBoxSelected() { 90 return checkbox_ ? checkbox_->checked() : false; 91} 92 93void MessageBoxView::SetIcon(const gfx::ImageSkia& icon) { 94 if (!icon_) 95 icon_ = new ImageView(); 96 icon_->SetImage(icon); 97 icon_->SetBounds(0, 0, icon.width(), icon.height()); 98 ResetLayoutManager(); 99} 100 101void MessageBoxView::SetCheckBoxLabel(const string16& label) { 102 if (!checkbox_) 103 checkbox_ = new Checkbox(label); 104 else 105 checkbox_->SetText(label); 106 ResetLayoutManager(); 107} 108 109void MessageBoxView::SetCheckBoxSelected(bool selected) { 110 if (!checkbox_) 111 return; 112 checkbox_->SetChecked(selected); 113} 114 115void MessageBoxView::GetAccessibleState(ui::AccessibleViewState* state) { 116 state->role = ui::AccessibilityTypes::ROLE_ALERT; 117} 118 119/////////////////////////////////////////////////////////////////////////////// 120// MessageBoxView, View overrides: 121 122void MessageBoxView::ViewHierarchyChanged( 123 const ViewHierarchyChangedDetails& details) { 124 if (details.child == this && details.is_add) { 125 if (prompt_field_) 126 prompt_field_->SelectAll(true); 127 128 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true); 129 } 130} 131 132bool MessageBoxView::AcceleratorPressed(const ui::Accelerator& accelerator) { 133 // We only accepts Ctrl-C. 134 DCHECK(accelerator.key_code() == 'C' && accelerator.IsCtrlDown()); 135 136 // We must not intercept Ctrl-C when we have a text box and it's focused. 137 if (prompt_field_ && prompt_field_->HasFocus()) 138 return false; 139 140 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 141 if (!clipboard) 142 return false; 143 144 ui::ScopedClipboardWriter scw(clipboard, 145 ui::Clipboard::BUFFER_STANDARD, 146 source_tag_); 147 string16 text = message_labels_[0]->text(); 148 for (size_t i = 1; i < message_labels_.size(); ++i) 149 text += message_labels_[i]->text(); 150 scw.WriteText(text); 151 return true; 152} 153 154/////////////////////////////////////////////////////////////////////////////// 155// MessageBoxView, private: 156 157void MessageBoxView::Init(const InitParams& params) { 158 if (params.options & DETECT_DIRECTIONALITY) { 159 std::vector<string16> texts; 160 SplitStringIntoParagraphs(params.message, &texts); 161 // If the text originates from a web page, its alignment is based on its 162 // first character with strong directionality. 163 base::i18n::TextDirection message_direction = 164 base::i18n::GetFirstStrongCharacterDirection(params.message); 165 gfx::HorizontalAlignment alignment = 166 (message_direction == base::i18n::RIGHT_TO_LEFT) ? 167 gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; 168 for (size_t i = 0; i < texts.size(); ++i) { 169 Label* message_label = new Label(texts[i]); 170 // Don't set multi-line to true if the text is empty, else the label will 171 // have a height of 0. 172 message_label->SetMultiLine(!texts[i].empty()); 173 message_label->SetAllowCharacterBreak(true); 174 message_label->set_directionality_mode(Label::AUTO_DETECT_DIRECTIONALITY); 175 message_label->SetHorizontalAlignment(alignment); 176 message_labels_.push_back(message_label); 177 } 178 } else { 179 Label* message_label = new Label(params.message); 180 message_label->SetMultiLine(true); 181 message_label->SetAllowCharacterBreak(true); 182 message_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 183 message_labels_.push_back(message_label); 184 } 185 186 if (params.options & HAS_PROMPT_FIELD) { 187 prompt_field_ = new Textfield; 188 prompt_field_->SetText(params.default_prompt); 189 } 190 191 inter_row_vertical_spacing_ = params.inter_row_vertical_spacing; 192 source_tag_ = params.clipboard_source_tag; 193 194 ResetLayoutManager(); 195} 196 197void MessageBoxView::ResetLayoutManager() { 198 // Initialize the Grid Layout Manager used for this dialog box. 199 GridLayout* layout = GridLayout::CreatePanel(this); 200 SetLayoutManager(layout); 201 202 gfx::Size icon_size; 203 if (icon_) 204 icon_size = icon_->GetPreferredSize(); 205 206 // Add the column set for the message displayed at the top of the dialog box. 207 // And an icon, if one has been set. 208 const int message_column_view_set_id = 0; 209 ColumnSet* column_set = layout->AddColumnSet(message_column_view_set_id); 210 if (icon_) { 211 column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, 212 GridLayout::FIXED, icon_size.width(), 213 icon_size.height()); 214 column_set->AddPaddingColumn(0, kUnrelatedControlHorizontalSpacing); 215 } 216 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, 217 GridLayout::FIXED, message_width_, 0); 218 219 // Column set for prompt Textfield, if one has been set. 220 const int textfield_column_view_set_id = 1; 221 if (prompt_field_) { 222 column_set = layout->AddColumnSet(textfield_column_view_set_id); 223 if (icon_) { 224 column_set->AddPaddingColumn( 225 0, icon_size.width() + kUnrelatedControlHorizontalSpacing); 226 } 227 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, 228 GridLayout::USE_PREF, 0, 0); 229 } 230 231 // Column set for checkbox, if one has been set. 232 const int checkbox_column_view_set_id = 2; 233 if (checkbox_) { 234 column_set = layout->AddColumnSet(checkbox_column_view_set_id); 235 if (icon_) { 236 column_set->AddPaddingColumn( 237 0, icon_size.width() + kUnrelatedControlHorizontalSpacing); 238 } 239 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, 240 GridLayout::USE_PREF, 0, 0); 241 } 242 243 for (size_t i = 0; i < message_labels_.size(); ++i) { 244 layout->StartRow(i, message_column_view_set_id); 245 if (icon_) { 246 if (i == 0) 247 layout->AddView(icon_); 248 else 249 layout->SkipColumns(1); 250 } 251 layout->AddView(message_labels_[i]); 252 } 253 254 if (prompt_field_) { 255 layout->AddPaddingRow(0, inter_row_vertical_spacing_); 256 layout->StartRow(0, textfield_column_view_set_id); 257 layout->AddView(prompt_field_); 258 } 259 260 if (checkbox_) { 261 layout->AddPaddingRow(0, inter_row_vertical_spacing_); 262 layout->StartRow(0, checkbox_column_view_set_id); 263 layout->AddView(checkbox_); 264 } 265 266 layout->AddPaddingRow(0, inter_row_vertical_spacing_); 267} 268 269} // namespace views 270