1 /*************************************************************************/
2 /*  file_dialog.cpp                                                      */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include "file_dialog.h"
31 #include "os/keyboard.h"
32 #include "print_string.h"
33 #include "scene/gui/label.h"
34 
35 FileDialog::GetIconFunc FileDialog::get_icon_func = NULL;
36 FileDialog::GetIconFunc FileDialog::get_large_icon_func = NULL;
37 
38 FileDialog::RegisterFunc FileDialog::register_func = NULL;
39 FileDialog::RegisterFunc FileDialog::unregister_func = NULL;
40 
get_vbox()41 VBoxContainer *FileDialog::get_vbox() {
42 	return vbox;
43 }
44 
_notification(int p_what)45 void FileDialog::_notification(int p_what) {
46 
47 	if (p_what == NOTIFICATION_ENTER_TREE) {
48 
49 		refresh->set_icon(get_icon("reload"));
50 	}
51 
52 	if (p_what == NOTIFICATION_DRAW) {
53 
54 		//RID ci = get_canvas_item();
55 		//get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size()));
56 	}
57 
58 	if (p_what == NOTIFICATION_POPUP_HIDE) {
59 
60 		set_process_unhandled_input(false);
61 	}
62 }
63 
_unhandled_input(const InputEvent & p_event)64 void FileDialog::_unhandled_input(const InputEvent &p_event) {
65 
66 	if (p_event.type == InputEvent::KEY && is_window_modal_on_top()) {
67 
68 		const InputEventKey &k = p_event.key;
69 
70 		if (k.pressed) {
71 
72 			bool handled = true;
73 
74 			switch (k.scancode) {
75 
76 				case KEY_H: {
77 
78 					if (k.mod.command) {
79 						set_show_hidden_files(!show_hidden_files);
80 					} else {
81 						handled = false;
82 					}
83 
84 				} break;
85 				case KEY_F5: {
86 
87 					invalidate();
88 				} break;
89 				default: { handled = false; }
90 			}
91 
92 			if (handled)
93 				accept_event();
94 		}
95 	}
96 }
97 
set_enable_multiple_selection(bool p_enable)98 void FileDialog::set_enable_multiple_selection(bool p_enable) {
99 
100 	tree->set_select_mode(p_enable ? Tree::SELECT_MULTI : Tree::SELECT_SINGLE);
101 };
102 
get_selected_files() const103 Vector<String> FileDialog::get_selected_files() const {
104 
105 	Vector<String> list;
106 
107 	TreeItem *item = tree->get_root();
108 	while ((item = tree->get_next_selected(item))) {
109 
110 		list.push_back(dir_access->get_current_dir().plus_file(item->get_text(0)));
111 	};
112 
113 	return list;
114 };
115 
update_dir()116 void FileDialog::update_dir() {
117 
118 	dir->set_text(dir_access->get_current_dir());
119 }
120 
_dir_entered(String p_dir)121 void FileDialog::_dir_entered(String p_dir) {
122 
123 	dir_access->change_dir(p_dir);
124 	file->set_text("");
125 	invalidate();
126 	update_dir();
127 }
128 
_file_entered(const String & p_file)129 void FileDialog::_file_entered(const String &p_file) {
130 
131 	_action_pressed();
132 }
133 
_save_confirm_pressed()134 void FileDialog::_save_confirm_pressed() {
135 	String f = dir_access->get_current_dir().plus_file(file->get_text());
136 	emit_signal("file_selected", f);
137 	hide();
138 }
139 
_post_popup()140 void FileDialog::_post_popup() {
141 
142 	ConfirmationDialog::_post_popup();
143 	if (invalidated) {
144 		update_file_list();
145 		invalidated = false;
146 	}
147 	if (mode == MODE_SAVE_FILE)
148 		file->grab_focus();
149 	else
150 		tree->grab_focus();
151 
152 	set_process_unhandled_input(true);
153 }
154 
_action_pressed()155 void FileDialog::_action_pressed() {
156 
157 	if (mode == MODE_OPEN_FILES) {
158 
159 		TreeItem *ti = tree->get_next_selected(NULL);
160 		String fbase = dir_access->get_current_dir();
161 
162 		DVector<String> files;
163 		while (ti) {
164 
165 			files.push_back(fbase.plus_file(ti->get_text(0)));
166 			ti = tree->get_next_selected(ti);
167 		}
168 
169 		if (files.size()) {
170 			emit_signal("files_selected", files);
171 			hide();
172 		}
173 
174 		return;
175 	}
176 
177 	String f = dir_access->get_current_dir().plus_file(file->get_text());
178 
179 	if ((mode == MODE_OPEN_ANY || mode == MODE_OPEN_FILE) && dir_access->file_exists(f)) {
180 		emit_signal("file_selected", f);
181 		hide();
182 	} else if (mode == MODE_OPEN_ANY || mode == MODE_OPEN_DIR) {
183 
184 		String path = dir_access->get_current_dir();
185 		/*if (tree->get_selected()) {
186 			Dictionary d = tree->get_selected()->get_metadata(0);
187 			if (d["dir"]) {
188 				path=path+"/"+String(d["name"]);
189 			}
190 		}*/
191 		path = path.replace("\\", "/");
192 		emit_signal("dir_selected", path);
193 		hide();
194 	}
195 
196 	if (mode == MODE_SAVE_FILE) {
197 
198 		bool valid = false;
199 
200 		if (filter->get_selected() == filter->get_item_count() - 1) {
201 			valid = true; //match none
202 		} else if (filters.size() > 1 && filter->get_selected() == 0) {
203 			// match all filters
204 			for (int i = 0; i < filters.size(); i++) {
205 
206 				String flt = filters[i].get_slice(";", 0);
207 				for (int j = 0; j < flt.get_slice_count(","); j++) {
208 
209 					String str = flt.get_slice(",", j).strip_edges();
210 					if (f.match(str)) {
211 						valid = true;
212 						break;
213 					}
214 				}
215 				if (valid)
216 					break;
217 			}
218 		} else {
219 			int idx = filter->get_selected();
220 			if (filters.size() > 1)
221 				idx--;
222 			if (idx >= 0 && idx < filters.size()) {
223 
224 				String flt = filters[idx].get_slice(";", 0);
225 				int filterSliceCount = flt.get_slice_count(",");
226 				for (int j = 0; j < filterSliceCount; j++) {
227 
228 					String str = (flt.get_slice(",", j).strip_edges());
229 					if (f.match(str)) {
230 						valid = true;
231 						break;
232 					}
233 				}
234 
235 				if (!valid && filterSliceCount > 0) {
236 					String str = (flt.get_slice(",", 0).strip_edges());
237 					f += str.substr(1, str.length() - 1);
238 					file->set_text(f.get_file());
239 					valid = true;
240 				}
241 			} else {
242 				valid = true;
243 			}
244 		}
245 
246 		if (!valid) {
247 
248 			exterr->popup_centered_minsize(Size2(250, 80));
249 			return;
250 		}
251 
252 		if (dir_access->file_exists(f)) {
253 			confirm_save->set_text(RTR("File Exists, Overwrite?"));
254 			confirm_save->popup_centered(Size2(200, 80));
255 		} else {
256 
257 			emit_signal("file_selected", f);
258 			hide();
259 		}
260 	}
261 }
262 
_cancel_pressed()263 void FileDialog::_cancel_pressed() {
264 
265 	file->set_text("");
266 	invalidate();
267 	hide();
268 }
269 
_tree_selected()270 void FileDialog::_tree_selected() {
271 
272 	TreeItem *ti = tree->get_selected();
273 	if (!ti)
274 		return;
275 	Dictionary d = ti->get_metadata(0);
276 
277 	if (!d["dir"]) {
278 
279 		file->set_text(d["name"]);
280 	}
281 }
282 
_tree_dc_selected()283 void FileDialog::_tree_dc_selected() {
284 
285 	TreeItem *ti = tree->get_selected();
286 	if (!ti)
287 		return;
288 
289 	Dictionary d = ti->get_metadata(0);
290 
291 	if (d["dir"]) {
292 
293 		dir_access->change_dir(d["name"]);
294 		if (mode == MODE_OPEN_FILE || mode == MODE_OPEN_FILES || mode == MODE_OPEN_DIR || mode == MODE_OPEN_ANY)
295 			file->set_text("");
296 		call_deferred("_update_file_list");
297 		call_deferred("_update_dir");
298 	} else {
299 
300 		_action_pressed();
301 	}
302 }
303 
update_file_list()304 void FileDialog::update_file_list() {
305 
306 	tree->clear();
307 	dir_access->list_dir_begin();
308 
309 	TreeItem *root = tree->create_item();
310 	Ref<Texture> folder = get_icon("folder");
311 	List<String> files;
312 	List<String> dirs;
313 
314 	bool isdir;
315 	bool ishidden;
316 	bool show_hidden = show_hidden_files;
317 	String item;
318 
319 	while ((item = dir_access->get_next(&isdir)) != "") {
320 
321 		ishidden = dir_access->current_is_hidden();
322 
323 		if (show_hidden || !ishidden) {
324 			if (!isdir)
325 				files.push_back(item);
326 			else
327 				dirs.push_back(item);
328 		}
329 	}
330 
331 	if (dirs.find("..") == NULL) {
332 		//may happen if lacking permissions
333 		dirs.push_back("..");
334 	}
335 
336 	dirs.sort_custom<NoCaseComparator>();
337 	files.sort_custom<NoCaseComparator>();
338 
339 	while (!dirs.empty()) {
340 
341 		if (dirs.front()->get() != ".") {
342 			TreeItem *ti = tree->create_item(root);
343 			ti->set_text(0, dirs.front()->get() + "/");
344 			ti->set_icon(0, folder);
345 			Dictionary d;
346 			d["name"] = dirs.front()->get();
347 			d["dir"] = true;
348 			ti->set_metadata(0, d);
349 		}
350 		dirs.pop_front();
351 	}
352 
353 	dirs.clear();
354 
355 	List<String> patterns;
356 	// build filter
357 	if (filter->get_selected() == filter->get_item_count() - 1) {
358 
359 		// match all
360 	} else if (filters.size() > 1 && filter->get_selected() == 0) {
361 		// match all filters
362 		for (int i = 0; i < filters.size(); i++) {
363 
364 			String f = filters[i].get_slice(";", 0);
365 			for (int j = 0; j < f.get_slice_count(","); j++) {
366 
367 				patterns.push_back(f.get_slice(",", j).strip_edges());
368 			}
369 		}
370 	} else {
371 		int idx = filter->get_selected();
372 		if (filters.size() > 1)
373 			idx--;
374 
375 		if (idx >= 0 && idx < filters.size()) {
376 
377 			String f = filters[idx].get_slice(";", 0);
378 			for (int j = 0; j < f.get_slice_count(","); j++) {
379 
380 				patterns.push_back(f.get_slice(",", j).strip_edges());
381 			}
382 		}
383 	}
384 
385 	String base_dir = dir_access->get_current_dir();
386 
387 	while (!files.empty()) {
388 
389 		bool match = patterns.empty();
390 		String match_str;
391 
392 		for (List<String>::Element *E = patterns.front(); E; E = E->next()) {
393 
394 			if (files.front()->get().matchn(E->get())) {
395 				match_str = E->get();
396 				match = true;
397 				break;
398 			}
399 		}
400 
401 		if (match) {
402 			TreeItem *ti = tree->create_item(root);
403 			ti->set_text(0, files.front()->get());
404 
405 			if (get_icon_func) {
406 
407 				Ref<Texture> icon = get_icon_func(base_dir.plus_file(files.front()->get()));
408 				ti->set_icon(0, icon);
409 			}
410 
411 			if (mode == MODE_OPEN_DIR) {
412 				ti->set_custom_color(0, get_color("files_disabled"));
413 				ti->set_selectable(0, false);
414 			}
415 			Dictionary d;
416 			d["name"] = files.front()->get();
417 			d["dir"] = false;
418 			ti->set_metadata(0, d);
419 
420 			if (file->get_text() == files.front()->get() || match_str == files.front()->get())
421 				ti->select(0);
422 		}
423 
424 		files.pop_front();
425 	}
426 
427 	if (tree->get_root() && tree->get_root()->get_children() && tree->get_selected() == NULL)
428 		tree->get_root()->get_children()->select(0);
429 
430 	files.clear();
431 }
432 
_filter_selected(int)433 void FileDialog::_filter_selected(int) {
434 
435 	update_file_list();
436 }
437 
update_filters()438 void FileDialog::update_filters() {
439 
440 	filter->clear();
441 
442 	if (filters.size() > 1) {
443 		String all_filters;
444 
445 		const int max_filters = 5;
446 
447 		for (int i = 0; i < MIN(max_filters, filters.size()); i++) {
448 			String flt = filters[i].get_slice(";", 0);
449 			if (i > 0)
450 				all_filters += ",";
451 			all_filters += flt;
452 		}
453 
454 		if (max_filters < filters.size())
455 			all_filters += ", ...";
456 
457 		filter->add_item(RTR("All Recognized") + " ( " + all_filters + " )");
458 	}
459 	for (int i = 0; i < filters.size(); i++) {
460 
461 		String flt = filters[i].get_slice(";", 0).strip_edges();
462 		String desc = filters[i].get_slice(";", 1).strip_edges();
463 		if (desc.length())
464 			filter->add_item(String(XL_MESSAGE(desc)) + " ( " + flt + " )");
465 		else
466 			filter->add_item("( " + flt + " )");
467 	}
468 
469 	filter->add_item(RTR("All Files (*)"));
470 }
471 
clear_filters()472 void FileDialog::clear_filters() {
473 
474 	filters.clear();
475 	update_filters();
476 	invalidate();
477 }
add_filter(const String & p_filter)478 void FileDialog::add_filter(const String &p_filter) {
479 
480 	filters.push_back(p_filter);
481 	update_filters();
482 	invalidate();
483 }
484 
set_filters(const Vector<String> & p_filters)485 void FileDialog::set_filters(const Vector<String> &p_filters) {
486 	filters = p_filters;
487 	update_filters();
488 	invalidate();
489 }
490 
get_filters() const491 Vector<String> FileDialog::get_filters() const {
492 	return filters;
493 }
494 
get_current_dir() const495 String FileDialog::get_current_dir() const {
496 
497 	return dir->get_text();
498 }
get_current_file() const499 String FileDialog::get_current_file() const {
500 
501 	return file->get_text();
502 }
get_current_path() const503 String FileDialog::get_current_path() const {
504 
505 	return dir->get_text().plus_file(file->get_text());
506 }
set_current_dir(const String & p_dir)507 void FileDialog::set_current_dir(const String &p_dir) {
508 
509 	dir_access->change_dir(p_dir);
510 	update_dir();
511 	invalidate();
512 }
set_current_file(const String & p_file)513 void FileDialog::set_current_file(const String &p_file) {
514 
515 	file->set_text(p_file);
516 	update_dir();
517 	invalidate();
518 	int lp = p_file.find_last(".");
519 	if (lp != -1) {
520 		file->select(0, lp);
521 		file->grab_focus();
522 	}
523 }
set_current_path(const String & p_path)524 void FileDialog::set_current_path(const String &p_path) {
525 
526 	if (!p_path.size())
527 		return;
528 	int pos = MAX(p_path.find_last("/"), p_path.find_last("\\"));
529 	if (pos == -1) {
530 
531 		set_current_file(p_path);
532 	} else {
533 
534 		String dir = p_path.substr(0, pos);
535 		String file = p_path.substr(pos + 1, p_path.length());
536 		set_current_dir(dir);
537 		set_current_file(file);
538 	}
539 }
540 
set_mode(Mode p_mode)541 void FileDialog::set_mode(Mode p_mode) {
542 
543 	mode = p_mode;
544 	switch (mode) {
545 
546 		case MODE_OPEN_FILE:
547 			get_ok()->set_text(RTR("Open"));
548 			set_title(RTR("Open a File"));
549 			makedir->hide();
550 			break;
551 		case MODE_OPEN_FILES:
552 			get_ok()->set_text(RTR("Open"));
553 			set_title(RTR("Open File(s)"));
554 			makedir->hide();
555 			break;
556 		case MODE_OPEN_DIR:
557 			get_ok()->set_text(RTR("Open"));
558 			set_title(RTR("Open a Directory"));
559 			makedir->show();
560 			break;
561 		case MODE_OPEN_ANY:
562 			get_ok()->set_text(RTR("Open"));
563 			set_title(RTR("Open a File or Directory"));
564 			makedir->show();
565 			break;
566 		case MODE_SAVE_FILE:
567 			get_ok()->set_text(RTR("Save"));
568 			set_title(RTR("Save a File"));
569 			makedir->show();
570 			break;
571 	}
572 
573 	if (mode == MODE_OPEN_FILES) {
574 		tree->set_select_mode(Tree::SELECT_MULTI);
575 	} else {
576 		tree->set_select_mode(Tree::SELECT_SINGLE);
577 	}
578 }
579 
get_mode() const580 FileDialog::Mode FileDialog::get_mode() const {
581 
582 	return mode;
583 }
584 
set_access(Access p_access)585 void FileDialog::set_access(Access p_access) {
586 
587 	ERR_FAIL_INDEX(p_access, 3);
588 	if (access == p_access)
589 		return;
590 	memdelete(dir_access);
591 	switch (p_access) {
592 		case ACCESS_FILESYSTEM: {
593 
594 			dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
595 		} break;
596 		case ACCESS_RESOURCES: {
597 
598 			dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
599 		} break;
600 		case ACCESS_USERDATA: {
601 
602 			dir_access = DirAccess::create(DirAccess::ACCESS_USERDATA);
603 		} break;
604 	}
605 	access = p_access;
606 	_update_drives();
607 	invalidate();
608 	update_filters();
609 	update_dir();
610 }
611 
invalidate()612 void FileDialog::invalidate() {
613 
614 	if (is_visible()) {
615 		update_file_list();
616 		invalidated = false;
617 	} else {
618 		invalidated = true;
619 	}
620 }
621 
get_access() const622 FileDialog::Access FileDialog::get_access() const {
623 
624 	return access;
625 }
626 
_make_dir_confirm()627 void FileDialog::_make_dir_confirm() {
628 
629 	Error err = dir_access->make_dir(makedirname->get_text());
630 	if (err == OK) {
631 		dir_access->change_dir(makedirname->get_text());
632 		invalidate();
633 		update_filters();
634 		update_dir();
635 	} else {
636 		mkdirerr->popup_centered_minsize(Size2(250, 50));
637 	}
638 	makedirname->set_text(""); // reset label
639 }
640 
_make_dir()641 void FileDialog::_make_dir() {
642 
643 	makedialog->popup_centered_minsize(Size2(250, 80));
644 	makedirname->grab_focus();
645 }
646 
_select_drive(int p_idx)647 void FileDialog::_select_drive(int p_idx) {
648 
649 	String d = drives->get_item_text(p_idx);
650 	dir_access->change_dir(d);
651 	file->set_text("");
652 	invalidate();
653 	update_dir();
654 }
655 
_update_drives()656 void FileDialog::_update_drives() {
657 
658 	int dc = dir_access->get_drive_count();
659 	if (dc == 0 || access != ACCESS_FILESYSTEM) {
660 		drives->hide();
661 	} else {
662 		drives->clear();
663 		drives->show();
664 
665 		for (int i = 0; i < dir_access->get_drive_count(); i++) {
666 			String d = dir_access->get_drive(i);
667 			drives->add_item(dir_access->get_drive(i));
668 		}
669 
670 		drives->select(dir_access->get_current_drive());
671 	}
672 }
673 
674 bool FileDialog::default_show_hidden_files = false;
675 
_bind_methods()676 void FileDialog::_bind_methods() {
677 
678 	ObjectTypeDB::bind_method(_MD("_unhandled_input"), &FileDialog::_unhandled_input);
679 
680 	ObjectTypeDB::bind_method(_MD("_tree_selected"), &FileDialog::_tree_selected);
681 	ObjectTypeDB::bind_method(_MD("_tree_db_selected"), &FileDialog::_tree_dc_selected);
682 	ObjectTypeDB::bind_method(_MD("_dir_entered"), &FileDialog::_dir_entered);
683 	ObjectTypeDB::bind_method(_MD("_file_entered"), &FileDialog::_file_entered);
684 	ObjectTypeDB::bind_method(_MD("_action_pressed"), &FileDialog::_action_pressed);
685 	ObjectTypeDB::bind_method(_MD("_cancel_pressed"), &FileDialog::_cancel_pressed);
686 	ObjectTypeDB::bind_method(_MD("_filter_selected"), &FileDialog::_filter_selected);
687 	ObjectTypeDB::bind_method(_MD("_save_confirm_pressed"), &FileDialog::_save_confirm_pressed);
688 
689 	ObjectTypeDB::bind_method(_MD("clear_filters"), &FileDialog::clear_filters);
690 	ObjectTypeDB::bind_method(_MD("add_filter", "filter"), &FileDialog::add_filter);
691 	ObjectTypeDB::bind_method(_MD("set_filters", "filters"), &FileDialog::set_filters);
692 	ObjectTypeDB::bind_method(_MD("get_filters"), &FileDialog::get_filters);
693 	ObjectTypeDB::bind_method(_MD("get_current_dir"), &FileDialog::get_current_dir);
694 	ObjectTypeDB::bind_method(_MD("get_current_file"), &FileDialog::get_current_file);
695 	ObjectTypeDB::bind_method(_MD("get_current_path"), &FileDialog::get_current_path);
696 	ObjectTypeDB::bind_method(_MD("set_current_dir", "dir"), &FileDialog::set_current_dir);
697 	ObjectTypeDB::bind_method(_MD("set_current_file", "file"), &FileDialog::set_current_file);
698 	ObjectTypeDB::bind_method(_MD("set_current_path", "path"), &FileDialog::set_current_path);
699 	ObjectTypeDB::bind_method(_MD("set_mode", "mode"), &FileDialog::set_mode);
700 	ObjectTypeDB::bind_method(_MD("get_mode"), &FileDialog::get_mode);
701 	ObjectTypeDB::bind_method(_MD("get_vbox:VBoxContainer"), &FileDialog::get_vbox);
702 	ObjectTypeDB::bind_method(_MD("set_access", "access"), &FileDialog::set_access);
703 	ObjectTypeDB::bind_method(_MD("get_access"), &FileDialog::get_access);
704 	ObjectTypeDB::bind_method(_MD("set_show_hidden_files", "show"), &FileDialog::set_show_hidden_files);
705 	ObjectTypeDB::bind_method(_MD("is_showing_hidden_files"), &FileDialog::is_showing_hidden_files);
706 	ObjectTypeDB::bind_method(_MD("_select_drive"), &FileDialog::_select_drive);
707 	ObjectTypeDB::bind_method(_MD("_make_dir"), &FileDialog::_make_dir);
708 	ObjectTypeDB::bind_method(_MD("_make_dir_confirm"), &FileDialog::_make_dir_confirm);
709 	ObjectTypeDB::bind_method(_MD("_update_file_list"), &FileDialog::update_file_list);
710 	ObjectTypeDB::bind_method(_MD("_update_dir"), &FileDialog::update_dir);
711 
712 	ObjectTypeDB::bind_method(_MD("invalidate"), &FileDialog::invalidate);
713 
714 	ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path")));
715 	ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::STRING_ARRAY, "paths")));
716 	ADD_SIGNAL(MethodInfo("dir_selected", PropertyInfo(Variant::STRING, "dir")));
717 
718 	BIND_CONSTANT(MODE_OPEN_FILE);
719 	BIND_CONSTANT(MODE_OPEN_FILES);
720 	BIND_CONSTANT(MODE_OPEN_DIR);
721 	BIND_CONSTANT(MODE_OPEN_ANY);
722 
723 	BIND_CONSTANT(MODE_SAVE_FILE);
724 
725 	BIND_CONSTANT(ACCESS_RESOURCES);
726 	BIND_CONSTANT(ACCESS_USERDATA);
727 	BIND_CONSTANT(ACCESS_FILESYSTEM);
728 
729 	ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"), _SCS("set_mode"), _SCS("get_mode"));
730 	ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), _SCS("set_access"), _SCS("get_access"));
731 	ADD_PROPERTY(PropertyInfo(Variant::STRING_ARRAY, "filters"), _SCS("set_filters"), _SCS("get_filters"));
732 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), _SCS("set_show_hidden_files"), _SCS("is_showing_hidden_files"));
733 }
734 
set_show_hidden_files(bool p_show)735 void FileDialog::set_show_hidden_files(bool p_show) {
736 	show_hidden_files = p_show;
737 	invalidate();
738 }
739 
is_showing_hidden_files() const740 bool FileDialog::is_showing_hidden_files() const {
741 	return show_hidden_files;
742 }
743 
set_default_show_hidden_files(bool p_show)744 void FileDialog::set_default_show_hidden_files(bool p_show) {
745 	default_show_hidden_files = p_show;
746 }
747 
FileDialog()748 FileDialog::FileDialog() {
749 
750 	show_hidden_files = default_show_hidden_files;
751 
752 	VBoxContainer *vbc = memnew(VBoxContainer);
753 	add_child(vbc);
754 	set_child_rect(vbc);
755 
756 	mode = MODE_SAVE_FILE;
757 	set_title(RTR("Save a File"));
758 
759 	dir = memnew(LineEdit);
760 	HBoxContainer *pathhb = memnew(HBoxContainer);
761 	pathhb->add_child(dir);
762 	dir->set_h_size_flags(SIZE_EXPAND_FILL);
763 
764 	refresh = memnew(ToolButton);
765 	refresh->connect("pressed", this, "_update_file_list");
766 	pathhb->add_child(refresh);
767 
768 	drives = memnew(OptionButton);
769 	pathhb->add_child(drives);
770 	drives->connect("item_selected", this, "_select_drive");
771 
772 	makedir = memnew(Button);
773 	makedir->set_text(RTR("Create Folder"));
774 	makedir->connect("pressed", this, "_make_dir");
775 	pathhb->add_child(makedir);
776 
777 	vbc->add_margin_child(RTR("Path:"), pathhb);
778 
779 	tree = memnew(Tree);
780 	tree->set_hide_root(true);
781 	vbc->add_margin_child(RTR("Directories & Files:"), tree, true);
782 
783 	file = memnew(LineEdit);
784 	//add_child(file);
785 	vbc->add_margin_child(RTR("File:"), file);
786 
787 	filter = memnew(OptionButton);
788 	//add_child(filter);
789 	vbc->add_margin_child(RTR("Filter:"), filter);
790 	filter->set_clip_text(true); //too many extensions overflow it
791 
792 	dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
793 	access = ACCESS_RESOURCES;
794 	_update_drives();
795 
796 	connect("confirmed", this, "_action_pressed");
797 	//cancel->connect("pressed", this,"_cancel_pressed");
798 	tree->connect("cell_selected", this, "_tree_selected", varray(), CONNECT_DEFERRED);
799 	tree->connect("item_activated", this, "_tree_db_selected", varray());
800 	dir->connect("text_entered", this, "_dir_entered");
801 	file->connect("text_entered", this, "_file_entered");
802 	filter->connect("item_selected", this, "_filter_selected");
803 
804 	confirm_save = memnew(ConfirmationDialog);
805 	confirm_save->set_as_toplevel(true);
806 	add_child(confirm_save);
807 
808 	confirm_save->connect("confirmed", this, "_save_confirm_pressed");
809 
810 	makedialog = memnew(ConfirmationDialog);
811 	makedialog->set_title(RTR("Create Folder"));
812 	VBoxContainer *makevb = memnew(VBoxContainer);
813 	makedialog->add_child(makevb);
814 	makedialog->set_child_rect(makevb);
815 	makedirname = memnew(LineEdit);
816 	makevb->add_margin_child(RTR("Name:"), makedirname);
817 	add_child(makedialog);
818 	makedialog->register_text_enter(makedirname);
819 	makedialog->connect("confirmed", this, "_make_dir_confirm");
820 	mkdirerr = memnew(AcceptDialog);
821 	mkdirerr->set_text(RTR("Could not create folder."));
822 	add_child(mkdirerr);
823 
824 	exterr = memnew(AcceptDialog);
825 	exterr->set_text(RTR("Must use a valid extension."));
826 	add_child(exterr);
827 
828 	//update_file_list();
829 	update_filters();
830 	update_dir();
831 
832 	set_hide_on_ok(false);
833 	vbox = vbc;
834 
835 	invalidated = true;
836 	if (register_func)
837 		register_func(this);
838 }
839 
~FileDialog()840 FileDialog::~FileDialog() {
841 
842 	if (unregister_func)
843 		unregister_func(this);
844 	memdelete(dir_access);
845 }
846 
_bind_methods()847 void LineEditFileChooser::_bind_methods() {
848 
849 	ObjectTypeDB::bind_method(_MD("_browse"), &LineEditFileChooser::_browse);
850 	ObjectTypeDB::bind_method(_MD("_chosen"), &LineEditFileChooser::_chosen);
851 	ObjectTypeDB::bind_method(_MD("get_button:Button"), &LineEditFileChooser::get_button);
852 	ObjectTypeDB::bind_method(_MD("get_line_edit:LineEdit"), &LineEditFileChooser::get_line_edit);
853 	ObjectTypeDB::bind_method(_MD("get_file_dialog:FileDialog"), &LineEditFileChooser::get_file_dialog);
854 }
855 
_chosen(const String & p_text)856 void LineEditFileChooser::_chosen(const String &p_text) {
857 
858 	line_edit->set_text(p_text);
859 	line_edit->emit_signal("text_entered", p_text);
860 }
861 
_browse()862 void LineEditFileChooser::_browse() {
863 
864 	dialog->popup_centered_ratio();
865 }
866 
LineEditFileChooser()867 LineEditFileChooser::LineEditFileChooser() {
868 
869 	line_edit = memnew(LineEdit);
870 	add_child(line_edit);
871 	line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
872 	button = memnew(Button);
873 	button->set_text(" .. ");
874 	add_child(button);
875 	button->connect("pressed", this, "_browse");
876 	dialog = memnew(FileDialog);
877 	add_child(dialog);
878 	dialog->connect("file_selected", this, "_chosen");
879 	dialog->connect("dir_selected", this, "_chosen");
880 	dialog->connect("files_selected", this, "_chosen");
881 }
882