From d8a6ffd18ff5118feefe1064e956aac2c9571010 Mon Sep 17 00:00:00 2001 From: Robert Patterson Date: Sun, 4 Jan 2026 21:08:56 -0600 Subject: [PATCH 1/3] vibe coded mods --- src/font_map_legacy.lua | 527 +++++++++++++++++++++++++++++++++------- 1 file changed, 440 insertions(+), 87 deletions(-) diff --git a/src/font_map_legacy.lua b/src/font_map_legacy.lua index 6e4a16f9..078b47a5 100644 --- a/src/font_map_legacy.lua +++ b/src/font_map_legacy.lua @@ -28,12 +28,209 @@ context = { smufl_list = library.get_smufl_font_list(), current_font = finale.FCFontInfo("Maestro", 24), current_mapping = {}, - popup_keys = {}, + entries_by_glyph = {}, + popup_entries = {}, current_directory = finenv.RunningLuaFolderPath() } +local enable_disable +local get_popup_entry + +local function reset_mapping_state() + context.current_mapping = {} + context.entries_by_glyph = {} + context.popup_entries = {} +end + +local function parse_legacy_codepoint_string(str) + if type(str) == "number" then + return str + end + if type(str) ~= "string" then + return nil + end + str = utils.trim(str) + if str:match("^0[xX]%x+$") then + return tonumber(str, 16) + end + return tonumber(str) +end + +local function legacy_codepoint_to_string(legacy_codepoint, original) + if type(original) == "string" and #original > 0 then + return original + end + return tostring(legacy_codepoint) +end + +local function register_entry_glyph(entry) + if not entry or type(entry.glyph) ~= "string" then + return + end + local glyph_name = entry.glyph + if entry._registered_glyph == glyph_name then + return + end + if entry._registered_glyph then + local old_list = context.entries_by_glyph[entry._registered_glyph] + if old_list then + for index, candidate in ipairs(old_list) do + if candidate == entry then + table.remove(old_list, index) + break + end + end + if #old_list == 0 then + context.entries_by_glyph[entry._registered_glyph] = nil + end + end + end + context.entries_by_glyph[glyph_name] = context.entries_by_glyph[glyph_name] or {} + local glyph_list = context.entries_by_glyph[glyph_name] + local exists = false + for _, candidate in ipairs(glyph_list) do + if candidate == entry then + exists = true + break + end + end + if not exists then + table.insert(glyph_list, entry) + end + entry._registered_glyph = glyph_name +end + +local function ensure_entry_registration(entry) + if not entry or type(entry.glyph) ~= "string" then + return + end + if not entry.legacyCodepoints or #entry.legacyCodepoints == 0 then + return + end + entry.legacyStrings = entry.legacyStrings or {} + register_entry_glyph(entry) + for index, legacy_cp in ipairs(entry.legacyCodepoints) do + entry.legacyStrings[index] = entry.legacyStrings[index] or legacy_codepoint_to_string(legacy_cp) + context.current_mapping[legacy_cp] = context.current_mapping[legacy_cp] or {} + local mapping_list = context.current_mapping[legacy_cp] + local exists = false + for _, candidate in ipairs(mapping_list) do + if candidate == entry then + exists = true + break + end + end + if not exists then + table.insert(mapping_list, entry) + end + end +end + +local function unregister_entry_if_empty(entry) + if not entry or not entry.legacyCodepoints or #entry.legacyCodepoints > 0 then + return + end + if not entry._registered_glyph then + return + end + local glyph_list = context.entries_by_glyph[entry._registered_glyph] + if not glyph_list then + return + end + for index, candidate in ipairs(glyph_list) do + if candidate == entry then + table.remove(glyph_list, index) + break + end + end + if #glyph_list == 0 then + context.entries_by_glyph[entry._registered_glyph] = nil + end + entry._registered_glyph = nil +end + +local function remove_legacy_codepoint_from_entry(entry, legacy_codepoint) + if not entry or not entry.legacyCodepoints then + return + end + for i, value in ipairs(entry.legacyCodepoints) do + if value == legacy_codepoint then + table.remove(entry.legacyCodepoints, i) + if entry.legacyStrings then + table.remove(entry.legacyStrings, i) + end + break + end + end + local mapping_list = context.current_mapping[legacy_codepoint] + if mapping_list then + for i, candidate in ipairs(mapping_list) do + if candidate == entry then + table.remove(mapping_list, i) + break + end + end + if #mapping_list == 0 then + context.current_mapping[legacy_codepoint] = nil + end + end + unregister_entry_if_empty(entry) +end + +local function set_entry_smufl_info(entry, smufl_point, font) + if not entry then + return + end + local glyph_name, info = smufl_glyphs.get_glyph_info(smufl_point, font) + entry.codepoint = smufl_point + if info then + entry.glyph = glyph_name + else + entry.glyph = utils.format_codepoint(smufl_point) + end + if font and smufl_point >= 0xF400 and smufl_point <= 0xF8FF then + entry.smuflFontName = font.Name + else + entry.smuflFontName = nil + end + register_entry_glyph(entry) +end + +local function normalize_entry_legacy_arrays(entry) + if not entry or not entry.legacyCodepoints then + return + end + local zipped = {} + for index, cp in ipairs(entry.legacyCodepoints) do + if cp then + local str + if entry.legacyStrings and entry.legacyStrings[index] then + str = entry.legacyStrings[index] + else + str = legacy_codepoint_to_string(cp) + end + table.insert(zipped, {codepoint = cp, value = str}) + end + end + table.sort(zipped, function(a, b) + if a.codepoint == b.codepoint then + return (a.value or "") < (b.value or "") + end + return (a.codepoint or 0) < (b.codepoint or 0) + end) + entry.legacyCodepoints = {} + entry.legacyStrings = {} + for index, item in ipairs(zipped) do + entry.legacyCodepoints[index] = item.codepoint + entry.legacyStrings[index] = item.value + end +end + local function format_mapping(mapping) - local codepoint_desc = "[" .. utils.format_codepoint(mapping.codepoint) .. "]" + if not mapping then + return "" + end + local codepoint_desc = "[" .. utils.format_codepoint(mapping.codepoint or 0) .. "]" if mapping.glyph then codepoint_desc = "'" .. mapping.glyph .. "' " .. codepoint_desc end @@ -43,25 +240,13 @@ local function format_mapping(mapping) return codepoint_desc end -local function enable_disable(dialog) - local delable = #(dialog:GetControl("legacy_box"):GetText()) > 0 - local addable = delable and #(dialog:GetControl("smufl_box"):GetText()) > 0 - if delable then - local popup = dialog:GetControl("mappings") - delable = popup:GetCount() > 0 and context.popup_keys[popup:GetSelectedItem() + 1] ~= nil - end - dialog:GetControl("add_mapping"):SetEnable(addable) - dialog:GetControl("delete_mapping"):SetEnable(delable) -end - local function change_font(dialog, font_info) if font_info.IsSMuFLFont then dialog:CreateChildUI():AlertError("Unable to map SMuFL font " .. font_info:CreateDescription(), "SMuFL Font") return end context.current_font = font_info - context.current_mapping = {} - context.popup_keys = {} + reset_mapping_state() local control = dialog:GetControl("legacy_box") control:SetText("") control:SetFont(context.current_font) @@ -87,6 +272,28 @@ local function set_codepoint(control, codepoint) control:SetText(fcstr) end +get_popup_entry = function(popup) + if not popup then + return nil + end + local index = popup:GetSelectedItem() + if index == nil or index < 0 then + return nil + end + return context.popup_entries[index + 1] +end + +enable_disable = function(dialog) + local delable = #(dialog:GetControl("legacy_box"):GetText()) > 0 + local addable = delable and #(dialog:GetControl("smufl_box"):GetText()) > 0 + if delable then + local popup = dialog:GetControl("mappings") + delable = popup:GetCount() > 0 and get_popup_entry(popup) ~= nil + end + dialog:GetControl("add_mapping"):SetEnable(addable) + dialog:GetControl("delete_mapping"):SetEnable(delable) +end + local function on_smufl_popup(popup) local dialog = popup:GetParent() local smufl_box = dialog:GetControl("smufl_box") @@ -96,8 +303,9 @@ local function on_smufl_popup(popup) end local function on_popup(popup) - local legacy_codepoint = context.popup_keys[popup:GetSelectedItem() + 1] or 0 - local current_mapping = legacy_codepoint > 0 and context.current_mapping[legacy_codepoint] + local selection = get_popup_entry(popup) + local legacy_codepoint = selection and selection.legacy_codepoint or 0 + local current_mapping = selection and selection.entry local smufl_codepoint = current_mapping and current_mapping.codepoint or 0 local dialog = popup:GetParent() if current_mapping and current_mapping.smuflFontName then @@ -115,18 +323,41 @@ local function on_popup(popup) set_codepoint(dialog:GetControl("smufl_box"), smufl_codepoint) end -local function update_popup(popup, current_codepoint) - context.popup_keys = {} - for k in pairs(context.current_mapping) do - table.insert(context.popup_keys, k) +local function update_popup(popup, target_codepoint, target_entry) + context.popup_entries = {} + for legacy_codepoint, entry_list in pairs(context.current_mapping) do + if type(entry_list) == "table" then + for legacy_index, entry in ipairs(entry_list) do + table.insert(context.popup_entries, { + legacy_codepoint = legacy_codepoint, + entry = entry, + legacy_index = legacy_index + }) + end + end end - table.sort(context.popup_keys) + table.sort(context.popup_entries, function(a, b) + if a.legacy_codepoint == b.legacy_codepoint then + local glyph_a = (a.entry and a.entry.glyph) or "" + local glyph_b = (b.entry and b.entry.glyph) or "" + if glyph_a == glyph_b then + local codepoint_a = (a.entry and a.entry.codepoint) or 0 + local codepoint_b = (b.entry and b.entry.codepoint) or 0 + return codepoint_a < codepoint_b + end + return glyph_a < glyph_b + end + return a.legacy_codepoint < b.legacy_codepoint + end) popup:Clear() local current_index - for k, v in ipairs(context.popup_keys) do - popup:AddString(tostring(v) .. " maps to " .. format_mapping(context.current_mapping[v])) - if v == current_codepoint then - current_index = k - 1 + for index, info in ipairs(context.popup_entries) do + local label = tostring(info.legacy_codepoint) .. " maps to " .. format_mapping(info.entry) + popup:AddString(label) + if target_entry and info.entry == target_entry and info.legacy_codepoint == target_codepoint then + current_index = index - 1 + elseif not current_index and target_codepoint and info.legacy_codepoint == target_codepoint then + current_index = index - 1 end end if not current_index and popup:GetCount() > 0 then @@ -178,23 +409,63 @@ local function on_select_file(control) local json_contents = file:read("*a") file:close() local json = cjson.decode(json_contents) + if type(json) ~= "table" then + dialog:CreateChildUI():AlertError("Selected file is not a valid mapping.", "Invalid File") + return + end context.current_directory = path change_font(dialog, font_info) - context.current_mapping = {} - for glyph, v in pairs(json) do - local t = {} - t.glyph = glyph - t.codepoint = utils.parse_codepoint(v.codepoint) - t.nameIsMakeMusic = v.nameIsMakeMusic - t.smuflFontName = v.smuflFontName - if t.codepoint == 0xFFFD then - local smufl_box = dialog:GetControl("smufl_box") - local _, info = smufl_glyphs.get_glyph_info(glyph, smufl_box:CreateFontInfo()) - if info then - t.codepoint = info.codepoint + local smufl_box = dialog:GetControl("smufl_box") + for glyph, value in pairs(json) do + if type(glyph) == "string" and type(value) == "table" then + local entries = value + if not entries[1] and (entries.codepoint or entries.legacyCodepoint) then + entries = {entries} + end + for _, entry_data in ipairs(entries) do + if type(entry_data) == "table" then + local entry = { + glyph = glyph, + codepoint = utils.parse_codepoint(entry_data.codepoint or ""), + description = entry_data.description or "", + nameIsMakeMusic = entry_data.nameIsMakeMusic, + smuflFontName = entry_data.smuflFontName, + xOffset = entry_data.xOffset, + yOffset = entry_data.yOffset, + alternate = entry_data.alternate, + notes = entry_data.notes, + legacyCodepoints = {}, + legacyStrings = {} + } + if entry.codepoint == 0xFFFD then + local _, info = smufl_glyphs.get_glyph_info(glyph, smufl_box:CreateFontInfo()) + if info then + entry.codepoint = info.codepoint + end + end + if type(entry_data.legacyCodepoints) == "table" then + for _, legacy_str in ipairs(entry_data.legacyCodepoints) do + local cp_value = parse_legacy_codepoint_string(legacy_str) + if cp_value then + table.insert(entry.legacyCodepoints, cp_value) + table.insert(entry.legacyStrings, legacy_codepoint_to_string(cp_value, legacy_str)) + end + end + elseif entry_data.legacyCodepoint ~= nil then + local legacy_str = tostring(entry_data.legacyCodepoint) + local cp_value = parse_legacy_codepoint_string(entry_data.legacyCodepoint) + if cp_value then + table.insert(entry.legacyCodepoints, cp_value) + table.insert(entry.legacyStrings, legacy_codepoint_to_string(cp_value, legacy_str)) + end + end + normalize_entry_legacy_arrays(entry) + if entry.codepoint and #entry.legacyCodepoints > 0 then + ensure_entry_registration(entry) + end + end end end - context.current_mapping[tonumber(v.legacyCodepoint)] = t end update_popup(dialog:GetControl("mappings")) end @@ -230,74 +501,141 @@ local function on_add_mapping(control) if legacy_point == 0 then return end local smufl_point = get_codepoint(dialog:GetControl("smufl_box")) if smufl_point == 0 then return end - local current_mapping = context.current_mapping[legacy_point] - if current_mapping then - if finale.YESRETURN ~= dialog:CreateChildUI():AlertYesNo("Symbol " .. legacy_point .. " is already mapped to " .. format_mapping(current_mapping) .. ". Continue?", "Already Mapped") then + local font = dialog:GetControl("smufl_box"):CreateFontInfo() + local selection = get_popup_entry(popup) + local editing_entry = selection and selection.legacy_codepoint == legacy_point and selection.entry + if editing_entry then + set_entry_smufl_info(editing_entry, smufl_point, font) + update_popup(popup, legacy_point, editing_entry) + return + end + local existing_entries = context.current_mapping[legacy_point] + if existing_entries and #existing_entries > 0 then + local message + if #existing_entries == 1 then + message = "Symbol " .. legacy_point .. " is already mapped to " .. format_mapping(existing_entries[1]) .. ". Add another mapping?" + else + message = "Symbol " .. legacy_point .. " already has " .. #existing_entries .. " mappings. Add another mapping?" + end + if finale.YESRETURN ~= dialog:CreateChildUI():AlertYesNo(message, "Already Mapped") then return end end - local font = dialog:GetControl("smufl_box"):CreateFontInfo() - current_mapping = {codepoint = smufl_point} local glyph, info = smufl_glyphs.get_glyph_info(smufl_point, font) - if info then - current_mapping.glyph = glyph - else - current_mapping.glyph = utils.format_codepoint(smufl_point) - end + local new_entry = { + codepoint = smufl_point, + glyph = info and glyph or utils.format_codepoint(smufl_point), + description = "", + nameIsMakeMusic = nil, + smuflFontName = nil, + xOffset = nil, + yOffset = nil, + alternate = nil, + notes = nil, + legacyCodepoints = { legacy_point }, + legacyStrings = { legacy_codepoint_to_string(legacy_point) } + } if font and smufl_point >= 0xF400 and smufl_point <= 0xF8FF then - current_mapping.smuflFontName = font.Name + new_entry.smuflFontName = font.Name end - context.current_mapping[legacy_point] = current_mapping - update_popup(popup, legacy_point) + ensure_entry_registration(new_entry) + update_popup(popup, legacy_point, new_entry) end local function on_delete_mapping(control) local dialog = control:GetParent() local popup = dialog:GetControl("mappings") if popup:GetCount() > 0 then - local legacy_codepoint = context.popup_keys[popup:GetSelectedItem() + 1] - if legacy_codepoint then - context.current_mapping[legacy_codepoint] = nil + local selection = get_popup_entry(popup) + if selection and selection.entry and selection.legacy_codepoint then + remove_legacy_codepoint_from_entry(selection.entry, selection.legacy_codepoint) update_popup(popup) end end end -- use hand-crafted json encoder to control order of elements -local function emit_json(mapping, reverse_lookup) +local function emit_json(entries_by_glyph) local function quote(str) return '"' .. tostring(str):gsub('\\', '\\\\'):gsub('"', '\\"') .. '"' end - local function emit_entry(entry, legacy_codepoint) + local function format_legacy_array(entry) + local strings = {} + if entry.legacyCodepoints then + for index, legacy_cp in ipairs(entry.legacyCodepoints) do + local str = entry.legacyStrings and entry.legacyStrings[index] or legacy_codepoint_to_string(legacy_cp) + table.insert(strings, str) + end + end + if #strings == 0 then + return ' "legacyCodepoints": []' + end local parts = {} + for _, str in ipairs(strings) do + table.insert(parts, ' ' .. quote(str)) + end + return ' "legacyCodepoints": [\n' .. table.concat(parts, ",\n") .. '\n ]' + end + + local function emit_entry(entry) + local parts = { format_legacy_array(entry) } + table.insert(parts, ' "codepoint": ' .. quote(utils.format_codepoint(entry.codepoint))) + table.insert(parts, ' "description": ' .. quote(entry.description or "")) if type(entry.nameIsMakeMusic) == "boolean" then - table.insert(parts, '\t\t"nameIsMakeMusic": ' .. tostring(entry.nameIsMakeMusic)) + table.insert(parts, ' "nameIsMakeMusic": ' .. tostring(entry.nameIsMakeMusic)) end - if entry.codepoint then - table.insert(parts, '\t\t"codepoint": ' .. quote(utils.format_codepoint(entry.codepoint))) + if entry.smuflFontName then + table.insert(parts, ' "smuflFontName": ' .. quote(entry.smuflFontName)) end - table.insert(parts, '\t\t"legacyCodepoint": ' .. quote(tostring(legacy_codepoint))) - if entry.description then - table.insert(parts, '\t\t"description": ' .. quote(entry.description)) - else - table.insert(parts, '\t\t"description": ' .. quote("")) + if entry.xOffset then + table.insert(parts, ' "xOffset": ' .. quote(tostring(entry.xOffset))) end - if entry.smuflFontName then - table.insert(parts, '\t\t"smuflFontName": ' .. quote(entry.smuflFontName)) + if entry.yOffset then + table.insert(parts, ' "yOffset": ' .. quote(tostring(entry.yOffset))) end - return "{\n" .. table.concat(parts, ",\n") .. "\n\t}" + if type(entry.alternate) == "boolean" then + table.insert(parts, ' "alternate": ' .. tostring(entry.alternate)) + end + if entry.notes and #entry.notes > 0 then + table.insert(parts, ' "notes": ' .. quote(entry.notes)) + end + return " {\n" .. table.concat(parts, ",\n") .. "\n }" end local lines = { "{" } - local first = true - for key, entry in pairsbykeys(mapping) do - if reverse_lookup[entry.glyph] then - if not first then - lines[#lines] = lines[#lines] .. "," -- ← append comma to previous line + local first_glyph = true + for glyph, entry_list in pairsbykeys(entries_by_glyph) do + if type(glyph) == "string" and type(entry_list) == "table" and #entry_list > 0 then + local sortable = {} + for _, entry in ipairs(entry_list) do + if entry.legacyCodepoints and #entry.legacyCodepoints > 0 then + table.insert(sortable, entry) + end + end + if #sortable > 0 then + table.sort(sortable, function(a, b) + local a_codepoint = a.legacyCodepoints and a.legacyCodepoints[1] or 0 + local b_codepoint = b.legacyCodepoints and b.legacyCodepoints[1] or 0 + if a_codepoint == b_codepoint then + return (a.codepoint or 0) < (b.codepoint or 0) + end + return a_codepoint < b_codepoint + end) + if not first_glyph then + lines[#lines] = lines[#lines] .. "," + end + table.insert(lines, " " .. quote(glyph) .. ": [") + for index, entry in ipairs(sortable) do + local entry_text = emit_entry(entry) + if index < #sortable then + entry_text = entry_text .. "," + end + table.insert(lines, entry_text) + end + table.insert(lines, " ]") + first_glyph = false end - table.insert(lines, "\t" .. quote(entry.glyph) .. ": " .. emit_entry(entry, key)) - first = false end end table.insert(lines, "}") @@ -306,7 +644,19 @@ end local function on_save(control) local dialog = control:GetParent() - if type(context.current_mapping) ~= "table" or not next(context.current_mapping) then + local function has_mappings() + for _, entry_list in pairs(context.entries_by_glyph) do + if type(entry_list) == "table" then + for _, entry in ipairs(entry_list) do + if entry.legacyCodepoints and #entry.legacyCodepoints > 0 then + return true + end + end + end + end + return false + end + if not has_mappings() then dialog:CreateChildUI():AlertInfo("Nothing has been mapped.", "No Mapping") return end @@ -321,20 +671,23 @@ local function on_save(control) end local path_fstr = finale.FCString() save_dialog:GetFileName(path_fstr) - local reverse_lookup = {} - for legacy_codepoint, info in pairs(context.current_mapping) do - if type(info.glyph) ~= "string" then - dialog:CreateChildUI():AlertError("No glyph name found for legacy codepoint " .. legacy_codepoint .. ".", - "No Glyph Name") - return - elseif reverse_lookup[info.glyph] then - if finale.YESRETURN ~= dialog:CreateChildUI():AlertYesNo("Glyph name " .. info.glyph .. " is mapped more than once. Continue?", "Duplicate Glyph Name") then - return + for _, entry_list in pairs(context.entries_by_glyph) do + if type(entry_list) == "table" then + for _, entry in ipairs(entry_list) do + if entry.legacyCodepoints and #entry.legacyCodepoints > 0 then + if type(entry.glyph) ~= "string" or entry.glyph == "" then + dialog:CreateChildUI():AlertError("A mapping is missing a glyph name.", "Missing Glyph Name") + return + end + if not entry.codepoint then + dialog:CreateChildUI():AlertError("A mapping is missing a SMuFL codepoint.", "Missing Codepoint") + return + end + end end end - reverse_lookup[info.glyph] = true end - local result = emit_json(context.current_mapping, reverse_lookup) + local result = emit_json(context.entries_by_glyph) local file = io.open(client.encode_with_client_codepage(path_fstr.LuaString), "w") if not file then dialog:CreateChildUI():AlertError("Unable to write to file " .. path_fstr.LuaString .. ".", "File Error") From 3bbd623308eebd52d0cb4bf62cbe74b06d6c3fbe Mon Sep 17 00:00:00 2001 From: Robert Patterson Date: Sun, 1 Feb 2026 10:51:35 -0600 Subject: [PATCH 2/3] fix typo in tie.lua --- src/library/tie.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library/tie.lua b/src/library/tie.lua index 5dad22b9..c4c4a068 100644 --- a/src/library/tie.lua +++ b/src/library/tie.lua @@ -555,7 +555,7 @@ function tie.calc_placement(note, tie_mod, for_pageview, direction, tie_prefs) end else local next_note = next_entry:GetItemAt(next_entry.Count - 1) - if next_note.Displacment > note.Displacement then + if next_note.Displacement > note.Displacement then end_placement = finale.TIEPLACE_OVERINNER else -- flaky behavior alert: this code might not work in a future release but From d3d31e299c55d5fd9a5b544fc1dc6a8f285149fd Mon Sep 17 00:00:00 2001 From: Robert Patterson Date: Thu, 5 Feb 2026 19:55:23 -0600 Subject: [PATCH 3/3] fix bugs found in musxdom testing: - get end notehead width from end testing - do not early exit with tie-end index unless it is a tie end --- src/library/tie.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/library/tie.lua b/src/library/tie.lua index c4c4a068..1a111f6c 100644 --- a/src/library/tie.lua +++ b/src/library/tie.lua @@ -720,7 +720,7 @@ local calc_tie_length = function(note, tie_mod, for_pageview, direction, tie_pre if rplacement == finale.TIEPLACE_OVERINNER or rplacement == finale.TIEPLACE_UNDERINNER or rplacement == finale.TIEPLACE_UNDEROUTERSTEM then incr_end = -INNER_INCREMENT else - horz_end = horz_end + (entry_metrics_start:GetNoteWidth(note.NoteIndex) * (1.0 - OUTER_NOTE_OFFSET_PCTG)) + horz_end = horz_end + (entry_metrics:GetNoteWidth(note_index) * (1.0 - OUTER_NOTE_OFFSET_PCTG)) end end @@ -760,15 +760,15 @@ function tie.calc_contour_index(note, tie_mod, for_pageview, direction, tie_pref end direction = direction and direction ~= finale.TIEMODDIR_AUTOMATIC and direction or tie.calc_direction(note, tie_mod, tie_prefs) local tie_placement_prefs = tie_prefs:CreateTiePlacementPrefs() - if tie_prefs.UseTieEndStyle then + if tie_mod and not tie_mod:IsStartTie() and tie_prefs.UseTieEndStyle then return finale.TCONTOURIDX_TIEENDS end local tie_length = calc_tie_length(note, tie_mod, for_pageview, direction, tie_prefs, tie_placement_prefs) local tie_contour_prefs = tie_prefs:CreateTieContourPrefs() if tie_length >= tie_contour_prefs:GetSpan(finale.TCONTOURIDX_LONG) then - return finale.TCONTOURIDX_LONG + return finale.TCONTOURIDX_LONG, tie_length elseif tie_length <= tie_contour_prefs:GetSpan(finale.TCONTOURIDX_SHORT) then - return finale.TCONTOURIDX_SHORT + return finale.TCONTOURIDX_SHORT, tie_length end return finale.TCONTOURIDX_MEDIUM, tie_length end