1 /*************************************************************************/
2 /* project_export.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 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
31 #include "project_export.h"
32
33 #include "core/compressed_translation.h"
34 #include "core/io/image_loader.h"
35 #include "core/io/resource_loader.h"
36 #include "core/io/resource_saver.h"
37 #include "core/os/dir_access.h"
38 #include "core/os/file_access.h"
39 #include "core/os/os.h"
40 #include "core/project_settings.h"
41 #include "editor_data.h"
42 #include "editor_node.h"
43 #include "editor_scale.h"
44 #include "editor_settings.h"
45 #include "scene/gui/box_container.h"
46 #include "scene/gui/margin_container.h"
47 #include "scene/gui/scroll_container.h"
48 #include "scene/gui/tab_container.h"
49
_notification(int p_what)50 void ProjectExportDialog::_notification(int p_what) {
51
52 switch (p_what) {
53 case NOTIFICATION_READY: {
54 duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons"));
55 delete_preset->set_icon(get_icon("Remove", "EditorIcons"));
56 connect("confirmed", this, "_export_pck_zip");
57 custom_feature_display->get_parent_control()->add_style_override("panel", get_stylebox("bg", "Tree"));
58 } break;
59 case NOTIFICATION_POPUP_HIDE: {
60 EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "export", get_rect());
61 } break;
62 case NOTIFICATION_THEME_CHANGED: {
63 duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons"));
64 delete_preset->set_icon(get_icon("Remove", "EditorIcons"));
65 Control *panel = custom_feature_display->get_parent_control();
66 if (panel)
67 panel->add_style_override("panel", get_stylebox("bg", "Tree"));
68 } break;
69 }
70 }
71
popup_export()72 void ProjectExportDialog::popup_export() {
73
74 add_preset->get_popup()->clear();
75 for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) {
76
77 Ref<EditorExportPlatform> plat = EditorExport::get_singleton()->get_export_platform(i);
78
79 add_preset->get_popup()->add_icon_item(plat->get_logo(), plat->get_name());
80 }
81
82 _update_presets();
83 if (presets->get_current() >= 0) {
84 _update_current_preset(); // triggers rescan for templates if newly installed
85 }
86
87 // Restore valid window bounds or pop up at default size.
88 Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "export", Rect2());
89 if (saved_size != Rect2()) {
90 popup(saved_size);
91 } else {
92 popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8);
93 }
94 }
95
_add_preset(int p_platform)96 void ProjectExportDialog::_add_preset(int p_platform) {
97
98 Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_platform(p_platform)->create_preset();
99 ERR_FAIL_COND(!preset.is_valid());
100
101 String name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name();
102 bool make_runnable = true;
103 int attempt = 1;
104 while (true) {
105
106 bool valid = true;
107
108 for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
109 Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
110 if (p->get_platform() == preset->get_platform() && p->is_runnable()) {
111 make_runnable = false;
112 }
113 if (p->get_name() == name) {
114 valid = false;
115 break;
116 }
117 }
118
119 if (valid)
120 break;
121
122 attempt++;
123 name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name() + " " + itos(attempt);
124 }
125
126 preset->set_name(name);
127 if (make_runnable)
128 preset->set_runnable(make_runnable);
129 EditorExport::get_singleton()->add_export_preset(preset);
130 _update_presets();
131 _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
132 }
133
_force_update_current_preset_parameters()134 void ProjectExportDialog::_force_update_current_preset_parameters() {
135 // Force the parameters section to refresh its UI.
136 parameters->edit(nullptr);
137 _update_current_preset();
138 }
139
_update_current_preset()140 void ProjectExportDialog::_update_current_preset() {
141
142 _edit_preset(presets->get_current());
143 }
144
_update_presets()145 void ProjectExportDialog::_update_presets() {
146
147 updating = true;
148
149 Ref<EditorExportPreset> current;
150 if (presets->get_current() >= 0 && presets->get_current() < presets->get_item_count())
151 current = get_current_preset();
152
153 int current_idx = -1;
154 presets->clear();
155 for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
156 Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
157 if (preset == current) {
158 current_idx = i;
159 }
160
161 String name = preset->get_name();
162 if (preset->is_runnable())
163 name += " (" + TTR("Runnable") + ")";
164 preset->update_files_to_export();
165 presets->add_item(name, preset->get_platform()->get_logo());
166 }
167
168 if (current_idx != -1) {
169 presets->select(current_idx);
170 }
171
172 updating = false;
173 }
174
_update_export_all()175 void ProjectExportDialog::_update_export_all() {
176
177 bool can_export = EditorExport::get_singleton()->get_export_preset_count() > 0;
178
179 for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
180 Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
181 bool needs_templates;
182 String error;
183 if (preset->get_export_path() == "" || !preset->get_platform()->can_export(preset, error, needs_templates)) {
184 can_export = false;
185 break;
186 }
187 }
188
189 if (can_export) {
190 export_all_button->set_disabled(false);
191 } else {
192 export_all_button->set_disabled(true);
193 }
194 }
195
_edit_preset(int p_index)196 void ProjectExportDialog::_edit_preset(int p_index) {
197
198 if (p_index < 0 || p_index >= presets->get_item_count()) {
199 name->set_text("");
200 name->set_editable(false);
201 export_path->hide();
202 runnable->set_disabled(true);
203 parameters->edit(NULL);
204 presets->unselect_all();
205 duplicate_preset->set_disabled(true);
206 delete_preset->set_disabled(true);
207 sections->hide();
208 patches->clear();
209 export_error->hide();
210 export_templates_error->hide();
211 return;
212 }
213
214 Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(p_index);
215 ERR_FAIL_COND(current.is_null());
216
217 updating = true;
218
219 presets->select(p_index);
220 sections->show();
221
222 name->set_editable(true);
223 export_path->show();
224 duplicate_preset->set_disabled(false);
225 delete_preset->set_disabled(false);
226 name->set_text(current->get_name());
227
228 List<String> extension_list = current->get_platform()->get_binary_extensions(current);
229 Vector<String> extension_vector;
230 for (int i = 0; i < extension_list.size(); i++) {
231 extension_vector.push_back("*." + extension_list[i]);
232 }
233
234 export_path->setup(extension_vector, false, true);
235 export_path->update_property();
236 runnable->set_disabled(false);
237 runnable->set_pressed(current->is_runnable());
238 parameters->edit(current.ptr());
239
240 export_filter->select(current->get_export_filter());
241 include_filters->set_text(current->get_include_filter());
242 exclude_filters->set_text(current->get_exclude_filter());
243
244 patches->clear();
245 TreeItem *patch_root = patches->create_item();
246 Vector<String> patchlist = current->get_patches();
247 for (int i = 0; i < patchlist.size(); i++) {
248 TreeItem *patch = patches->create_item(patch_root);
249 patch->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
250 String file = patchlist[i].get_file();
251 patch->set_editable(0, true);
252 patch->set_text(0, file.get_file().replace("*", ""));
253 if (file.ends_with("*"))
254 patch->set_checked(0, true);
255 patch->set_tooltip(0, patchlist[i]);
256 patch->set_metadata(0, i);
257 patch->add_button(0, get_icon("Remove", "EditorIcons"), 0);
258 patch->add_button(0, get_icon("folder", "FileDialog"), 1);
259 }
260
261 TreeItem *patch_add = patches->create_item(patch_root);
262 patch_add->set_metadata(0, patchlist.size());
263 if (patchlist.size() == 0)
264 patch_add->set_text(0, TTR("Add initial export..."));
265 else
266 patch_add->set_text(0, TTR("Add previous patches..."));
267
268 patch_add->add_button(0, get_icon("folder", "FileDialog"), 1);
269
270 _fill_resource_tree();
271
272 bool needs_templates;
273 String error;
274 if (!current->get_platform()->can_export(current, error, needs_templates)) {
275
276 if (error != String()) {
277
278 Vector<String> items = error.split("\n", false);
279 error = "";
280 for (int i = 0; i < items.size(); i++) {
281 if (i > 0)
282 error += "\n";
283 error += " - " + items[i];
284 }
285
286 export_error->set_text(error);
287 export_error->show();
288 } else {
289 export_error->hide();
290 }
291 if (needs_templates)
292 export_templates_error->show();
293 else
294 export_templates_error->hide();
295
296 export_button->set_disabled(true);
297 get_ok()->set_disabled(true);
298
299 } else {
300 export_error->hide();
301 export_templates_error->hide();
302 export_button->set_disabled(false);
303 get_ok()->set_disabled(false);
304 }
305
306 custom_features->set_text(current->get_custom_features());
307 _update_feature_list();
308 _update_export_all();
309 minimum_size_changed();
310
311 int script_export_mode = current->get_script_export_mode();
312 script_mode->select(script_export_mode);
313
314 String key = current->get_script_encryption_key();
315 if (!updating_script_key) {
316 script_key->set_text(key);
317 }
318 if (script_export_mode == EditorExportPreset::MODE_SCRIPT_ENCRYPTED) {
319 script_key->set_editable(true);
320
321 bool key_valid = _validate_script_encryption_key(key);
322 if (key_valid) {
323 script_key_error->hide();
324 } else {
325 script_key_error->show();
326 }
327 } else {
328 script_key->set_editable(false);
329 script_key_error->hide();
330 }
331
332 updating = false;
333 }
334
_update_feature_list()335 void ProjectExportDialog::_update_feature_list() {
336
337 Ref<EditorExportPreset> current = get_current_preset();
338 ERR_FAIL_COND(current.is_null());
339
340 Set<String> fset;
341 List<String> features;
342
343 current->get_platform()->get_platform_features(&features);
344 current->get_platform()->get_preset_features(current, &features);
345
346 String custom = current->get_custom_features();
347 Vector<String> custom_list = custom.split(",");
348 for (int i = 0; i < custom_list.size(); i++) {
349 String f = custom_list[i].strip_edges();
350 if (f != String()) {
351 features.push_back(f);
352 }
353 }
354
355 for (List<String>::Element *E = features.front(); E; E = E->next()) {
356 fset.insert(E->get());
357 }
358
359 custom_feature_display->clear();
360 for (Set<String>::Element *E = fset.front(); E; E = E->next()) {
361 String f = E->get();
362 if (E->next()) {
363 f += ", ";
364 }
365 custom_feature_display->add_text(f);
366 }
367 }
368
_custom_features_changed(const String & p_text)369 void ProjectExportDialog::_custom_features_changed(const String &p_text) {
370
371 if (updating)
372 return;
373
374 Ref<EditorExportPreset> current = get_current_preset();
375 ERR_FAIL_COND(current.is_null());
376
377 current->set_custom_features(p_text);
378 _update_feature_list();
379 }
380
_tab_changed(int)381 void ProjectExportDialog::_tab_changed(int) {
382 _update_feature_list();
383 }
384
_patch_button_pressed(Object * p_item,int p_column,int p_id)385 void ProjectExportDialog::_patch_button_pressed(Object *p_item, int p_column, int p_id) {
386
387 TreeItem *ti = (TreeItem *)p_item;
388
389 patch_index = ti->get_metadata(0);
390
391 Ref<EditorExportPreset> current = get_current_preset();
392 ERR_FAIL_COND(current.is_null());
393
394 if (p_id == 0) {
395 Vector<String> patches = current->get_patches();
396 ERR_FAIL_INDEX(patch_index, patches.size());
397 patch_erase->set_text(vformat(TTR("Delete patch '%s' from list?"), patches[patch_index].get_file()));
398 patch_erase->popup_centered_minsize();
399 } else {
400 patch_dialog->popup_centered_ratio();
401 }
402 }
403
_patch_edited()404 void ProjectExportDialog::_patch_edited() {
405
406 TreeItem *item = patches->get_edited();
407 if (!item)
408 return;
409 int index = item->get_metadata(0);
410
411 Ref<EditorExportPreset> current = get_current_preset();
412 ERR_FAIL_COND(current.is_null());
413
414 Vector<String> patches = current->get_patches();
415
416 ERR_FAIL_INDEX(index, patches.size());
417
418 String patch = patches[index].replace("*", "");
419
420 if (item->is_checked(0)) {
421 patch += "*";
422 }
423
424 current->set_patch(index, patch);
425 }
426
_patch_selected(const String & p_path)427 void ProjectExportDialog::_patch_selected(const String &p_path) {
428
429 Ref<EditorExportPreset> current = get_current_preset();
430 ERR_FAIL_COND(current.is_null());
431
432 Vector<String> patches = current->get_patches();
433
434 if (patch_index >= patches.size()) {
435
436 current->add_patch(ProjectSettings::get_singleton()->get_resource_path().path_to(p_path) + "*");
437 } else {
438 String enabled = patches[patch_index].ends_with("*") ? String("*") : String();
439 current->set_patch(patch_index, ProjectSettings::get_singleton()->get_resource_path().path_to(p_path) + enabled);
440 }
441
442 _update_current_preset();
443 }
444
_patch_deleted()445 void ProjectExportDialog::_patch_deleted() {
446
447 Ref<EditorExportPreset> current = get_current_preset();
448 ERR_FAIL_COND(current.is_null());
449
450 Vector<String> patches = current->get_patches();
451 if (patch_index < patches.size()) {
452
453 current->remove_patch(patch_index);
454 _update_current_preset();
455 }
456 }
457
_update_parameters(const String & p_edited_property)458 void ProjectExportDialog::_update_parameters(const String &p_edited_property) {
459
460 _update_current_preset();
461 }
462
_runnable_pressed()463 void ProjectExportDialog::_runnable_pressed() {
464
465 if (updating)
466 return;
467
468 Ref<EditorExportPreset> current = get_current_preset();
469 ERR_FAIL_COND(current.is_null());
470
471 if (runnable->is_pressed()) {
472
473 for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
474 Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
475 if (p->get_platform() == current->get_platform()) {
476 p->set_runnable(current == p);
477 }
478 }
479 } else {
480
481 current->set_runnable(false);
482 }
483
484 _update_presets();
485 }
486
_name_changed(const String & p_string)487 void ProjectExportDialog::_name_changed(const String &p_string) {
488
489 if (updating)
490 return;
491
492 Ref<EditorExportPreset> current = get_current_preset();
493 ERR_FAIL_COND(current.is_null());
494
495 current->set_name(p_string);
496 _update_presets();
497 }
498
set_export_path(const String & p_value)499 void ProjectExportDialog::set_export_path(const String &p_value) {
500 Ref<EditorExportPreset> current = get_current_preset();
501 ERR_FAIL_COND(current.is_null());
502
503 current->set_export_path(p_value);
504 }
505
get_export_path()506 String ProjectExportDialog::get_export_path() {
507 Ref<EditorExportPreset> current = get_current_preset();
508 ERR_FAIL_COND_V(current.is_null(), String(""));
509
510 return current->get_export_path();
511 }
512
get_current_preset() const513 Ref<EditorExportPreset> ProjectExportDialog::get_current_preset() const {
514
515 return EditorExport::get_singleton()->get_export_preset(presets->get_current());
516 }
517
_export_path_changed(const StringName & p_property,const Variant & p_value,const String & p_field,bool p_changing)518 void ProjectExportDialog::_export_path_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {
519
520 if (updating)
521 return;
522
523 Ref<EditorExportPreset> current = get_current_preset();
524 ERR_FAIL_COND(current.is_null());
525
526 current->set_export_path(p_value);
527 _update_presets();
528 }
529
_script_export_mode_changed(int p_mode)530 void ProjectExportDialog::_script_export_mode_changed(int p_mode) {
531
532 if (updating)
533 return;
534
535 Ref<EditorExportPreset> current = get_current_preset();
536 ERR_FAIL_COND(current.is_null());
537
538 current->set_script_export_mode(p_mode);
539
540 _update_current_preset();
541 }
542
_script_encryption_key_changed(const String & p_key)543 void ProjectExportDialog::_script_encryption_key_changed(const String &p_key) {
544
545 if (updating)
546 return;
547
548 Ref<EditorExportPreset> current = get_current_preset();
549 ERR_FAIL_COND(current.is_null());
550
551 current->set_script_encryption_key(p_key);
552
553 updating_script_key = true;
554 _update_current_preset();
555 updating_script_key = false;
556 }
557
_validate_script_encryption_key(const String & p_key)558 bool ProjectExportDialog::_validate_script_encryption_key(const String &p_key) {
559
560 bool is_valid = false;
561
562 if (!p_key.empty() && p_key.is_valid_hex_number(false) && p_key.length() == 64) {
563 is_valid = true;
564 }
565 return is_valid;
566 }
567
_duplicate_preset()568 void ProjectExportDialog::_duplicate_preset() {
569
570 Ref<EditorExportPreset> current = get_current_preset();
571 if (current.is_null())
572 return;
573
574 Ref<EditorExportPreset> preset = current->get_platform()->create_preset();
575 ERR_FAIL_COND(!preset.is_valid());
576
577 String name = current->get_name() + " (copy)";
578 bool make_runnable = true;
579 while (true) {
580
581 bool valid = true;
582
583 for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
584 Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
585 if (p->get_platform() == preset->get_platform() && p->is_runnable()) {
586 make_runnable = false;
587 }
588 if (p->get_name() == name) {
589 valid = false;
590 break;
591 }
592 }
593
594 if (valid)
595 break;
596
597 name += " (copy)";
598 }
599
600 preset->set_name(name);
601 if (make_runnable)
602 preset->set_runnable(make_runnable);
603 preset->set_export_filter(current->get_export_filter());
604 preset->set_include_filter(current->get_include_filter());
605 preset->set_exclude_filter(current->get_exclude_filter());
606 Vector<String> list = current->get_patches();
607 for (int i = 0; i < list.size(); i++) {
608 preset->add_patch(list[i]);
609 }
610 preset->set_custom_features(current->get_custom_features());
611
612 for (const List<PropertyInfo>::Element *E = current->get_properties().front(); E; E = E->next()) {
613 preset->set(E->get().name, current->get(E->get().name));
614 }
615
616 EditorExport::get_singleton()->add_export_preset(preset);
617 _update_presets();
618 _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
619 }
620
_delete_preset()621 void ProjectExportDialog::_delete_preset() {
622
623 Ref<EditorExportPreset> current = get_current_preset();
624 if (current.is_null())
625 return;
626
627 delete_confirm->set_text(vformat(TTR("Delete preset '%s'?"), current->get_name()));
628 delete_confirm->popup_centered_minsize();
629 }
630
_delete_preset_confirm()631 void ProjectExportDialog::_delete_preset_confirm() {
632
633 int idx = presets->get_current();
634 _edit_preset(-1);
635 export_button->set_disabled(true);
636 get_ok()->set_disabled(true);
637 EditorExport::get_singleton()->remove_export_preset(idx);
638 _update_presets();
639 }
640
get_drag_data_fw(const Point2 & p_point,Control * p_from)641 Variant ProjectExportDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
642
643 if (p_from == presets) {
644 int pos = presets->get_item_at_position(p_point, true);
645
646 if (pos >= 0) {
647 Dictionary d;
648 d["type"] = "export_preset";
649 d["preset"] = pos;
650
651 HBoxContainer *drag = memnew(HBoxContainer);
652 TextureRect *tr = memnew(TextureRect);
653 tr->set_texture(presets->get_item_icon(pos));
654 drag->add_child(tr);
655 Label *label = memnew(Label);
656 label->set_text(presets->get_item_text(pos));
657 drag->add_child(label);
658
659 set_drag_preview(drag);
660
661 return d;
662 }
663 } else if (p_from == patches) {
664
665 TreeItem *item = patches->get_item_at_position(p_point);
666
667 if (item && item->get_cell_mode(0) == TreeItem::CELL_MODE_CHECK) {
668
669 int metadata = item->get_metadata(0);
670 Dictionary d;
671 d["type"] = "export_patch";
672 d["patch"] = metadata;
673
674 Label *label = memnew(Label);
675 label->set_text(item->get_text(0));
676 set_drag_preview(label);
677
678 return d;
679 }
680 }
681
682 return Variant();
683 }
684
can_drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from) const685 bool ProjectExportDialog::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
686
687 if (p_from == presets) {
688 Dictionary d = p_data;
689 if (!d.has("type") || String(d["type"]) != "export_preset")
690 return false;
691
692 if (presets->get_item_at_position(p_point, true) < 0 && !presets->is_pos_at_end_of_items(p_point))
693 return false;
694 } else if (p_from == patches) {
695
696 Dictionary d = p_data;
697 if (!d.has("type") || String(d["type"]) != "export_patch")
698 return false;
699
700 patches->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM);
701
702 TreeItem *item = patches->get_item_at_position(p_point);
703
704 if (!item) {
705
706 return false;
707 }
708 }
709
710 return true;
711 }
712
drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from)713 void ProjectExportDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
714
715 if (p_from == presets) {
716 Dictionary d = p_data;
717 int from_pos = d["preset"];
718
719 int to_pos = -1;
720
721 if (presets->get_item_at_position(p_point, true) >= 0) {
722 to_pos = presets->get_item_at_position(p_point, true);
723 }
724
725 if (to_pos == -1 && !presets->is_pos_at_end_of_items(p_point))
726 return;
727
728 if (to_pos == from_pos)
729 return;
730 else if (to_pos > from_pos) {
731 to_pos--;
732 }
733
734 Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(from_pos);
735 EditorExport::get_singleton()->remove_export_preset(from_pos);
736 EditorExport::get_singleton()->add_export_preset(preset, to_pos);
737
738 _update_presets();
739 if (to_pos >= 0)
740 _edit_preset(to_pos);
741 else
742 _edit_preset(presets->get_item_count() - 1);
743 } else if (p_from == patches) {
744
745 Dictionary d = p_data;
746 if (!d.has("type") || String(d["type"]) != "export_patch")
747 return;
748
749 int from_pos = d["patch"];
750
751 TreeItem *item = patches->get_item_at_position(p_point);
752 if (!item)
753 return;
754
755 int to_pos = item->get_cell_mode(0) == TreeItem::CELL_MODE_CHECK ? int(item->get_metadata(0)) : -1;
756
757 if (to_pos == from_pos)
758 return;
759 else if (to_pos > from_pos) {
760 to_pos--;
761 }
762
763 Ref<EditorExportPreset> preset = get_current_preset();
764 String patch = preset->get_patch(from_pos);
765 preset->remove_patch(from_pos);
766 preset->add_patch(patch, to_pos);
767
768 _update_current_preset();
769 }
770 }
771
_export_type_changed(int p_which)772 void ProjectExportDialog::_export_type_changed(int p_which) {
773
774 if (updating)
775 return;
776
777 Ref<EditorExportPreset> current = get_current_preset();
778 if (current.is_null())
779 return;
780
781 current->set_export_filter(EditorExportPreset::ExportFilter(p_which));
782 updating = true;
783 _fill_resource_tree();
784 updating = false;
785 }
786
_filter_changed(const String & p_filter)787 void ProjectExportDialog::_filter_changed(const String &p_filter) {
788
789 if (updating)
790 return;
791
792 Ref<EditorExportPreset> current = get_current_preset();
793 if (current.is_null())
794 return;
795
796 current->set_include_filter(include_filters->get_text());
797 current->set_exclude_filter(exclude_filters->get_text());
798 }
799
_fill_resource_tree()800 void ProjectExportDialog::_fill_resource_tree() {
801
802 include_files->clear();
803 include_label->hide();
804 include_margin->hide();
805
806 Ref<EditorExportPreset> current = get_current_preset();
807 if (current.is_null())
808 return;
809
810 EditorExportPreset::ExportFilter f = current->get_export_filter();
811
812 if (f == EditorExportPreset::EXPORT_ALL_RESOURCES) {
813 return;
814 }
815
816 include_label->show();
817 include_margin->show();
818
819 TreeItem *root = include_files->create_item();
820
821 _fill_tree(EditorFileSystem::get_singleton()->get_filesystem(), root, current, f == EditorExportPreset::EXPORT_SELECTED_SCENES);
822 }
823
_fill_tree(EditorFileSystemDirectory * p_dir,TreeItem * p_item,Ref<EditorExportPreset> & current,bool p_only_scenes)824 bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> ¤t, bool p_only_scenes) {
825
826 p_item->set_icon(0, get_icon("folder", "FileDialog"));
827 p_item->set_text(0, p_dir->get_name() + "/");
828
829 bool used = false;
830 for (int i = 0; i < p_dir->get_subdir_count(); i++) {
831
832 TreeItem *subdir = include_files->create_item(p_item);
833 if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_only_scenes)) {
834 used = true;
835 } else {
836 memdelete(subdir);
837 }
838 }
839
840 for (int i = 0; i < p_dir->get_file_count(); i++) {
841
842 String type = p_dir->get_file_type(i);
843 if (p_only_scenes && type != "PackedScene")
844 continue;
845
846 TreeItem *file = include_files->create_item(p_item);
847 file->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
848 file->set_text(0, p_dir->get_file(i));
849
850 String path = p_dir->get_file_path(i);
851
852 file->set_icon(0, EditorNode::get_singleton()->get_class_icon(type));
853 file->set_editable(0, true);
854 file->set_checked(0, current->has_export_file(path));
855 file->set_metadata(0, path);
856
857 used = true;
858 }
859
860 return used;
861 }
862
_tree_changed()863 void ProjectExportDialog::_tree_changed() {
864
865 if (updating)
866 return;
867
868 Ref<EditorExportPreset> current = get_current_preset();
869 if (current.is_null())
870 return;
871
872 TreeItem *item = include_files->get_edited();
873 if (!item)
874 return;
875
876 String path = item->get_metadata(0);
877 bool added = item->is_checked(0);
878
879 if (added) {
880 current->add_export_file(path);
881 } else {
882 current->remove_export_file(path);
883 }
884 }
885
_export_pck_zip()886 void ProjectExportDialog::_export_pck_zip() {
887
888 export_pck_zip->popup_centered_ratio();
889 }
890
_export_pck_zip_selected(const String & p_path)891 void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) {
892
893 Ref<EditorExportPreset> current = get_current_preset();
894 ERR_FAIL_COND(current.is_null());
895 Ref<EditorExportPlatform> platform = current->get_platform();
896 ERR_FAIL_COND(platform.is_null());
897
898 if (p_path.ends_with(".zip")) {
899 platform->export_zip(current, export_pck_zip_debug->is_pressed(), p_path);
900 } else if (p_path.ends_with(".pck")) {
901 platform->export_pack(current, export_pck_zip_debug->is_pressed(), p_path);
902 }
903 }
904
_open_export_template_manager()905 void ProjectExportDialog::_open_export_template_manager() {
906
907 EditorNode::get_singleton()->open_export_template_manager();
908 hide();
909 }
910
_validate_export_path(const String & p_path)911 void ProjectExportDialog::_validate_export_path(const String &p_path) {
912 // Disable export via OK button or Enter key if LineEdit has an empty filename
913 bool invalid_path = (p_path.get_file().get_basename() == "");
914
915 // Check if state change before needlessly messing with signals
916 if (invalid_path && export_project->get_ok()->is_disabled())
917 return;
918 if (!invalid_path && !export_project->get_ok()->is_disabled())
919 return;
920
921 if (invalid_path) {
922 export_project->get_ok()->set_disabled(true);
923 export_project->get_line_edit()->disconnect("text_entered", export_project, "_file_entered");
924 } else {
925 export_project->get_ok()->set_disabled(false);
926 export_project->get_line_edit()->connect("text_entered", export_project, "_file_entered");
927 }
928 }
929
_export_project()930 void ProjectExportDialog::_export_project() {
931
932 Ref<EditorExportPreset> current = get_current_preset();
933 ERR_FAIL_COND(current.is_null());
934 Ref<EditorExportPlatform> platform = current->get_platform();
935 ERR_FAIL_COND(platform.is_null());
936
937 export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
938 export_project->clear_filters();
939
940 List<String> extension_list = platform->get_binary_extensions(current);
941 for (int i = 0; i < extension_list.size(); i++) {
942 export_project->add_filter("*." + extension_list[i] + " ; " + platform->get_name() + " Export");
943 }
944
945 if (current->get_export_path() != "") {
946 export_project->set_current_path(current->get_export_path());
947 } else {
948 if (extension_list.size() >= 1) {
949 export_project->set_current_file(default_filename + "." + extension_list[0]);
950 } else {
951 export_project->set_current_file(default_filename);
952 }
953 }
954
955 // Ensure that signal is connected if previous attempt left it disconnected with _validate_export_path
956 if (!export_project->get_line_edit()->is_connected("text_entered", export_project, "_file_entered")) {
957 export_project->get_ok()->set_disabled(false);
958 export_project->get_line_edit()->connect("text_entered", export_project, "_file_entered");
959 }
960
961 export_project->set_mode(EditorFileDialog::MODE_SAVE_FILE);
962 export_project->popup_centered_ratio();
963 }
964
_export_project_to_path(const String & p_path)965 void ProjectExportDialog::_export_project_to_path(const String &p_path) {
966 // Save this name for use in future exports (but drop the file extension)
967 default_filename = p_path.get_file().get_basename();
968 EditorSettings::get_singleton()->set_project_metadata("export_options", "default_filename", default_filename);
969
970 Ref<EditorExportPreset> current = get_current_preset();
971 ERR_FAIL_COND(current.is_null());
972 Ref<EditorExportPlatform> platform = current->get_platform();
973 ERR_FAIL_COND(platform.is_null());
974 current->set_export_path(p_path);
975
976 Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0);
977 if (err != OK && err != ERR_SKIP) {
978 if (err == ERR_FILE_NOT_FOUND) {
979 error_dialog->set_text(vformat(TTR("Failed to export the project for platform '%s'.\nExport templates seem to be missing or invalid."), platform->get_name()));
980 } else { // Assume misconfiguration. FIXME: Improve error handling and preset config validation.
981 error_dialog->set_text(vformat(TTR("Failed to export the project for platform '%s'.\nThis might be due to a configuration issue in the export preset or your export settings."), platform->get_name()));
982 }
983
984 ERR_PRINTS(vformat("Failed to export the project for platform '%s'.", platform->get_name()));
985 error_dialog->show();
986 error_dialog->popup_centered_minsize(Size2(300, 80));
987 }
988 }
989
_export_all_dialog()990 void ProjectExportDialog::_export_all_dialog() {
991
992 export_all_dialog->show();
993 export_all_dialog->popup_centered_minsize(Size2(300, 80));
994 }
995
_export_all_dialog_action(const String & p_str)996 void ProjectExportDialog::_export_all_dialog_action(const String &p_str) {
997
998 export_all_dialog->hide();
999
1000 _export_all(p_str != "release");
1001 }
1002
_export_all(bool p_debug)1003 void ProjectExportDialog::_export_all(bool p_debug) {
1004
1005 String mode = p_debug ? TTR("Debug") : TTR("Release");
1006 EditorProgress ep("exportall", TTR("Exporting All") + " " + mode, EditorExport::get_singleton()->get_export_preset_count(), true);
1007
1008 for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
1009 Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
1010 ERR_FAIL_COND(preset.is_null());
1011 Ref<EditorExportPlatform> platform = preset->get_platform();
1012 ERR_FAIL_COND(platform.is_null());
1013
1014 ep.step(preset->get_name(), i);
1015
1016 Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
1017 if (err != OK && err != ERR_SKIP) {
1018 if (err == ERR_FILE_BAD_PATH) {
1019 error_dialog->set_text(TTR("The given export path doesn't exist:") + "\n" + preset->get_export_path().get_base_dir());
1020 } else {
1021 error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted:") + " " + platform->get_name());
1022 }
1023 error_dialog->show();
1024 error_dialog->popup_centered_minsize(Size2(300, 80));
1025 ERR_PRINT("Failed to export project");
1026 }
1027 }
1028 }
1029
_bind_methods()1030 void ProjectExportDialog::_bind_methods() {
1031
1032 ClassDB::bind_method("_add_preset", &ProjectExportDialog::_add_preset);
1033 ClassDB::bind_method("_edit_preset", &ProjectExportDialog::_edit_preset);
1034 ClassDB::bind_method("_update_parameters", &ProjectExportDialog::_update_parameters);
1035 ClassDB::bind_method("_runnable_pressed", &ProjectExportDialog::_runnable_pressed);
1036 ClassDB::bind_method("_name_changed", &ProjectExportDialog::_name_changed);
1037 ClassDB::bind_method("_duplicate_preset", &ProjectExportDialog::_duplicate_preset);
1038 ClassDB::bind_method("_delete_preset", &ProjectExportDialog::_delete_preset);
1039 ClassDB::bind_method("_delete_preset_confirm", &ProjectExportDialog::_delete_preset_confirm);
1040 ClassDB::bind_method("get_drag_data_fw", &ProjectExportDialog::get_drag_data_fw);
1041 ClassDB::bind_method("can_drop_data_fw", &ProjectExportDialog::can_drop_data_fw);
1042 ClassDB::bind_method("drop_data_fw", &ProjectExportDialog::drop_data_fw);
1043 ClassDB::bind_method("_export_type_changed", &ProjectExportDialog::_export_type_changed);
1044 ClassDB::bind_method("_filter_changed", &ProjectExportDialog::_filter_changed);
1045 ClassDB::bind_method("_tree_changed", &ProjectExportDialog::_tree_changed);
1046 ClassDB::bind_method("_patch_button_pressed", &ProjectExportDialog::_patch_button_pressed);
1047 ClassDB::bind_method("_patch_selected", &ProjectExportDialog::_patch_selected);
1048 ClassDB::bind_method("_patch_deleted", &ProjectExportDialog::_patch_deleted);
1049 ClassDB::bind_method("_patch_edited", &ProjectExportDialog::_patch_edited);
1050 ClassDB::bind_method("_export_pck_zip", &ProjectExportDialog::_export_pck_zip);
1051 ClassDB::bind_method("_export_pck_zip_selected", &ProjectExportDialog::_export_pck_zip_selected);
1052 ClassDB::bind_method("_open_export_template_manager", &ProjectExportDialog::_open_export_template_manager);
1053 ClassDB::bind_method("_validate_export_path", &ProjectExportDialog::_validate_export_path);
1054 ClassDB::bind_method("_export_path_changed", &ProjectExportDialog::_export_path_changed);
1055 ClassDB::bind_method("_script_export_mode_changed", &ProjectExportDialog::_script_export_mode_changed);
1056 ClassDB::bind_method("_script_encryption_key_changed", &ProjectExportDialog::_script_encryption_key_changed);
1057 ClassDB::bind_method("_export_project", &ProjectExportDialog::_export_project);
1058 ClassDB::bind_method("_export_project_to_path", &ProjectExportDialog::_export_project_to_path);
1059 ClassDB::bind_method("_export_all", &ProjectExportDialog::_export_all);
1060 ClassDB::bind_method("_export_all_dialog", &ProjectExportDialog::_export_all_dialog);
1061 ClassDB::bind_method("_export_all_dialog_action", &ProjectExportDialog::_export_all_dialog_action);
1062 ClassDB::bind_method("_custom_features_changed", &ProjectExportDialog::_custom_features_changed);
1063 ClassDB::bind_method("_tab_changed", &ProjectExportDialog::_tab_changed);
1064 ClassDB::bind_method("set_export_path", &ProjectExportDialog::set_export_path);
1065 ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path);
1066 ClassDB::bind_method("get_current_preset", &ProjectExportDialog::get_current_preset);
1067 ClassDB::bind_method("_force_update_current_preset_parameters", &ProjectExportDialog::_force_update_current_preset_parameters);
1068
1069 ADD_PROPERTY(PropertyInfo(Variant::STRING, "export_path"), "set_export_path", "get_export_path");
1070 }
1071
ProjectExportDialog()1072 ProjectExportDialog::ProjectExportDialog() {
1073
1074 set_title(TTR("Export"));
1075 set_resizable(true);
1076
1077 VBoxContainer *main_vb = memnew(VBoxContainer);
1078 add_child(main_vb);
1079 HSplitContainer *hbox = memnew(HSplitContainer);
1080 main_vb->add_child(hbox);
1081 hbox->set_v_size_flags(SIZE_EXPAND_FILL);
1082
1083 // Presets list.
1084
1085 VBoxContainer *preset_vb = memnew(VBoxContainer);
1086 preset_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1087 hbox->add_child(preset_vb);
1088
1089 HBoxContainer *preset_hb = memnew(HBoxContainer);
1090 preset_hb->add_child(memnew(Label(TTR("Presets"))));
1091 preset_hb->add_spacer();
1092 preset_vb->add_child(preset_hb);
1093
1094 add_preset = memnew(MenuButton);
1095 add_preset->set_text(TTR("Add..."));
1096 add_preset->get_popup()->connect("index_pressed", this, "_add_preset");
1097 preset_hb->add_child(add_preset);
1098 MarginContainer *mc = memnew(MarginContainer);
1099 preset_vb->add_child(mc);
1100 mc->set_v_size_flags(SIZE_EXPAND_FILL);
1101 presets = memnew(ItemList);
1102 presets->set_drag_forwarding(this);
1103 mc->add_child(presets);
1104 presets->connect("item_selected", this, "_edit_preset");
1105 duplicate_preset = memnew(ToolButton);
1106 preset_hb->add_child(duplicate_preset);
1107 duplicate_preset->connect("pressed", this, "_duplicate_preset");
1108 delete_preset = memnew(ToolButton);
1109 preset_hb->add_child(delete_preset);
1110 delete_preset->connect("pressed", this, "_delete_preset");
1111
1112 // Preset settings.
1113
1114 VBoxContainer *settings_vb = memnew(VBoxContainer);
1115 settings_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1116 hbox->add_child(settings_vb);
1117
1118 name = memnew(LineEdit);
1119 settings_vb->add_margin_child(TTR("Name:"), name);
1120 name->connect("text_changed", this, "_name_changed");
1121 runnable = memnew(CheckButton);
1122 runnable->set_text(TTR("Runnable"));
1123 runnable->set_tooltip(TTR("If checked, the preset will be available for use in one-click deploy.\nOnly one preset per platform may be marked as runnable."));
1124 runnable->connect("pressed", this, "_runnable_pressed");
1125 settings_vb->add_child(runnable);
1126
1127 export_path = memnew(EditorPropertyPath);
1128 settings_vb->add_child(export_path);
1129 export_path->set_label(TTR("Export Path"));
1130 export_path->set_object_and_property(this, "export_path");
1131 export_path->set_save_mode();
1132 export_path->connect("property_changed", this, "_export_path_changed");
1133
1134 // Subsections.
1135
1136 sections = memnew(TabContainer);
1137 sections->set_tab_align(TabContainer::ALIGN_LEFT);
1138 sections->set_use_hidden_tabs_for_min_size(true);
1139 settings_vb->add_child(sections);
1140 sections->set_v_size_flags(SIZE_EXPAND_FILL);
1141
1142 // Main preset parameters.
1143
1144 parameters = memnew(EditorInspector);
1145 sections->add_child(parameters);
1146 parameters->set_name(TTR("Options"));
1147 parameters->set_v_size_flags(SIZE_EXPAND_FILL);
1148 parameters->connect("property_edited", this, "_update_parameters");
1149 EditorExport::get_singleton()->connect("export_presets_updated", this, "_force_update_current_preset_parameters");
1150
1151 // Resources export parameters.
1152
1153 VBoxContainer *resources_vb = memnew(VBoxContainer);
1154 sections->add_child(resources_vb);
1155 resources_vb->set_name(TTR("Resources"));
1156
1157 export_filter = memnew(OptionButton);
1158 export_filter->add_item(TTR("Export all resources in the project"));
1159 export_filter->add_item(TTR("Export selected scenes (and dependencies)"));
1160 export_filter->add_item(TTR("Export selected resources (and dependencies)"));
1161 resources_vb->add_margin_child(TTR("Export Mode:"), export_filter);
1162 export_filter->connect("item_selected", this, "_export_type_changed");
1163
1164 include_label = memnew(Label);
1165 include_label->set_text(TTR("Resources to export:"));
1166 resources_vb->add_child(include_label);
1167 include_margin = memnew(MarginContainer);
1168 include_margin->set_v_size_flags(SIZE_EXPAND_FILL);
1169 resources_vb->add_child(include_margin);
1170
1171 include_files = memnew(Tree);
1172 include_margin->add_child(include_files);
1173 include_files->connect("item_edited", this, "_tree_changed");
1174
1175 include_filters = memnew(LineEdit);
1176 resources_vb->add_margin_child(
1177 TTR("Filters to export non-resource files/folders\n(comma-separated, e.g: *.json, *.txt, docs/*)"),
1178 include_filters);
1179 include_filters->connect("text_changed", this, "_filter_changed");
1180
1181 exclude_filters = memnew(LineEdit);
1182 resources_vb->add_margin_child(
1183 TTR("Filters to exclude files/folders from project\n(comma-separated, e.g: *.json, *.txt, docs/*)"),
1184 exclude_filters);
1185 exclude_filters->connect("text_changed", this, "_filter_changed");
1186
1187 // Patch packages.
1188
1189 VBoxContainer *patch_vb = memnew(VBoxContainer);
1190 sections->add_child(patch_vb);
1191 patch_vb->set_name(TTR("Patches"));
1192
1193 // FIXME: Patching support doesn't seem properly implemented yet, so we hide it.
1194 // The rest of the code is still kept for now, in the hope that it will be made
1195 // functional and reactivated.
1196 patch_vb->hide();
1197
1198 patches = memnew(Tree);
1199 patch_vb->add_child(patches);
1200 patches->set_v_size_flags(SIZE_EXPAND_FILL);
1201 patches->set_hide_root(true);
1202 patches->connect("button_pressed", this, "_patch_button_pressed");
1203 patches->connect("item_edited", this, "_patch_edited");
1204 patches->set_drag_forwarding(this);
1205 patches->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
1206
1207 HBoxContainer *patches_hb = memnew(HBoxContainer);
1208 patch_vb->add_child(patches_hb);
1209 patches_hb->add_spacer();
1210 patch_export = memnew(Button);
1211 patch_export->set_text(TTR("Make Patch"));
1212 patches_hb->add_child(patch_export);
1213 patches_hb->add_spacer();
1214
1215 patch_dialog = memnew(EditorFileDialog);
1216 patch_dialog->add_filter("*.pck ; " + TTR("Pack File"));
1217 patch_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
1218 patch_dialog->connect("file_selected", this, "_patch_selected");
1219 add_child(patch_dialog);
1220
1221 patch_erase = memnew(ConfirmationDialog);
1222 patch_erase->get_ok()->set_text(TTR("Delete"));
1223 patch_erase->connect("confirmed", this, "_patch_deleted");
1224 add_child(patch_erase);
1225
1226 // Feature tags.
1227
1228 VBoxContainer *feature_vb = memnew(VBoxContainer);
1229 feature_vb->set_name(TTR("Features"));
1230 custom_features = memnew(LineEdit);
1231 custom_features->connect("text_changed", this, "_custom_features_changed");
1232 feature_vb->add_margin_child(TTR("Custom (comma-separated):"), custom_features);
1233 custom_feature_display = memnew(RichTextLabel);
1234 custom_feature_display->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1235 feature_vb->add_margin_child(TTR("Feature List:"), custom_feature_display, true);
1236 sections->add_child(feature_vb);
1237
1238 // Script export parameters.
1239
1240 updating_script_key = false;
1241
1242 VBoxContainer *script_vb = memnew(VBoxContainer);
1243 script_vb->set_name(TTR("Script"));
1244 script_mode = memnew(OptionButton);
1245 script_vb->add_margin_child(TTR("Script Export Mode:"), script_mode);
1246 script_mode->add_item(TTR("Text"), (int)EditorExportPreset::MODE_SCRIPT_TEXT);
1247 script_mode->add_item(TTR("Compiled"), (int)EditorExportPreset::MODE_SCRIPT_COMPILED);
1248 script_mode->add_item(TTR("Encrypted (Provide Key Below)"), (int)EditorExportPreset::MODE_SCRIPT_ENCRYPTED);
1249 script_mode->connect("item_selected", this, "_script_export_mode_changed");
1250 script_key = memnew(LineEdit);
1251 script_key->connect("text_changed", this, "_script_encryption_key_changed");
1252 script_key_error = memnew(Label);
1253 script_key_error->set_text("- " + TTR("Invalid Encryption Key (must be 64 characters long)"));
1254 script_key_error->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor"));
1255 script_vb->add_margin_child(TTR("Script Encryption Key (256-bits as hex):"), script_key);
1256 script_vb->add_child(script_key_error);
1257 sections->add_child(script_vb);
1258
1259 sections->connect("tab_changed", this, "_tab_changed");
1260
1261 // Disable by default.
1262 name->set_editable(false);
1263 export_path->hide();
1264 runnable->set_disabled(true);
1265 duplicate_preset->set_disabled(true);
1266 delete_preset->set_disabled(true);
1267 script_key_error->hide();
1268 sections->hide();
1269 parameters->edit(NULL);
1270
1271 // Deletion dialog.
1272
1273 delete_confirm = memnew(ConfirmationDialog);
1274 add_child(delete_confirm);
1275 delete_confirm->get_ok()->set_text(TTR("Delete"));
1276 delete_confirm->connect("confirmed", this, "_delete_preset_confirm");
1277
1278 // Export buttons, dialogs and errors.
1279
1280 updating = false;
1281
1282 get_cancel()->set_text(TTR("Close"));
1283 get_ok()->set_text(TTR("Export PCK/Zip"));
1284 export_button = add_button(TTR("Export Project"), !OS::get_singleton()->get_swap_ok_cancel(), "export");
1285 export_button->connect("pressed", this, "_export_project");
1286 // Disable initially before we select a valid preset
1287 export_button->set_disabled(true);
1288 get_ok()->set_disabled(true);
1289
1290 export_all_dialog = memnew(ConfirmationDialog);
1291 add_child(export_all_dialog);
1292 export_all_dialog->set_title("Export All");
1293 export_all_dialog->set_text(TTR("Export mode?"));
1294 export_all_dialog->get_ok()->hide();
1295 export_all_dialog->add_button(TTR("Debug"), true, "debug");
1296 export_all_dialog->add_button(TTR("Release"), true, "release");
1297 export_all_dialog->connect("custom_action", this, "_export_all_dialog_action");
1298
1299 export_all_button = add_button(TTR("Export All"), !OS::get_singleton()->get_swap_ok_cancel(), "export");
1300 export_all_button->connect("pressed", this, "_export_all_dialog");
1301 export_all_button->set_disabled(true);
1302
1303 export_pck_zip = memnew(EditorFileDialog);
1304 export_pck_zip->add_filter("*.zip ; " + TTR("ZIP File"));
1305 export_pck_zip->add_filter("*.pck ; " + TTR("Godot Game Pack"));
1306 export_pck_zip->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1307 export_pck_zip->set_mode(EditorFileDialog::MODE_SAVE_FILE);
1308 add_child(export_pck_zip);
1309 export_pck_zip->connect("file_selected", this, "_export_pck_zip_selected");
1310
1311 export_error = memnew(Label);
1312 main_vb->add_child(export_error);
1313 export_error->hide();
1314 export_error->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor"));
1315
1316 export_templates_error = memnew(HBoxContainer);
1317 main_vb->add_child(export_templates_error);
1318 export_templates_error->hide();
1319
1320 Label *export_error2 = memnew(Label);
1321 export_templates_error->add_child(export_error2);
1322 export_error2->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor"));
1323 export_error2->set_text(" - " + TTR("Export templates for this platform are missing:") + " ");
1324
1325 error_dialog = memnew(AcceptDialog);
1326 error_dialog->set_title("Error");
1327 error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted:") + " ");
1328 main_vb->add_child(error_dialog);
1329 error_dialog->hide();
1330
1331 LinkButton *download_templates = memnew(LinkButton);
1332 download_templates->set_text(TTR("Manage Export Templates"));
1333 download_templates->set_v_size_flags(SIZE_SHRINK_CENTER);
1334 export_templates_error->add_child(download_templates);
1335 download_templates->connect("pressed", this, "_open_export_template_manager");
1336
1337 export_project = memnew(EditorFileDialog);
1338 export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1339 add_child(export_project);
1340 export_project->connect("file_selected", this, "_export_project_to_path");
1341 export_project->get_line_edit()->connect("text_changed", this, "_validate_export_path");
1342
1343 export_debug = memnew(CheckBox);
1344 export_debug->set_text(TTR("Export With Debug"));
1345 export_debug->set_pressed(true);
1346 export_project->get_vbox()->add_child(export_debug);
1347
1348 export_pck_zip_debug = memnew(CheckBox);
1349 export_pck_zip_debug->set_text(TTR("Export With Debug"));
1350 export_pck_zip_debug->set_pressed(true);
1351 export_pck_zip->get_vbox()->add_child(export_pck_zip_debug);
1352
1353 set_hide_on_ok(false);
1354
1355 editor_icons = "EditorIcons";
1356
1357 default_filename = EditorSettings::get_singleton()->get_project_metadata("export_options", "default_filename", "");
1358 // If no default set, use project name
1359 if (default_filename == "") {
1360 // If no project name defined, use a sane default
1361 default_filename = ProjectSettings::get_singleton()->get("application/config/name");
1362 if (default_filename == "") {
1363 default_filename = "UnnamedProject";
1364 }
1365 }
1366
1367 _update_export_all();
1368 }
1369
~ProjectExportDialog()1370 ProjectExportDialog::~ProjectExportDialog() {
1371 }
1372