diff --git a/addons/resources_speadsheet_view/import_export/formats_edit/edit_csv.gd b/addons/resources_speadsheet_view/import_export/formats_edit/edit_csv.gd index 34f0fe8..4e11de7 100644 --- a/addons/resources_speadsheet_view/import_export/formats_edit/edit_csv.gd +++ b/addons/resources_speadsheet_view/import_export/formats_edit/edit_csv.gd @@ -21,16 +21,6 @@ func save_entries(all_entries : Array, indices : Array, repeat : bool = true): var file = File.new() var space_after_delimeter = import_data.delimeter.ends_with(" ") file.open(import_data.edited_path, File.WRITE) - if import_data.remove_first_row: - var names = [] - names.resize(import_data.prop_names.size()) - for i in names.size(): - names[i] = TextEditingUtils.string_snake_to_naming_case(import_data.prop_names[i]) - if i != 0 and space_after_delimeter: - names[i] = " " + names[i] - - file.store_csv_line(names, import_data.delimeter[0]) - for x in csv_rows: if space_after_delimeter: for i in x.size(): @@ -41,7 +31,7 @@ func save_entries(all_entries : Array, indices : Array, repeat : bool = true): file.close() if repeat: - timer = editor_view.get_tree().create_timer(5.0) + timer = editor_view.get_tree().create_timer(3.0) timer.connect("timeout", self, "save_entries", [all_entries, indices, false]) @@ -53,38 +43,19 @@ func import_from_path(path : String, insert_func : FuncRef, sort_by : String, so import_data = load(path) var file = File.new() file.open(import_data.edited_path, File.READ) - - var line - var first = true - var space_after_delimeter = import_data.delimeter.ends_with(" ") - csv_rows = [] - while !file.eof_reached(): - line = file.get_csv_line(import_data.delimeter[0]) - if space_after_delimeter: - for i in line.size(): - line[i] = line[i].trim_prefix(" ") - - if first and import_data.remove_first_row: - line = " " - first = false - continue - - if csv_rows.size() == 0: - csv_rows.append(line) - - elif line.size() != 1: - if line.size() != csv_rows[0].size(): - line.resize(csv_rows[0].size()) - - csv_rows.append(line) + csv_rows = SpreadsheetImportFormatCsv.import_as_arrays(import_data) var rows := [] var res : Resource resource_original_positions.clear() for i in csv_rows.size(): + if import_data.remove_first_row and i == 0: + continue + res = import_data.strings_to_resource(csv_rows[i]) insert_func.call_func(res, rows, sort_by, sort_reverse) resource_original_positions[res] = i editor_view.fill_property_data(rows[0]) + file.close() return rows diff --git a/addons/resources_speadsheet_view/import_export/formats_export/export_csv.gd b/addons/resources_speadsheet_view/import_export/formats_export/export_csv.gd new file mode 100644 index 0000000..f77c3e0 --- /dev/null +++ b/addons/resources_speadsheet_view/import_export/formats_export/export_csv.gd @@ -0,0 +1,31 @@ +class_name SpreadsheetExportFormatCsv +extends Reference + + +static func export_to_file(entries_array : Array, column_names : Array, into_path : String, import_data): + var file = File.new() + file.open(into_path, File.WRITE) + + var line = PoolStringArray() + var space_after_delimeter = import_data.delimeter.ends_with(" ") + import_data.prop_names = column_names + import_data.prop_types = import_data.get_resource_property_types(entries_array[0], column_names) + import_data.resource_path = "" + line.resize(column_names.size()) + if import_data.remove_first_row: + for j in column_names.size(): + line[j] = column_names[j] + if space_after_delimeter and j != 0: + line[j] = " " + line[j] + + file.store_csv_line(line, import_data.delimeter[0]) + + for i in entries_array.size(): + for j in column_names.size(): + line[j] = import_data.property_to_string((entries_array[i].get(column_names[j])), j) + if space_after_delimeter and j != 0: + line[j] = " " + line[j] + + file.store_csv_line(line, import_data.delimeter[0]) + + file.close() diff --git a/addons/resources_speadsheet_view/import_export/formats_import/import_csv.gd b/addons/resources_speadsheet_view/import_export/formats_import/import_csv.gd new file mode 100644 index 0000000..62096c4 --- /dev/null +++ b/addons/resources_speadsheet_view/import_export/formats_import/import_csv.gd @@ -0,0 +1,49 @@ +class_name SpreadsheetImportFormatCsv +extends Reference + + +static func can_edit_path(path : String): + return path.ends_with(".csv") + + +static func import_as_arrays(import_data) -> Array: + var file = File.new() + file.open(import_data.edited_path, File.READ) + + import_data.delimeter = ";" + var text_lines := [file.get_line().split(import_data.delimeter)] + var space_after_delimeter = false + var line = text_lines[0] + if line.size() == 1: + import_data.delimeter = "," + line = line[0].split(import_data.delimeter) + text_lines[0] = line + if line[1].begins_with(" "): + for i in line.size(): + line[i] = line[i].trim_prefix(" ") + + text_lines[0] = line + space_after_delimeter = true + import_data.delimeter = ", " + + while !file.eof_reached(): + line = file.get_csv_line(import_data.delimeter[0]) + if space_after_delimeter: + for i in line.size(): + line[i] = line[i].trim_prefix(" ") + + if line.size() == text_lines[0].size(): + text_lines.append(line) + + elif line.size() != 1: + line.resize(text_lines[0].size()) + text_lines.append(line) + + file.close() + var entries = [] + entries.resize(text_lines.size()) + + for i in entries.size(): + entries[i] = text_lines[i] + + return entries diff --git a/addons/resources_speadsheet_view/import_export/import_export_dialog.gd b/addons/resources_speadsheet_view/import_export/import_export_dialog.gd index 421a828..b4cb3a2 100644 --- a/addons/resources_speadsheet_view/import_export/import_export_dialog.gd +++ b/addons/resources_speadsheet_view/import_export/import_export_dialog.gd @@ -2,6 +2,8 @@ tool extends WindowDialog export var prop_list_item_scene : PackedScene +export(Array, Script) var formats_export +export(Array, Script) var formats_import onready var editor_view := $"../.." onready var node_filename_options := $"TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer/OptionButton" @@ -18,62 +20,25 @@ var import_data : SpreadsheetImport func _on_FileDialogText_file_selected(path : String): import_data = SpreadsheetImport.new() import_data.initialize(path) - - _open_dialog() + _reset_controls() + _open_dialog(path) popup_centered() -func _open_dialog(): +func _open_dialog(path : String): node_classname_field.text = TextEditingUtils\ .string_snake_to_naming_case(import_data.edited_path.get_file().get_basename())\ .replace(" ", "") import_data.script_classname = node_classname_field.text - _load_entries() + for x in formats_import: + if x.new().can_edit_path(path): + entries = x.new().import_as_arrays(import_data) + _load_property_names() _create_prop_editors() -func _load_entries(): - var file = File.new() - file.open(import_data.edited_path, File.READ) - - import_data.delimeter = ";" - var text_lines := [file.get_line().split(import_data.delimeter)] - var space_after_delimeter = false - var line = text_lines[0] - if line.size() == 1: - import_data.delimeter = "," - line = line[0].split(import_data.delimeter) - text_lines[0] = line - if line[1].begins_with(" "): - for i in line.size(): - line[i] = line[i].trim_prefix(" ") - - text_lines[0] = line - space_after_delimeter = true - import_data.delimeter = ", " - - while !file.eof_reached(): - line = file.get_csv_line(import_data.delimeter[0]) - if space_after_delimeter: - for i in line.size(): - line[i] = line[i].trim_prefix(" ") - - if line.size() == text_lines[0].size(): - text_lines.append(line) - - elif line.size() != 1: - line.resize(text_lines[0].size()) - text_lines.append(line) - - entries = [] - entries.resize(text_lines.size()) - - for i in entries.size(): - entries[i] = text_lines[i] - - func _load_property_names(): import_data.prop_names = Array(entries[0]) import_data.prop_types.resize(import_data.prop_names.size()) @@ -89,10 +54,8 @@ func _load_property_names(): .replace("\\", "_")\ .to_lower() - if entries[1][i].is_valid_integer(): - import_data.prop_types[i] = SpreadsheetImport.PropType.INT - - elif entries[1][i].is_valid_float(): + # Don't imply Ints automatically - further rows might have floats + if entries[1][i].is_valid_float(): import_data.prop_types[i] = SpreadsheetImport.PropType.REAL elif entries[1][i].begins_with("res://"): @@ -118,29 +81,16 @@ func _create_prop_editors(): func _generate_class(save_script = true): var new_script = GDScript.new() - if import_data.script_classname != "": + if save_script and import_data.script_classname != "": new_script.source_code = "class_name " + import_data.script_classname + " \nextends Resource\n\n" else: new_script.source_code = "extends Resource\n\n" # Enums - var uniques = {} - import_data.uniques = uniques + import_data.uniques = import_data.get_uniques(entries) for i in import_data.prop_types.size(): if import_data.prop_types[i] == SpreadsheetImport.PropType.ENUM: - var cur_value := "" - uniques[i] = {} - for j in entries.size(): - if j == 0 and import_data.remove_first_row: continue - - cur_value = entries[j][i].replace(" ", "_").to_upper() - if cur_value == "": - cur_value = "N_A" - - if !uniques[i].has(cur_value): - uniques[i][cur_value] = uniques[i].size() - new_script.source_code += import_data.create_enum_for_prop(i) # Properties @@ -169,7 +119,7 @@ func _export_tres_folder(): ResourceSaver.save(new_res.resource_path, new_res) -func _on_Ok_pressed(): +func _on_import_to_tres_pressed(): hide() _generate_class() _export_tres_folder() @@ -179,9 +129,10 @@ func _on_Ok_pressed(): editor_view.refresh() -func _on_OkSafe_pressed(): +func _on_import_edit_pressed(): hide() _generate_class(false) + import_data.prop_used_as_filename = "" import_data.save() yield(get_tree(), "idle_frame") editor_view.display_folder(import_data.resource_path) @@ -189,6 +140,19 @@ func _on_OkSafe_pressed(): editor_view.refresh() +func _on_export_csv_pressed(): + hide() + var exported_cols = editor_view.columns.duplicate() + exported_cols.erase("resource_path") + exported_cols.erase("resource_local_to_scene") + for x in editor_view.hidden_columns[editor_view.current_path].keys(): + exported_cols.erase(x) + + SpreadsheetExportFormatCsv.export_to_file(editor_view.rows, exported_cols, import_data.edited_path, import_data) + yield(get_tree(), "idle_frame") + editor_view.refresh() + + # Input controls func _on_classname_field_text_changed(new_text : String): import_data.script_classname = new_text.replace(" ", "") @@ -196,6 +160,8 @@ func _on_classname_field_text_changed(new_text : String): func _on_remove_first_row_toggled(button_pressed : bool): import_data.remove_first_row = button_pressed + $"TabContainer/Export/HBoxContainer2/Button".pressed = true + $"TabContainer/Export/HBoxContainer3/CheckBox".pressed = true func _on_filename_options_item_selected(index): @@ -208,3 +174,20 @@ func _on_list_item_type_selected(type : int, index : int): func _on_list_item_name_changed(name : String, index : int): import_data.prop_names[index] = name.replace(" ", "") + + +func _on_export_delimeter_pressed(del : String): + import_data.delimeter = del + import_data.delimeter.substr(1) + + +func _on_export_space_toggled(button_pressed : bool): + import_data.delimeter = ( + import_data.delimeter[0] + if !button_pressed else + import_data.delimeter + " " + ) + + +func _reset_controls(): + $"TabContainer/Export/HBoxContainer2/CheckBox".pressed = false + _on_remove_first_row_toggled(true) diff --git a/addons/resources_speadsheet_view/import_export/import_export_dialog.tscn b/addons/resources_speadsheet_view/import_export/import_export_dialog.tscn index a60b6b3..87b2504 100644 --- a/addons/resources_speadsheet_view/import_export/import_export_dialog.tscn +++ b/addons/resources_speadsheet_view/import_export/import_export_dialog.tscn @@ -1,21 +1,27 @@ -[gd_scene load_steps=3 format=2] +[gd_scene load_steps=5 format=2] [ext_resource path="res://addons/resources_speadsheet_view/import_export/import_export_dialog.gd" type="Script" id=1] [ext_resource path="res://addons/resources_speadsheet_view/import_export/property_list_item.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/resources_speadsheet_view/import_export/formats_import/import_csv.gd" type="Script" id=3] + +[sub_resource type="ButtonGroup" id=1] [node name="Control" type="WindowDialog"] pause_mode = 2 +visible = true margin_right = 493.0 margin_bottom = 294.0 popup_exclusive = true window_title = "Import/Export As Text" script = ExtResource( 1 ) prop_list_item_scene = ExtResource( 2 ) +formats_import = [ ExtResource( 3 ) ] [node name="TabContainer" type="TabContainer" parent="."] anchor_right = 1.0 anchor_bottom = 1.0 tab_align = 0 +use_hidden_tabs_for_min_size = true [node name="Import" type="VBoxContainer" parent="TabContainer"] anchor_right = 1.0 @@ -112,9 +118,120 @@ margin_right = 434.0 margin_bottom = 20.0 text = "Cancel" +[node name="Export" type="VBoxContainer" parent="TabContainer"] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 4.0 +margin_top = 32.0 +margin_right = -4.0 +margin_bottom = -4.0 + +[node name="Info" type="Label" parent="TabContainer/Export"] +margin_right = 485.0 +margin_bottom = 65.0 +size_flags_vertical = 0 +text = "The currently edited folder will be exported into the selected file. + +Rows hidden by the filter will NOT be exported, and order follows the current sorting key. Rows on non-selected pages will not be removed. + +Hidden columns will NOT be exported." +autowrap = true + +[node name="HSeparator" type="HSeparator" parent="TabContainer/Export"] +margin_top = 69.0 +margin_right = 485.0 +margin_bottom = 73.0 + +[node name="HBoxContainer2" type="HBoxContainer" parent="TabContainer/Export"] +margin_top = 77.0 +margin_right = 485.0 +margin_bottom = 101.0 +alignment = 1 + +[node name="Label2" type="Label" parent="TabContainer/Export/HBoxContainer2"] +margin_top = 5.0 +margin_right = 131.0 +margin_bottom = 19.0 +size_flags_horizontal = 3 +text = "Delimeter:" + +[node name="Button" type="Button" parent="TabContainer/Export/HBoxContainer2"] +margin_left = 135.0 +margin_right = 212.0 +margin_bottom = 24.0 +toggle_mode = true +pressed = true +group = SubResource( 1 ) +text = "Comma (,)" + +[node name="Button2" type="Button" parent="TabContainer/Export/HBoxContainer2"] +margin_left = 216.0 +margin_right = 311.0 +margin_bottom = 24.0 +toggle_mode = true +group = SubResource( 1 ) +text = "Semicolon (;)" + +[node name="Button3" type="Button" parent="TabContainer/Export/HBoxContainer2"] +margin_left = 315.0 +margin_right = 349.0 +margin_bottom = 24.0 +toggle_mode = true +group = SubResource( 1 ) +text = "Tab" + +[node name="CheckBox" type="CheckBox" parent="TabContainer/Export/HBoxContainer2"] +margin_left = 353.0 +margin_right = 485.0 +margin_bottom = 24.0 +text = "With space after" + +[node name="HBoxContainer3" type="HBoxContainer" parent="TabContainer/Export"] +margin_top = 105.0 +margin_right = 485.0 +margin_bottom = 129.0 + +[node name="CheckBox" type="CheckBox" parent="TabContainer/Export/HBoxContainer3"] +margin_right = 246.0 +margin_bottom = 24.0 +pressed = true +text = "First row contains property names (CSV)" + +[node name="Control" type="Control" parent="TabContainer/Export"] +margin_top = 133.0 +margin_right = 485.0 +margin_bottom = 234.0 +size_flags_vertical = 3 + +[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Export"] +margin_top = 238.0 +margin_right = 485.0 +margin_bottom = 258.0 +alignment = 1 + +[node name="Button" type="Button" parent="TabContainer/Export/HBoxContainer"] +margin_left = 165.0 +margin_right = 261.0 +margin_bottom = 20.0 +text = "Export to CSV" + +[node name="Cancel" type="Button" parent="TabContainer/Export/HBoxContainer"] +margin_left = 265.0 +margin_right = 319.0 +margin_bottom = 20.0 +text = "Cancel" + [connection signal="item_selected" from="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer/OptionButton" to="." method="_on_filename_options_item_selected"] [connection signal="text_changed" from="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer/LineEdit" to="." method="_on_classname_field_text_changed"] [connection signal="toggled" from="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer/CheckBox" to="." method="_on_remove_first_row_toggled"] -[connection signal="pressed" from="TabContainer/Import/HBoxContainer/Ok2" to="." method="_on_OkSafe_pressed"] -[connection signal="pressed" from="TabContainer/Import/HBoxContainer/Ok" to="." method="_on_Ok_pressed"] +[connection signal="pressed" from="TabContainer/Import/HBoxContainer/Ok2" to="." method="_on_import_edit_pressed"] +[connection signal="pressed" from="TabContainer/Import/HBoxContainer/Ok" to="." method="_on_import_to_tres_pressed"] [connection signal="pressed" from="TabContainer/Import/HBoxContainer/Cancel" to="." method="hide"] +[connection signal="pressed" from="TabContainer/Export/HBoxContainer2/Button" to="." method="_on_export_delimeter_pressed" binds= [ "," ]] +[connection signal="pressed" from="TabContainer/Export/HBoxContainer2/Button2" to="." method="_on_export_delimeter_pressed" binds= [ ";" ]] +[connection signal="pressed" from="TabContainer/Export/HBoxContainer2/Button3" to="." method="_on_export_delimeter_pressed" binds= [ " " ]] +[connection signal="toggled" from="TabContainer/Export/HBoxContainer2/CheckBox" to="." method="_on_export_space_toggled"] +[connection signal="toggled" from="TabContainer/Export/HBoxContainer3/CheckBox" to="." method="_on_remove_first_row_toggled"] +[connection signal="pressed" from="TabContainer/Export/HBoxContainer/Button" to="." method="_on_export_csv_pressed"] +[connection signal="pressed" from="TabContainer/Export/HBoxContainer/Cancel" to="." method="hide"] diff --git a/addons/resources_speadsheet_view/import_export/spreadsheet_import.gd b/addons/resources_speadsheet_view/import_export/spreadsheet_import.gd index e1fabc6..c8b27ef 100644 --- a/addons/resources_speadsheet_view/import_export/spreadsheet_import.gd +++ b/addons/resources_speadsheet_view/import_export/spreadsheet_import.gd @@ -2,8 +2,6 @@ tool class_name SpreadsheetImport extends Resource -const SUFFIX := "_spreadsheet_import.tres" - enum PropType { BOOL, INT, @@ -19,6 +17,16 @@ enum PropType { MAX, } +const SUFFIX := "_spreadsheet_import.tres" +const TYPE_MAP := { + TYPE_STRING : PropType.STRING, + TYPE_REAL : PropType.REAL, + TYPE_BOOL : PropType.BOOL, + TYPE_INT : PropType.INT, + TYPE_OBJECT : PropType.OBJECT, + TYPE_COLOR : PropType.COLOR, +} + export var prop_types : Array export var prop_names : Array @@ -75,6 +83,11 @@ func string_to_property(string : String, col_index : int): func property_to_string(value, col_index : int) -> String: + if prop_types[col_index] is PoolStringArray: + return TextEditingUtils.string_snake_to_naming_case( + prop_types[col_index][value] + ) + match prop_types[col_index]: PropType.STRING: return value @@ -185,3 +198,44 @@ func resource_to_strings(res : Resource): strings[i] = property_to_string(res.get(prop_names[i]), i) return PoolStringArray(strings) + + +func get_uniques(entries : Array) -> Dictionary: + var result = {} + for i in prop_types.size(): + if prop_types[i] == PropType.ENUM: + var cur_value := "" + result[i] = {} + for j in entries.size(): + if j == 0 and remove_first_row: continue + + cur_value = entries[j][i].replace(" ", "_").to_upper() + if cur_value == "": + cur_value = "N_A" + + if !result[i].has(cur_value): + result[i][cur_value] = result[i].size() + + return result + + +static func get_resource_property_types(res : Resource, properties : Array) -> Array: + var result = [] + result.resize(properties.size()) + result.fill(PropType.STRING) + var cur_type := 0 + for x in res.get_property_list(): + var found = properties.find(x["name"]) + if found == -1: continue + if x["usage"] & PROPERTY_USAGE_EDITOR != 0: + if x["hint"] == PROPERTY_HINT_ENUM: + var enum_values = x["hint_string"].split(",") + for i in enum_values.size(): + enum_values[i] = enum_values[i].left(enum_values[i].find(":")) + + result[found] = enum_values + + else: + result[found] = TYPE_MAP.get(x["type"], PropType.STRING) + + return result