1 // Copyright (c) 2011 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/input_window_dialog.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/message_loop.h"
9 #include "base/task.h"
10 #include "grit/generated_resources.h"
11 #include "views/controls/label.h"
12 #include "views/controls/textfield/textfield.h"
13 #include "views/controls/textfield/textfield_controller.h"
14 #include "views/layout/grid_layout.h"
15 #include "views/layout/layout_constants.h"
16 #include "views/window/dialog_delegate.h"
17 #include "views/window/window.h"
18
19 // Width to make the text field, in pixels.
20 static const int kTextfieldWidth = 200;
21
22 class ContentView;
23
24 // The Windows implementation of the cross platform input dialog interface.
25 class WinInputWindowDialog : public InputWindowDialog {
26 public:
27 WinInputWindowDialog(HWND parent,
28 const std::wstring& window_title,
29 const std::wstring& label,
30 const std::wstring& contents,
31 Delegate* delegate);
32 virtual ~WinInputWindowDialog();
33
34 virtual void Show();
35 virtual void Close();
36
window_title() const37 const std::wstring& window_title() const { return window_title_; }
label() const38 const std::wstring& label() const { return label_; }
contents() const39 const std::wstring& contents() const { return contents_; }
40
delegate()41 InputWindowDialog::Delegate* delegate() { return delegate_.get(); }
42
43 private:
44 // Our chrome views window.
45 views::Window* window_;
46
47 // Strings to feed to the on screen window.
48 std::wstring window_title_;
49 std::wstring label_;
50 std::wstring contents_;
51
52 // Our delegate. Consumes the window's output.
53 scoped_ptr<InputWindowDialog::Delegate> delegate_;
54 };
55
56 // ContentView, as the name implies, is the content view for the InputWindow.
57 // It registers accelerators that accept/cancel the input.
58 class ContentView : public views::View,
59 public views::DialogDelegate,
60 public views::TextfieldController {
61 public:
ContentView(WinInputWindowDialog * delegate)62 explicit ContentView(WinInputWindowDialog* delegate)
63 : delegate_(delegate),
64 ALLOW_THIS_IN_INITIALIZER_LIST(focus_grabber_factory_(this)) {
65 DCHECK(delegate_);
66 }
67
68 // views::DialogDelegate:
69 virtual bool IsDialogButtonEnabled(
70 MessageBoxFlags::DialogButton button) const;
71 virtual bool Accept();
72 virtual bool Cancel();
73 virtual void DeleteDelegate();
74 virtual std::wstring GetWindowTitle() const;
IsModal() const75 virtual bool IsModal() const { return true; }
76 virtual views::View* GetContentsView();
77
78 // views::TextfieldController:
79 virtual void ContentsChanged(views::Textfield* sender,
80 const std::wstring& new_contents);
HandleKeyEvent(views::Textfield *,const views::KeyEvent &)81 virtual bool HandleKeyEvent(views::Textfield*,
82 const views::KeyEvent&) {
83 return false;
84 }
85
86 protected:
87 // views::View:
88 virtual void ViewHierarchyChanged(bool is_add,
89 views::View* parent,
90 views::View* child);
91
92 private:
93 // Set up dialog controls and layout.
94 void InitControlLayout();
95
96 // Sets focus to the first focusable element within the dialog.
97 void FocusFirstFocusableControl();
98
99 // The Textfield that the user can type into.
100 views::Textfield* text_field_;
101
102 // The delegate that the ContentView uses to communicate changes to the
103 // caller.
104 WinInputWindowDialog* delegate_;
105
106 // Helps us set focus to the first Textfield in the window.
107 ScopedRunnableMethodFactory<ContentView> focus_grabber_factory_;
108
109 DISALLOW_COPY_AND_ASSIGN(ContentView);
110 };
111
112 ///////////////////////////////////////////////////////////////////////////////
113 // ContentView, views::DialogDelegate implementation:
114
IsDialogButtonEnabled(MessageBoxFlags::DialogButton button) const115 bool ContentView::IsDialogButtonEnabled(
116 MessageBoxFlags::DialogButton button) const {
117 if (button == MessageBoxFlags::DIALOGBUTTON_OK &&
118 !delegate_->delegate()->IsValid(text_field_->text())) {
119 return false;
120 }
121 return true;
122 }
123
Accept()124 bool ContentView::Accept() {
125 delegate_->delegate()->InputAccepted(text_field_->text());
126 return true;
127 }
128
Cancel()129 bool ContentView::Cancel() {
130 delegate_->delegate()->InputCanceled();
131 return true;
132 }
133
DeleteDelegate()134 void ContentView::DeleteDelegate() {
135 delete delegate_;
136 }
137
GetWindowTitle() const138 std::wstring ContentView::GetWindowTitle() const {
139 return delegate_->window_title();
140 }
141
GetContentsView()142 views::View* ContentView::GetContentsView() {
143 return this;
144 }
145
146 ///////////////////////////////////////////////////////////////////////////////
147 // ContentView, views::TextfieldController implementation:
148
ContentsChanged(views::Textfield * sender,const std::wstring & new_contents)149 void ContentView::ContentsChanged(views::Textfield* sender,
150 const std::wstring& new_contents) {
151 GetDialogClientView()->UpdateDialogButtons();
152 }
153
154 ///////////////////////////////////////////////////////////////////////////////
155 // ContentView, protected:
156
ViewHierarchyChanged(bool is_add,views::View * parent,views::View * child)157 void ContentView::ViewHierarchyChanged(bool is_add,
158 views::View* parent,
159 views::View* child) {
160 if (is_add && child == this)
161 InitControlLayout();
162 }
163
164 ///////////////////////////////////////////////////////////////////////////////
165 // ContentView, private:
166
InitControlLayout()167 void ContentView::InitControlLayout() {
168 text_field_ = new views::Textfield;
169 text_field_->SetText(delegate_->contents());
170 text_field_->SetController(this);
171
172 using views::ColumnSet;
173 using views::GridLayout;
174
175 // TODO(sky): Vertical alignment should be baseline.
176 GridLayout* layout = GridLayout::CreatePanel(this);
177 SetLayoutManager(layout);
178
179 ColumnSet* c1 = layout->AddColumnSet(0);
180 c1->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
181 GridLayout::USE_PREF, 0, 0);
182 c1->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
183 c1->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
184 GridLayout::USE_PREF, kTextfieldWidth, kTextfieldWidth);
185
186 layout->StartRow(0, 0);
187 views::Label* label = new views::Label(delegate_->label());
188 layout->AddView(label);
189 layout->AddView(text_field_);
190
191 MessageLoop::current()->PostTask(FROM_HERE,
192 focus_grabber_factory_.NewRunnableMethod(
193 &ContentView::FocusFirstFocusableControl));
194 }
195
FocusFirstFocusableControl()196 void ContentView::FocusFirstFocusableControl() {
197 text_field_->SelectAll();
198 text_field_->RequestFocus();
199 }
200
WinInputWindowDialog(HWND parent,const std::wstring & window_title,const std::wstring & label,const std::wstring & contents,Delegate * delegate)201 WinInputWindowDialog::WinInputWindowDialog(HWND parent,
202 const std::wstring& window_title,
203 const std::wstring& label,
204 const std::wstring& contents,
205 Delegate* delegate)
206 : window_title_(window_title),
207 label_(label),
208 contents_(contents),
209 delegate_(delegate) {
210 window_ = views::Window::CreateChromeWindow(parent, gfx::Rect(),
211 new ContentView(this));
212 window_->client_view()->AsDialogClientView()->UpdateDialogButtons();
213 }
214
~WinInputWindowDialog()215 WinInputWindowDialog::~WinInputWindowDialog() {
216 }
217
Show()218 void WinInputWindowDialog::Show() {
219 window_->Show();
220 }
221
Close()222 void WinInputWindowDialog::Close() {
223 window_->CloseWindow();
224 }
225
226 // static
Create(HWND parent,const std::wstring & window_title,const std::wstring & label,const std::wstring & contents,Delegate * delegate)227 InputWindowDialog* InputWindowDialog::Create(HWND parent,
228 const std::wstring& window_title,
229 const std::wstring& label,
230 const std::wstring& contents,
231 Delegate* delegate) {
232 return new WinInputWindowDialog(parent,
233 window_title,
234 label,
235 contents,
236 delegate);
237 }
238