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