]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Editor/Common/reference_manager_window.gd
Initial Godot project with Dialogic 2.0-Alpha-17
[wolf-seeking-sheep.git] / addons / dialogic / Editor / Common / reference_manager_window.gd
1 @tool
2 extends Window
3
4 ## This window manages communication with the replacement manager it contains.
5 ## Other scripts can call the add_ref_change() method to register changes directly
6 ##   or use the helpers add_variable_ref_change() and add_portrait_ref_change()
7
8 @onready var editors_manager := get_node("../EditorsManager")
9 @onready var broken_manager := get_node("Manager/Tabs/BrokenReferences")
10 enum Where {EVERYWHERE, BY_CHARACTER, TEXTS_ONLY}
11 enum Types {TEXT, VARIABLE, PORTRAIT, CHARACTER_NAME, TIMELINE_NAME}
12
13 var icon_button: Button = null
14
15
16 func _ready() -> void:
17         if owner.get_parent() is SubViewport:
18                 return
19
20         $Manager.theme = owner.get_theme()
21
22         icon_button = editors_manager.add_icon_button(get_theme_icon("Unlinked", "EditorIcons"), 'Reference Manager')
23         icon_button.pressed.connect(open)
24
25         var dot := Sprite2D.new()
26         dot.texture = get_theme_icon("GuiGraphNodePort", "EditorIcons")
27         dot.scale = Vector2(0.8, 0.8)
28         dot.z_index = 10
29         dot.position = Vector2(icon_button.size.x*0.8, icon_button.size.x*0.2)
30         dot.modulate = get_theme_color("warning_color", "Editor").lightened(0.5)
31
32         icon_button.add_child(dot)
33
34         var old_changes: Array = DialogicUtil.get_editor_setting('reference_changes', [])
35         if !old_changes.is_empty():
36                 broken_manager.reference_changes = old_changes
37
38         update_indicator()
39
40         hide()
41
42         get_parent().plugin_reference.get_editor_interface().get_file_system_dock().files_moved.connect(_on_file_moved)
43         get_parent().plugin_reference.get_editor_interface().get_file_system_dock().file_removed.connect(_on_file_removed)
44         get_parent().get_node('ResourceRenameWarning').confirmed.connect(open)
45
46
47 func add_ref_change(old_name:String, new_name:String, type:Types, where:=Where.TEXTS_ONLY, character_names:=[],
48                                         whole_words:=false, case_sensitive:=false, previous:Dictionary = {}) -> void:
49         var regexes := []
50         var category_name := ""
51         match type:
52                 Types.TEXT:
53                         category_name = "Texts"
54                         if '<replace>' in old_name:
55                                 regexes = [old_name]
56                         else:
57                                 regexes = [
58                                         r'(?<replace>%s)' % old_name.replace('/', '\\/')
59                                         ]
60                                 if !case_sensitive:
61                                         regexes[0] = '(?i)'+regexes[0]
62                                 if whole_words:
63                                         regexes = ['\\b'+regexes[0]+'\\b']
64
65                 Types.VARIABLE:
66                         regexes = [
67                                 r'{(?<replace>\s*%s\s*)}'                       % old_name.replace("/", "\\/"),
68                                 r'var\s*=\s*"(?<replace>\s*%s\s*)"' % old_name.replace("/", "\\/")
69                                 ]
70                         category_name = "Variables"
71
72                 Types.PORTRAIT:
73                         regexes = [
74                                 r'(?m)^[^:(\n]*\((?<replace>%s)\)'                      % old_name.replace('/', '\\/'),
75                                 r'\[\s*portrait\s*=(?<replace>\s*%s\s*)\]'      % old_name.replace('/', '\\/')
76                                 ]
77                         category_name = "Portraits by "+character_names[0]
78
79                 Types.CHARACTER_NAME:
80                         # for reference: ((join|leave|update) )?(?<replace>NAME)(?!\B)(?(1)|(?!([^:\n]|\\:)*(\n|$)))
81                         regexes = [
82                                 r'((join|leave|update) )?(?<replace>%s)(?!\B)(?(1)|(?!([^:\n]|\\:)*(\n|$)))' % old_name
83                                 ]
84                         category_name = "Renamed Character Files"
85
86                 Types.TIMELINE_NAME:
87                         regexes = [
88                                 r'timeline ?= ?" ?(?<replace>%s) ?"' % old_name
89                                 ]
90                         category_name = "Renamed Timeline Files"
91
92         if where != Where.BY_CHARACTER:
93                 character_names = []
94
95         # previous is only given when an existing item is edited
96         # in that case the old one is removed first
97         var idx := len(broken_manager.reference_changes)
98         if previous in broken_manager.reference_changes:
99                 idx = broken_manager.reference_changes.find(previous)
100                 broken_manager.reference_changes.erase(previous)
101
102         if _check_for_ref_change_cycle(old_name, new_name, category_name):
103                 update_indicator()
104                 return
105
106         broken_manager.reference_changes.insert(idx,
107                 {'what':old_name,
108                 'forwhat':new_name,
109                 'regex': regexes,
110                 'regex_replacement':new_name,
111                 'category':category_name,
112                 'character_names':character_names,
113                 'texts_only':where == Where.TEXTS_ONLY,
114                 'type':type
115                 })
116
117         update_indicator()
118
119         if visible:
120                 $Manager.open()
121                 broken_manager.open()
122
123
124 ## Checks for reference cycles or chains.
125 ## E.g. if you first rename a portrait from "happy" to "happy1" and then to "Happy/happy1"
126 ## This will make sure only a change "happy" -> "Happy/happy1" is remembered
127 ## This is very important for correct replacement
128 func _check_for_ref_change_cycle(old_name:String, new_name:String, category:String) -> bool:
129         for ref in broken_manager.reference_changes:
130                 if ref['forwhat'] == old_name and ref['category'] == category:
131                         if new_name == ref['what']:
132                                 broken_manager.reference_changes.erase(ref)
133                         else:
134                                 broken_manager.reference_changes[broken_manager.reference_changes.find(ref)]['forwhat'] = new_name
135                                 broken_manager.reference_changes[broken_manager.reference_changes.find(ref)]['regex_replacement'] = new_name
136                         return true
137         return false
138
139
140 ## Helper for adding variable ref changes
141 func add_variable_ref_change(old_name:String, new_name:String) -> void:
142         add_ref_change(old_name, new_name, Types.VARIABLE, Where.EVERYWHERE)
143
144
145 ## Helper for adding portrait ref changes
146 func add_portrait_ref_change(old_name:String, new_name:String, character_names:PackedStringArray) -> void:
147         add_ref_change(old_name, new_name, Types.PORTRAIT, Where.BY_CHARACTER, character_names)
148
149
150 ## Helper for adding character name ref changes
151 func add_character_name_ref_change(old_name:String, new_name:String) -> void:
152         add_ref_change(old_name, new_name, Types.CHARACTER_NAME, Where.EVERYWHERE)
153
154
155 ## Helper for adding timeline name ref changes
156 func add_timeline_name_ref_change(old_name:String, new_name:String) -> void:
157         add_ref_change(old_name, new_name, Types.TIMELINE_NAME, Where.EVERYWHERE)
158
159
160 func open() -> void:
161         DialogicResourceUtil.update_directory('dch')
162         DialogicResourceUtil.update_directory('dtl')
163         popup_centered_ratio(0.5)
164         move_to_foreground()
165         grab_focus()
166
167
168 func _on_close_requested() -> void:
169         hide()
170         broken_manager.close()
171
172
173 func update_indicator() -> void:
174         icon_button.get_child(0).visible = !broken_manager.reference_changes.is_empty()
175
176
177 ## FILE MANAGEMENT:
178 func _on_file_moved(old_file:String, new_file:String) -> void:
179         if old_file.ends_with('.dch') and new_file.ends_with('.dch'):
180                 DialogicResourceUtil.change_resource_path(old_file, new_file)
181                 if old_file.get_file() != new_file.get_file():
182                         get_parent().get_node('ResourceRenameWarning').popup_centered()
183         elif old_file.ends_with('.dtl') and new_file.ends_with('.dtl'):
184                 DialogicResourceUtil.change_resource_path(old_file, new_file)
185                 if old_file.get_file() != new_file.get_file():
186                         get_parent().get_node('ResourceRenameWarning').popup_centered()
187
188
189 func _on_file_removed(file:String) -> void:
190         if file.get_extension() in ['dch', 'dtl']:
191                 DialogicResourceUtil.remove_resource(file)