1
2
3
4
5
6 using System;
7 using System.Collections;
8 using System.IO;
9
10 namespace System.Screen
11 {
12 public enum FileDialogKind
13 {
14 openDialog, saveDialog
15 }
16
17 public const uchar folderSymbol = cast<uchar>(0x1F5C0);
18 public const uchar emptyDocumentSymbol = cast<uchar>(0x1F5CB);
19
20 public class DirectoryItem
21 {
22 public nothrow DirectoryItem(const DirectoryEntry& entry_, const FileStatus& status_) : entry(entry_), status(status_)
23 {
24 }
25 public DirectoryEntry entry;
26 public FileStatus status;
27 }
28
29 public class DirectoryItemLess : Rel<DirectoryItem>
30 {
31 public nothrow bool operator()(const DirectoryItem& left, const DirectoryItem& right) const
32 {
33 if (left.status.fileType == FileType.directory)
34 {
35 if (right.status.fileType == FileType.directory)
36 {
37 return left.entry.name < right.entry.name;
38 }
39 else
40 {
41 return true;
42 }
43 }
44 else if (left.status.fileType == FileType.regular)
45 {
46 if (right.status.fileType == FileType.directory)
47 {
48 return false;
49 }
50 else if (right.status.fileType == FileType.regular)
51 {
52 return left.entry.name < right.entry.name;
53 }
54 else
55 {
56 return true;
57 }
58 }
59 else if (left.status.fileType == FileType.fifo)
60 {
61 if (right.status.fileType == FileType.directory)
62 {
63 return false;
64 }
65 else if (right.status.fileType == FileType.regular)
66 {
67 return false;
68 }
69 else
70 {
71 return left.entry.name < right.entry.name;
72 }
73 }
74 return false;
75 }
76 }
77
78 public DirectoryItem MakeItem(const string& directory, const DirectoryEntry& entry)
79 {
80 string path = Path.Combine(directory, entry.name);
81 FileStatus status;
82 Stat(path.Chars(), status);
83 return DirectoryItem(entry, status);
84 }
85
86 public class FileDialogCreateParams
87 {
88 public nothrow FileDialogCreateParams() : controlCreateParams(), kind(FileDialogKind.openDialog), caption(), directory(GetCurrentWorkingDirectory()), requireFileExists(false)
89 {
90 }
91 public nothrow FileDialogCreateParams& Defaults()
92 {
93 return *this;
94 }
95 public nothrow FileDialogCreateParams& SetKind(FileDialogKind kind_)
96 {
97 kind = kind_;
98 return *this;
99 }
100 public nothrow FileDialogCreateParams& SetCaption(const string& caption_)
101 {
102 caption = caption_;
103 return *this;
104 }
105 public nothrow FileDialogCreateParams& SetDirectory(const string& directory_)
106 {
107 directory = directory_;
108 return *this;
109 }
110 public nothrow FileDialogCreateParams& SetRequireFileExists(bool requireFileExists_)
111 {
112 requireFileExists = requireFileExists_;
113 return *this;
114 }
115 public ControlCreateParams controlCreateParams;
116 public FileDialogKind kind;
117 public string caption;
118 public string directory;
119 public bool requireFileExists;
120 }
121
122 public class FileDialog : Window
123 {
124 public nothrow FileDialog(FileDialogCreateParams& createParams) :
125 base(createParams.controlCreateParams),
126 kind(createParams.kind),
127 caption(createParams.caption),
128 directory(createParams.directory),
129 requireFileExists(createParams.requireFileExists)
130 {
131 InvalidateGuard guard(this, InvalidateKind.invalidateIfNotDefault);
132 if (caption.IsEmpty())
133 {
134 switch (kind)
135 {
136 case FileDialogKind.openDialog:
137 {
138 caption = "Open File";
139 break;
140 }
141 case FileDialogKind.saveDialog:
142 {
143 caption = "Save File";
144 break;
145 }
146 }
147 }
148 bool setLoc = false;
149 if (Location().IsDefault())
150 {
151 SetLocation(Point(0, 0));
152 setLoc = true;
153 }
154 if (GetSize().IsDefault())
155 {
156 SetSize(Size(60, 20));
157 }
158 if (ForeColor() == ConsoleColor.defaultColor)
159 {
160 SetForeColor(ConsoleColor.black);
161 }
162 if (BackColor() == ConsoleColor.defaultColor)
163 {
164 SetBackColor(ConsoleColor.gray);
165 }
166 Measure(setLoc);
167 Point loc = Location();
168 Size sz = GetSize();
169 Label* label = new Label(LabelCreateParams().SetLocation(Point(loc.x + 2, loc.y + 1)).SetText("Directory:"));
170 AddChild(label);
171 directoryLabel = new Label(LabelCreateParams().SetLocation(Point(loc.x + 2, loc.y + 2)).SetText(directory));
172 AddChild(directoryLabel);
173 listBox = new ListBox(ListBoxCreateParams().SetLocation(Point(loc.x + 2, loc.y + 4)).SetSize(Size(sz.w - 4, 13)));
174 listBox->SelectedIndexChangedEvent().AddHandler(ListBoxSelectedIndexChanged);
175 listBox->ItemSelectedEvent().AddHandler(ListBoxItemSelected);
176 AddChild(listBox);
177 textBox = new TextBox(TextBoxCreateParams().SetLocation(Point(loc.x + 2, loc.y + 18)).SetSize(Size(sz.w - 4, 1)));
178 textBox->TextChangedEvent().AddHandler(TextBoxTextChanged);
179 textBox->TextEnteredEvent().AddHandler(TextBoxTextEntered);
180 AddChild(textBox);
181 string buttonText;
182 switch (kind)
183 {
184 case FileDialogKind.openDialog:
185 {
186 buttonText = "[ &Open ]";
187 break;
188 }
189 case FileDialogKind.saveDialog:
190 {
191 buttonText = "[ &Save ]";
192 break;
193 }
194 }
195 defaultButton = new Button(ButtonCreateParams().Text(buttonText));
196 defaultButton->SetDialogResult(DialogResult.ok);
197 defaultButton->SetDefault();
198 AddChild(defaultButton);
199 Button* cancelButton = Button.Cancel();
200 AddChild(cancelButton);
201 cancelButton->SetLocation(Point(loc.x + sz.w - cancelButton->GetSize().w - 1, loc.y + sz.h - 2));
202 defaultButton->SetLocation(Point(cancelButton->Location().x - defaultButton->GetSize().w - 1, cancelButton->Location().y));
203 MakeListBoxContent();
204 Control* firstFocusableControl = FirstFocusabledControl();
205 if (firstFocusableControl != null)
206 {
207 firstFocusableControl->SetFocus();
208 }
209 SetDefaultButtonStatus();
210 }
211 public string FilePath() const
212 {
213 if (!textBox->Text().IsEmpty())
214 {
215 return Path.Combine(directory, ToUtf8(textBox->Text()));
216 }
217 else if (listBox->SelectedIndex() >= 0 && listBox->SelectedIndex() < listBox->ItemCount())
218 {
219 if (items[listBox->SelectedIndex()].status.fileType == FileType.regular)
220 {
221 return Path.Combine(directory, items[listBox->SelectedIndex()].entry.name);
222 }
223 }
224 return string();
225 }
226 public override void OnWriteScreen(WriteScreenEventArgs& args)
227 {
228 base->OnWriteScreen(args);
229 Rect updateRect = GetRect();
230 bool isDefault = args.GetRect().IsDefault();
231 if (!isDefault)
232 {
233 updateRect = Rect.Intersection(updateRect, args.GetRect());
234 }
235 if (updateRect.IsEmpty()) return;
236 if (isDefault)
237 {
238 WriteBox(updateRect, ForeColor(), BackColor());
239 if (!caption.IsEmpty())
240 {
241 WriteCaption(u" " + ToUtf32(caption) + u" ");
242 }
243 }
244 }
245 private void ListBoxItemSelected()
246 {
247 if (items[listBox->SelectedIndex()].status.fileType == FileType.directory)
248 {
249 InvalidateGuard listBoxGuard(listBox, InvalidateKind.forceInvalidate);
250 InvalidateGuard directoryLabelGuard(directoryLabel, InvalidateKind.forceInvalidate);
251 directory = GetFullPath(Path.Combine(directory, items[listBox->SelectedIndex()].entry.name));
252 directoryLabel->SetText(directory);
253 MakeListBoxContent();
254 }
255 else if (items[listBox->SelectedIndex()].status.fileType == FileType.regular)
256 {
257 SetDefaultButtonStatus();
258 defaultButton->Press();
259 }
260 }
261 private void ListBoxSelectedIndexChanged()
262 {
263 if (requireFileExists)
264 {
265 if (items[listBox->SelectedIndex()].status.fileType == FileType.directory)
266 {
267 defaultButton->SetEnabled();
268 }
269 else if (items[listBox->SelectedIndex()].status.fileType == FileType.regular)
270 {
271 SetDefaultButtonStatus();
272 }
273 else if (items[listBox->SelectedIndex()].status.fileType == FileType.fifo)
274 {
275 defaultButton->SetDisabled();
276 }
277 }
278 else if (items[listBox->SelectedIndex()].status.fileType == FileType.regular)
279 {
280 defaultButton->SetEnabled();
281 }
282 else if (items[listBox->SelectedIndex()].status.fileType == FileType.fifo)
283 {
284 defaultButton->SetDisabled();
285 }
286 }
287 private void TextBoxTextChanged()
288 {
289 if (!textBox->Text().IsEmpty())
290 {
291 SetDefaultButtonStatus();
292 }
293 else
294 {
295 defaultButton->SetDisabled();
296 }
297 }
298 private void TextBoxTextEntered()
299 {
300 if (!textBox->Text().IsEmpty())
301 {
302 SetDefaultButtonStatus();
303 defaultButton->Press();
304 }
305 }
306 private void SetDefaultButtonStatus()
307 {
308 if (requireFileExists)
309 {
310 string filePath = FilePath();
311 if (!filePath.IsEmpty() && File.Exists(filePath))
312 {
313 defaultButton->SetEnabled();
314 }
315 else
316 {
317 defaultButton->SetDisabled();
318 }
319 }
320 else
321 {
322 string filePath = FilePath();
323 if (!filePath.IsEmpty())
324 {
325 defaultButton->SetEnabled();
326 }
327 else
328 {
329 defaultButton->SetDisabled();
330 }
331 }
332 }
333 private void Measure(bool setLoc)
334 {
335 Rect rect = GetRect();
336 rect.Inflate(1, 1);
337 SetLocation(rect.location);
338 SetSize(rect.size);
339 Size sz = GetSize();
340 if (setLoc)
341 {
342 int x = Max(cast<int>(0), (TerminalWindowWidth() - sz.w) / 2);
343 int y = Max(cast<int>(0), (TerminalWindowHeight() - sz.h) / 2);
344 SetLocation(Point(x, y));
345 }
346 }
347 private void WriteCaption(const ustring& captionStr)
348 {
349 Point loc = Location();
350 Size sz = GetSize();
351 int offset = Max(cast<int>(0), cast<int>(sz.w - captionStr.Length()));
352 int x = loc.x + offset / 2;
353 int y = loc.y;
354 SetCursorPos(x, y);
355 Terminal.Out() << captionStr;
356 }
357 private void MakeListBoxContent()
358 {
359 ReadDirectory();
360 SortItems();
361 AddItemsToListBox();
362 }
363 private void AddItemsToListBox()
364 {
365 listBox->Clear();
366 Size sz = listBox->GetSize();
367 int width = sz.w;
368 for (const DirectoryItem& item : items)
369 {
370 ustring itemStr;
371 if (item.status.fileType == FileType.directory)
372 {
373 itemStr.Append(' ').Append(folderSymbol).Append(' ');
374 }
375 else if (item.status.fileType == FileType.regular)
376 {
377 itemStr.Append(' ').Append(emptyDocumentSymbol).Append(' ');
378 }
379 else if (item.status.fileType == FileType.fifo)
380 {
381 itemStr.Append(' ').Append(emptyDocumentSymbol).Append(' ');
382 }
383 ustring entryName = ToUtf32(item.entry.name);
384 int entryWidth = width - 3;
385 itemStr.Append(entryName.Substring(0, entryWidth));
386 int spaceWidth = Max(cast<int>(0), cast<int>(entryWidth - entryName.Length()));
387 if (spaceWidth > 0)
388 {
389 itemStr.Append(ustring(' ', spaceWidth));
390 }
391 listBox->AddItem(itemStr);
392 }
393 listBox->SetSelectedIndex(0);
394 }
395 private void ReadDirectory()
396 {
397 items.Clear();
398 DirectoryReader reader(directory);
399 DirectoryEntry entry;
400 while (reader.Read(entry))
401 {
402 if (entry.IsDot())
403 {
404 continue;
405 }
406 else if (entry.IsDotDot())
407 {
408 if (directory != "/")
409 {
410 items.Add(MakeItem(directory, entry));
411 }
412 }
413 else if (directory == "/" && entry.name == "dev")
414 {
415 continue;
416 }
417 else
418 {
419 items.Add(MakeItem(directory, entry));
420 }
421 }
422 }
423 private void SortItems()
424 {
425 Sort(items.Begin(), items.End(), DirectoryItemLess());
426 }
427 private FileDialogKind kind;
428 private string caption;
429 private string directory;
430 private Label* directoryLabel;
431 private ListBox* listBox;
432 private TextBox* textBox;
433 private Button* defaultButton;
434 private List<DirectoryItem> items;
435 private bool requireFileExists;
436 }
437 }