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.
5 #include "ui/views/controls/message_box_view.h"
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/accessibility/ax_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/label.h"
16 #include "ui/views/controls/link.h"
17 #include "ui/views/controls/scroll_view.h"
18 #include "ui/views/controls/textfield/textfield.h"
19 #include "ui/views/layout/box_layout.h"
20 #include "ui/views/layout/grid_layout.h"
21 #include "ui/views/layout/layout_constants.h"
22 #include "ui/views/widget/widget.h"
23 #include "ui/views/window/client_view.h"
24 #include "ui/views/window/dialog_delegate.h"
28 const int kDefaultMessageWidth
= 320;
30 // Paragraph separators are defined in
31 // http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBidiClass.txt
33 // # Bidi_Class=Paragraph_Separator
35 // 000A ; B # Cc <control-000A>
36 // 000D ; B # Cc <control-000D>
37 // 001C..001E ; B # Cc [3] <control-001C>..<control-001E>
38 // 0085 ; B # Cc <control-0085>
39 // 2029 ; B # Zp PARAGRAPH SEPARATOR
40 bool IsParagraphSeparator(base::char16 c
) {
41 return ( c
== 0x000A || c
== 0x000D || c
== 0x001C || c
== 0x001D ||
42 c
== 0x001E || c
== 0x0085 || c
== 0x2029);
45 // Splits |text| into a vector of paragraphs.
46 // Given an example "\nabc\ndef\n\n\nhij\n", the split results should be:
47 // "", "abc", "def", "", "", "hij", and "".
48 void SplitStringIntoParagraphs(const base::string16
& text
,
49 std::vector
<base::string16
>* paragraphs
) {
53 for (size_t i
= 0; i
< text
.length(); ++i
) {
54 if (IsParagraphSeparator(text
[i
])) {
55 paragraphs
->push_back(text
.substr(start
, i
- start
));
59 paragraphs
->push_back(text
.substr(start
, text
.length() - start
));
66 ///////////////////////////////////////////////////////////////////////////////
67 // MessageBoxView, public:
70 const char MessageBoxView::kViewClassName
[] = "MessageBoxView";
72 MessageBoxView::InitParams::InitParams(const base::string16
& message
)
73 : options(NO_OPTIONS
),
75 message_width(kDefaultMessageWidth
),
76 inter_row_vertical_spacing(kRelatedControlVerticalSpacing
) {}
78 MessageBoxView::InitParams::~InitParams() {
81 MessageBoxView::MessageBoxView(const InitParams
& params
)
82 : prompt_field_(NULL
),
85 message_width_(params
.message_width
) {
89 MessageBoxView::~MessageBoxView() {}
91 base::string16
MessageBoxView::GetInputText() {
92 return prompt_field_
? prompt_field_
->text() : base::string16();
95 bool MessageBoxView::IsCheckBoxSelected() {
96 return checkbox_
? checkbox_
->checked() : false;
99 void MessageBoxView::SetCheckBoxLabel(const base::string16
& label
) {
101 checkbox_
= new Checkbox(label
);
103 checkbox_
->SetText(label
);
104 ResetLayoutManager();
107 void MessageBoxView::SetCheckBoxSelected(bool selected
) {
110 checkbox_
->SetChecked(selected
);
113 void MessageBoxView::SetLink(const base::string16
& text
,
114 LinkListener
* listener
) {
123 link_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
125 link_
->SetText(text
);
126 link_
->set_listener(listener
);
128 ResetLayoutManager();
131 void MessageBoxView::GetAccessibleState(ui::AXViewState
* state
) {
132 state
->role
= ui::AX_ROLE_ALERT
;
135 ///////////////////////////////////////////////////////////////////////////////
136 // MessageBoxView, View overrides:
138 void MessageBoxView::ViewHierarchyChanged(
139 const ViewHierarchyChangedDetails
& details
) {
140 if (details
.child
== this && details
.is_add
) {
142 prompt_field_
->SelectAll(true);
144 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT
, true);
148 bool MessageBoxView::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
149 // We only accepts Ctrl-C.
150 DCHECK(accelerator
.key_code() == 'C' && accelerator
.IsCtrlDown());
152 // We must not intercept Ctrl-C when we have a text box and it's focused.
153 if (prompt_field_
&& prompt_field_
->HasFocus())
156 ui::ScopedClipboardWriter
scw(ui::CLIPBOARD_TYPE_COPY_PASTE
);
157 base::string16 text
= message_labels_
[0]->text();
158 for (size_t i
= 1; i
< message_labels_
.size(); ++i
)
159 text
+= message_labels_
[i
]->text();
164 const char* MessageBoxView::GetClassName() const {
165 return kViewClassName
;
168 ///////////////////////////////////////////////////////////////////////////////
169 // MessageBoxView, private:
171 void MessageBoxView::Init(const InitParams
& params
) {
172 if (params
.options
& DETECT_DIRECTIONALITY
) {
173 std::vector
<base::string16
> texts
;
174 SplitStringIntoParagraphs(params
.message
, &texts
);
175 for (size_t i
= 0; i
< texts
.size(); ++i
) {
176 Label
* message_label
= new Label(texts
[i
]);
177 // Avoid empty multi-line labels, which have a height of 0.
178 message_label
->SetMultiLine(!texts
[i
].empty());
179 message_label
->SetAllowCharacterBreak(true);
180 message_label
->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD
);
181 message_labels_
.push_back(message_label
);
184 Label
* message_label
= new Label(params
.message
);
185 message_label
->SetMultiLine(true);
186 message_label
->SetAllowCharacterBreak(true);
187 message_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
188 message_labels_
.push_back(message_label
);
191 if (params
.options
& HAS_PROMPT_FIELD
) {
192 prompt_field_
= new Textfield
;
193 prompt_field_
->SetText(params
.default_prompt
);
196 inter_row_vertical_spacing_
= params
.inter_row_vertical_spacing
;
198 ResetLayoutManager();
201 void MessageBoxView::ResetLayoutManager() {
202 // Initialize the Grid Layout Manager used for this dialog box.
203 GridLayout
* layout
= GridLayout::CreatePanel(this);
204 SetLayoutManager(layout
);
206 // Add the column set for the message displayed at the top of the dialog box.
207 const int message_column_view_set_id
= 0;
208 ColumnSet
* column_set
= layout
->AddColumnSet(message_column_view_set_id
);
209 column_set
->AddColumn(GridLayout::FILL
, GridLayout::FILL
, 1,
210 GridLayout::FIXED
, message_width_
, 0);
212 // Column set for extra elements, if any.
213 const int extra_column_view_set_id
= 1;
214 if (prompt_field_
|| checkbox_
|| link_
) {
215 column_set
= layout
->AddColumnSet(extra_column_view_set_id
);
216 column_set
->AddColumn(GridLayout::FILL
, GridLayout::FILL
, 1,
217 GridLayout::USE_PREF
, 0, 0);
220 const int kMaxScrollViewHeight
= 600;
221 views::View
* message_contents
= new views::View();
222 message_contents
->SetLayoutManager(
223 new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
224 for (size_t i
= 0; i
< message_labels_
.size(); ++i
)
225 message_contents
->AddChildView(message_labels_
[i
]);
226 ScrollView
* scroll_view
= new views::ScrollView();
227 scroll_view
->ClipHeightTo(0, kMaxScrollViewHeight
);
228 scroll_view
->SetContents(message_contents
);
229 layout
->StartRow(0, message_column_view_set_id
);
230 layout
->AddView(scroll_view
);
233 layout
->AddPaddingRow(0, inter_row_vertical_spacing_
);
234 layout
->StartRow(0, extra_column_view_set_id
);
235 layout
->AddView(prompt_field_
);
239 layout
->AddPaddingRow(0, inter_row_vertical_spacing_
);
240 layout
->StartRow(0, extra_column_view_set_id
);
241 layout
->AddView(checkbox_
);
245 layout
->AddPaddingRow(0, inter_row_vertical_spacing_
);
246 layout
->StartRow(0, extra_column_view_set_id
);
247 layout
->AddView(link_
);