Files
Kalulu/sources/language_tool/word_list_element.gd
T
2025-09-05 15:55:48 +02:00

376 lines
13 KiB
GDScript

class_name WordListElement
extends MarginContainer
signal delete_pressed()
signal new_gp_asked(i: int)
signal validated()
signal gps_updated()
const GP_LIST_BUTTON_SCENE: PackedScene = preload("res://sources/language_tool/gp_list_button.tscn")
const PLUS_BUTTON_SCENE: PackedScene = preload("res://sources/language_tool/plus_button.tscn")
@export var table: String = "Words"
@export var table_graph_column: String = "Word"
@export var sub_table: String = "GPs"
@export var sub_table_graph_column: String = "Grapheme"
@export var sub_table_phon_column: String = "Phoneme"
@export var relational_table: String = "GPsInWords"
@export var sub_table_id: String = "GPID"
var word: String = "":
set = set_word
var lesson: int = 0:
set = set_lesson
var undo_redo: UndoRedo:
get:
if not undo_redo:
undo_redo = UndoRedo.new()
return undo_redo
var id: int = -1
var gp_ids: Array[int] = []:
set = set_gp_ids
var unvalidated_gp_ids: Array[int] = []
var exception: int = 0:
set = set_exception
var reading: int = 0:
set = set_reading
var writing: int = 0:
set = set_writing
var sub_elements_list: Dictionary = {}
@onready var word_label: Label = %Word
@onready var graphemes_label: Label = %Graphemes
@onready var word_edit: LineEdit = %WordEdit
@onready var tab_container: TabContainer = $TabContainer
@onready var lesson_label: Label = %Lesson
@onready var exception_checkbox: CheckBox = %ExceptionCheckBox
@onready var exception_edit_checkbox: CheckBox = %ExceptionEditCheckBox
@onready var reading_checkbox: CheckBox = %ReadingCheckBox
@onready var writing_checkbox: CheckBox = %WritingCheckBox
@onready var reading_edit_checkbox: CheckBox = %ReadingEditCheckBox
@onready var writing_edit_checkbox: CheckBox = %WritingEditCheckBox
@onready var graphemes_edit_container: HBoxContainer = %GraphemesEditContainer
@onready var add_gp_button: MarginContainer = %AddGPButton
@onready var remove_gp_button: MarginContainer = %RemoveGPButton2
func set_exception(p_exception: bool) -> void:
exception = p_exception
if exception_checkbox:
exception_checkbox.button_pressed = bool(exception)
if exception_edit_checkbox:
exception_edit_checkbox.button_pressed = bool(exception)
func set_reading(p_reading: bool) -> void:
reading = p_reading
if reading_checkbox:
reading_checkbox.button_pressed = bool(reading)
if reading_edit_checkbox:
reading_edit_checkbox.button_pressed = bool(reading)
func set_writing(p_writing: bool) -> void:
writing = p_writing
if writing_checkbox:
writing_checkbox.button_pressed = bool(writing)
if writing_edit_checkbox:
writing_edit_checkbox.button_pressed = bool(writing)
func set_word(p_word: String) -> void:
word = p_word
if word_label:
word_label.text = word
if word_edit:
word_edit.text = word
func set_gp_ids(p_gp_ids: Array[int]) -> void:
gp_ids = p_gp_ids
if graphemes_label:
graphemes_label.text = get_gps(gp_ids)
func set_graphemes_edit(p_gp_ids: Array[int]) -> void:
if graphemes_edit_container:
for child: Node in graphemes_edit_container.get_children():
graphemes_edit_container.remove_child(child)
child.queue_free()
for ind_gp_id: int in range(p_gp_ids.size()):
var gp_id: int = p_gp_ids[ind_gp_id]
add_gp_list_button(gp_id, ind_gp_id)
func add_gp_list_button(gp_id: int, ind_gp_id: int) -> void:
var gp_list_button: GPListButton = GP_LIST_BUTTON_SCENE.instantiate()
gp_list_button.set_gp_list(sub_elements_list)
graphemes_edit_container.add_child(gp_list_button)
graphemes_edit_container.move_child(gp_list_button, 2 * ind_gp_id)
gp_list_button.select_id(gp_id)
gp_list_button.gp_selected.connect(_on_gp_list_button_selected.bind(gp_list_button))
gp_list_button.new_selected.connect(_on_gp_list_button_new_selected.bind(gp_list_button))
var plus_button: PlusButton = PLUS_BUTTON_SCENE.instantiate()
plus_button.size = Vector2(50, 50)
graphemes_edit_container.add_child(plus_button)
graphemes_edit_container.move_child(plus_button, 2 * ind_gp_id + 1)
plus_button.pressed.connect(_on_add_gp_button_pressed.bind(gp_list_button))
func _on_gp_list_button_selected(gp_id: int, element: Node) -> void:
var ind_gp_id: int = int(element.get_index() / 2.0)
unvalidated_gp_ids[ind_gp_id] = gp_id
word_edit.text = get_graphemes(unvalidated_gp_ids)
func _on_gp_list_button_new_selected(element: Node) -> void:
var ind_gp_id: int = int(element.get_index() / 2.0)
new_gp_asked.emit(ind_gp_id)
func get_graphemes(p_gp_ids: Array[int]) -> String:
var res: String = ""
for gp_id: int in p_gp_ids:
res += sub_elements_list[gp_id].grapheme
return res
func get_gps(p_gp_ids: Array[int]) -> String:
var res: String = ""
for gp_id: int in p_gp_ids:
res += sub_elements_list[gp_id].grapheme
res += "-"
res += sub_elements_list[gp_id].phoneme
res += " "
return res
func set_lesson(p_lesson: int) -> void:
lesson = p_lesson
if lesson_label:
lesson_label.text = str(p_lesson)
func _ready() -> void:
set_word(word)
set_gp_ids(gp_ids)
set_lesson(lesson)
set_exception(exception)
set_reading(reading)
set_writing(writing)
(%WordEditLabel as Label).text = table_graph_column + ": "
(%GPsEditLabel as Label).text = sub_table + ": "
func _on_edit_button_pressed() -> void:
edit_mode()
func edit_mode() -> void:
set_graphemes_edit(gp_ids)
tab_container.current_tab = 1
func _on_minus_button_pressed() -> void:
delete_pressed.emit()
func _on_validate_button_pressed() -> void:
undo_redo.create_action("validated")
undo_redo.add_do_property(self, "gp_ids", unvalidated_gp_ids.duplicate())
undo_redo.add_do_property(self, "word", word_edit.text)
undo_redo.add_do_property(self, "exception", int(exception_edit_checkbox.button_pressed))
undo_redo.add_do_property(self, "reading", int(reading_edit_checkbox.button_pressed))
undo_redo.add_do_property(self, "writing", int(writing_edit_checkbox.button_pressed))
undo_redo.add_undo_property(self, "gp_ids", gp_ids.duplicate())
undo_redo.add_undo_property(self, "word", word)
undo_redo.add_undo_property(self, "exception", int(exception_checkbox.button_pressed))
undo_redo.add_undo_property(self, "reading", int(reading_checkbox.button_pressed))
undo_redo.add_undo_property(self, "writing", int(writing_checkbox.button_pressed))
undo_redo.commit_action()
tab_container.current_tab = 0
update_lesson()
validated.emit()
func update_lesson() -> void:
var index: int = -1
for gp_id: int in gp_ids:
var min_index: int = Database.get_min_lesson_for_gp_id(gp_id)
if min_index < 0:
index = -1
break
index = maxi(index, min_index)
lesson = index
func gp_ids_from_string(p_gp_ids: String) -> Array[int]:
var res: Array[int] = []
var pack: PackedStringArray = p_gp_ids.split(" ")
res.resize(pack.size())
for index: int in range(pack.size()):
res[index] = int(pack[index])
return res
func set_gp_ids_from_string(p_gp_ids: String) -> void:
gp_ids = gp_ids_from_string(p_gp_ids)
unvalidated_gp_ids = gp_ids.duplicate()
func insert_in_database() -> void:
if id < 0:
Database.db.query_with_bindings("SELECT * FROM %s WHERE %s=?" % [table, table_graph_column], [word])
if not Database.db.query_result.is_empty():
id = Database.db.query_result[0].ID
if id >= 0:
var query: String = "SELECT %s, group_concat(%s, ' ') as %ss, group_concat(%s, ' ') as %ss, group_concat(%s.ID, ' ') as %sIDs, group_concat(%s.ID, ' ') as %sIDs, %s.Exception, %s.Reading, %s.Writing
FROM %s
INNER JOIN ( SELECT * FROM %s ORDER BY %s.Position ) %s ON %s.ID = %s.%sID
INNER JOIN %s ON %s.ID = %s.%s
WHERE %s.ID = ?
GROUP BY %s.ID" % [table_graph_column, sub_table_graph_column, sub_table_graph_column,
sub_table_phon_column, sub_table_phon_column,
relational_table, relational_table, sub_table, sub_table, table, table, table,
table,
relational_table, relational_table, relational_table, table, relational_table, table_graph_column,
sub_table, sub_table, relational_table, sub_table_id,
table,
table]
Database.db.query_with_bindings(query, [id])
Logger.trace("WordListElement: Sending query to insert in database: %s" % query)
if not Database.db.query_result.is_empty():
var element: Dictionary = Database.db.query_result[0]
if word != element[table_graph_column] or exception != element.Exception or reading != element.Reading or writing != element.Writing:
Database.db.update_rows(table, "ID=%s" % id, {table_graph_column: word, "Exception": exception, "Reading": reading, "Writing": writing})
if " ".join(gp_ids) != element[sub_table + "IDs"]:
var gps_in_words_ids: Array[String] = Array((element[relational_table + "IDs"] as String).split(" "))
while gps_in_words_ids.size() > gp_ids.size():
Database.db.delete_rows(relational_table, "ID=%s" % int(gps_in_words_ids.pop_back() as String))
for index: int in range(gps_in_words_ids.size()):
var gps_in_words_id: int = int(gps_in_words_ids[index])
Database.db.update_rows(relational_table, "ID=%s" % gps_in_words_id, {
table_graph_column + "ID": id,
sub_table_id: gp_ids[index],
"Position": index
})
for index: int in range(gps_in_words_ids.size(), gp_ids.size()):
Database.db.insert_row(relational_table, {
table_graph_column + "ID": id,
sub_table_id: gp_ids[index],
"Position": index
})
return
else:
Database.db.query_with_bindings("SELECT * FROM %s WHERE %s=?" % [table, table_graph_column], [word])
if not Database.db.query_result.is_empty():
var element: Dictionary = Database.db.query_result[0]
id = element.ID
if word != element[table_graph_column] or exception != element.Exception or reading != element.Reading or writing != element.writing:
Database.db.update_rows(table, "ID=%s" % id, {table_graph_column: word, "Exception": exception, "Reading": reading, "Writing": writing})
for index: int in range(gp_ids.size()):
Database.db.insert_row(relational_table, {
table_graph_column + "ID": id,
sub_table_id: gp_ids[index],
"Position": index
})
Database.db.query_with_bindings("SELECT * FROM %s WHERE %s=?" % [table, table_graph_column], [word])
if Database.db.query_result.is_empty():
Database.db.insert_row(table, {table_graph_column: word})
id = Database.db.last_insert_rowid
for index: int in range(gp_ids.size()):
Database.db.insert_row(relational_table, {
table_graph_column + "ID": id,
sub_table_id: gp_ids[index],
"Position": index
})
func _already_in_database(text: String) -> int:
var query: String = "SELECT %s.ID, %s, group_concat(%s, ' ') as %ss, group_concat(%s, ' ') as %ss, group_concat(%s.ID, ' ') as %sIDs
FROM %s
INNER JOIN ( SELECT * FROM %s ORDER BY %s.Position ) %s ON %s.ID = %s.%sID
INNER JOIN %s ON %s.ID = %s.%s
WHERE %s.%s = ?
GROUP BY %s.ID" % [table, table_graph_column, sub_table_graph_column, sub_table_graph_column,
sub_table_phon_column, sub_table_phon_column,
relational_table, relational_table,
table,
relational_table, relational_table, relational_table, table, relational_table, table_graph_column,
sub_table, sub_table, relational_table, sub_table_id,
table, table_graph_column,
table]
Database.db.query_with_bindings(query, [text])
if not Database.db.query_result.is_empty():
var element: Dictionary = Database.db.query_result[0]
id = element.ID
return id
return -1
func _add_from_additional_word_list(new_text: String) -> int:
if new_text in Database.additional_word_list:
var is_word: bool = table == "Words"
# res contains an int, followed by an array of int
var res: Array[Variant] = Database._import_word_from_csv(new_text, Database.additional_word_list[new_text].GPMATCH as String, is_word)
gps_updated.emit()
id = res[0]
gp_ids = res[1]
unvalidated_gp_ids = gp_ids
return id
return -1
func _try_to_complete_from_word(new_text: String) -> int:
var index: int = _already_in_database(new_text)
if index >= 0:
Logger.trace("WordListElement: Already in DB " + new_text)
return index
index = _add_from_additional_word_list(new_text)
if index >= 0:
return index
return -1
func _on_word_edit_text_submitted(new_text: String) -> void:
if _try_to_complete_from_word(new_text) >= 0:
_on_validate_button_pressed()
update_lesson()
func _on_remove_gp_button_2_pressed() -> void:
if graphemes_edit_container.get_child_count() < 2:
return
unvalidated_gp_ids.pop_back()
graphemes_edit_container.get_child(graphemes_edit_container.get_child_count() - 1).queue_free()
graphemes_edit_container.get_child(graphemes_edit_container.get_child_count() - 2).queue_free()
word_edit.text = get_graphemes(unvalidated_gp_ids)
func _process(_delta: float) -> void:
add_gp_button.visible = graphemes_edit_container.get_child_count() <= 0
remove_gp_button.visible = graphemes_edit_container.get_child_count() > 0
func new_gp_asked_added(ind: int, gp_id: int) -> void:
unvalidated_gp_ids[ind] = gp_id
set_graphemes_edit(unvalidated_gp_ids)
word_edit.text = get_graphemes(unvalidated_gp_ids)
func _on_add_gp_button_pressed(element: Node) -> void:
var ind_gp_id: int = int(element.get_index() / 2.0)
var gp_id: int = sub_elements_list.keys()[0]
unvalidated_gp_ids.insert(ind_gp_id + 1, gp_id)
add_gp_list_button(gp_id, ind_gp_id + 1)
func _on_empty_add_gp_button_pressed() -> void:
var gp_id: int = sub_elements_list.keys()[0]
unvalidated_gp_ids.append(gp_id)
add_gp_list_button(gp_id, unvalidated_gp_ids.size() - 1)