@@ -3,32 +3,36 @@ function plugindef()
33 finaleplugin .Author = " Carl Vine"
44 finaleplugin .AuthorURL = " https://carlvine.com/lua/"
55 finaleplugin .Copyright = " https://creativecommons.org/licenses/by/4.0/"
6- finaleplugin .Version = " 0.70 "
7- finaleplugin .Date = " 2023/07/25 "
6+ finaleplugin .Version = " 0.83 "
7+ finaleplugin .Date = " 2024/01/27 "
88 finaleplugin .MinJWLuaVersion = 0.60
9- finaleplugin .Notes = [[
10- This script provides rapid entry of simple or complex
9+ finaleplugin .Notes = [[
10+ This script allows rapid creation of simple or complex
1111 time signatures with a few keystrokes.
1212 It supports composite numerators like [3+2+3/16] and can join
13- with further composites (e.g. [3+2+3/16]+[1/4]+[5+4/8]).
14- "Display only" time signatures can be equally complex and set without using a mouse.
13+ easily with extra composites (e.g. [3+2+3/16]+[1/4]+[5+4/8]).
14+ "Display only" time signatures can be equally complex and set without using a mouse.
15+
1516 At startup the time signature of the first selected measure is shown.
16- Click the "Clear All" button to revert to a simple 4/4 with no other options.
17+ To revert to a simple 4/4 with no other options click the "Clear All" button or type [x].
18+ To read these script notes click the [?] button or type [q].
19+ To respace notes on completion click the "Respace" button or type [r].
1720
1821 All measures in the current selection will be assigned the new time signature.
22+ Use this feature to quickly copy the initial meter throughout the selection.
1923 If just one measure is selected only it will be changed.
2024
21- "Bottom" numbers (denominators) are the usual "note" numbers: 2, 4, 8, 16, 32, 64.
25+ "Bottom" numbers (denominators) are the usual "note" numbers: 2, 4, 8, 16, 32, or 64.
2226 "Top" numbers (numerators) are integers, optionally joined by '+' signs for composite meters.
23- Multiples of 3 automatically convert to compound signatures so [9/16] will
24- convert to three groups of dotted 8ths.
25- To prevent automatic compounding, instead of the bottom 'note' number enter its EDU value
26- (quarter note = 1024; eighth note = 512 etc).
27+ Numerators that are multiples of 3 automatically convert to compound signatures
28+ so [9/16] will convert to three groups of dotted 8ths.
29+ To prevent automatic compounding, instead of the bottom 'note' number enter its
30+ EDU value (quarter note = 1024; eighth note = 512; sixteenth = 256 etc).
2731
2832 Empty and zero "Top" numbers will be ignored.
29- If "Secondary" numbers are zero then "Tertiary" values are ignored.
33+ "Tertiary" values will be ignored if "Secondary" numbers are blank or zero.
3034 ]]
31- return " Meter Set Numeric" , " Meter Set Numeric" , " Set the Meter Numerically"
35+ return " Meter Set Numeric... " , " Meter Set Numeric" , " Set the Meter Numerically"
3236end
3337
3438--[[
@@ -45,130 +49,196 @@ end
4549 }
4650]]
4751
52+ local info_notes = [[
53+ This script allows rapid creation of simple or complex
54+ time signatures with a few keystrokes.
55+ It supports composite numerators like [3+2+3/16] and can join
56+ easily with extra composites (e.g. [3+2+3/16]+[1/4]+[5+4/8]).
57+ "Display only" time signatures can be equally complex and set without using a mouse.
58+ **
59+ At startup the time signature of the first selected measure is shown.
60+ To revert to a simple 4/4 with no other options click the "Clear All" button or type [x].
61+ To read these script notes click the [?] button or type [q].
62+ To respace notes on completion click the "Respace" button or type [r].
63+ **
64+ All measures in the current selection will be assigned the new time signature.
65+ Use this feature to quickly copy the initial meter throughout the selection.
66+ If just one measure is selected only it will be changed.
67+ **
68+ "Bottom" numbers (denominators) are the usual "note" numbers: 2, 4, 8, 16, 32, or 64.
69+ "Top" numbers (numerators) are integers, optionally joined by '+' signs for composite meters.
70+ Numerators that are multiples of 3 automatically convert to compound signatures
71+ so [9/16] will convert to three groups of dotted 8ths.
72+ To prevent automatic compounding, instead of the bottom 'note' number enter its
73+ EDU value (quarter note = 1024; eighth note = 512; sixteenth = 256 etc).
74+ **
75+ Empty and zero "Top" numbers will be ignored.
76+ "Tertiary" values will be ignored if "Secondary" numbers are blank or zero.
77+ ]]
78+ info_notes = info_notes :gsub (" \n %s*" , " " ):gsub (" *" , " \n " )
79+ .. " \n (v" .. finaleplugin .Version .. " )"
80+
4881local mixin = require (" library.mixin" )
4982local configuration = require (" library.configuration" )
83+ local library = require (" library.general_library" )
84+ local script_name = library .calc_script_name ()
85+
5086local config = {
5187 note_spacing = true ,
5288 window_pos_x = false ,
5389 window_pos_y = false ,
5490}
55- local script_name = " meter_set_numeric"
5691configuration .get_user_settings (script_name , config , true )
5792
58- function blank_meter ()
93+ local function blank_meter ()
5994 return { -- empty composite meter record
60- main = { top = {}, bottom = {} },
61- display = { top = {}, bottom = {} }
95+ main = { top = {}, bottom = {} },
96+ display = { top = {}, bottom = {} }
6297 }
6398end
6499
65- function dialog_set_position (dialog )
100+ local function dialog_set_position (dialog )
66101 if config .window_pos_x and config .window_pos_y then
67102 dialog :StorePosition ()
68103 dialog :SetRestorePositionOnlyData (config .window_pos_x , config .window_pos_y )
69104 dialog :RestorePosition ()
70105 end
71106end
72107
73- function dialog_save_position (dialog )
108+ local function dialog_save_position (dialog )
74109 dialog :StorePosition ()
75110 config .window_pos_x = dialog .StoredX
76111 config .window_pos_y = dialog .StoredY
77112 configuration .save_user_settings (script_name , config )
78113end
79114
80- function user_chooses_meter (meter , rgn )
115+ local function user_chooses_meter (meter , rgn )
81116 local x = { 0 , 70 , 130 , 210 , 280 , 290 } -- horizontal grid
82- local label = {" PRIMARY" , " (+ SECONDARY)" , " (+ TERTIARY)" }
83- local label_off = { 0 , - 37 , - 21 } -- horizontal offset for these names (ranged right)
117+ local label = { -- data type descriptors and (range right) horizontal offset
118+ { " PRIMARY" , 0 }, -- name, horiz (left) offset
119+ { " (+ SECONDARY)" , - 37 },
120+ { " (+ TERTIARY)" , - 21 }
121+ }
84122 local y_middle = 118 -- window vertical mid-point
85123 local offset = finenv .UI ():IsOnMac () and 3 or 0
124+ local name = plugindef ():gsub (" %.%.%." , " " ) -- remove trailing dots
125+ local function show_info () finenv .UI ():AlertInfo (info_notes , " About " .. name ) end
126+ local box , save_text = {}, {} -- user's edit-box entry responses
86127 local message = " m. " .. rgn .StartMeasure -- measure selection information
87128 if rgn .StartMeasure ~= rgn .EndMeasure then -- multiple measures
88129 message = " m" .. message .. " -" .. rgn .EndMeasure
89130 end
131+ local respace_check -- pre-declare the "respace" checkbox
132+
90133 local y = 0
91- local dialog = mixin .FCXCustomLuaWindow ():SetTitle (plugindef ())
92- -- STATIC TEXT ELEMENTS
93- local function cstat (nx , ny , nwide , ntext )
94- local dx = (type (nx ) == " number" ) and x [nx ] or tonumber (nx )
95- return dialog :CreateStatic (dx , ny ):SetWidth (nwide ):SetText (ntext )
96- end
97- local function join (values ) -- join integers from the meter.top array with '+' signs
98- if not values or # values == 0 then return " 0" end
99- return table.concat (values , " +" )
100- end
101- local shadow = cstat (" 1" , y + 1 , x [3 ], " TIME SIGNATURE" )
102- if shadow .SetTextColor then shadow :SetTextColor (153 , 153 , 153 ) end
103- cstat (1 , y , x [3 ], " TIME SIGNATURE" )
134+ local dialog = mixin .FCXCustomLuaWindow ():SetTitle (name )
135+ local function cstat (nx , ny , nwide , ntext , id )
136+ local dx = (type (nx ) == " number" ) and x [nx ] or tonumber (nx )
137+ return dialog :CreateStatic (dx , ny , id ):SetWidth (nwide ):SetText (ntext )
138+ end
139+ local function join (values ) -- join integers from the meter.top array with '+' signs
140+ if not values or # values == 0 then return " 0" end
141+ return table.concat (values , " +" )
142+ end
143+ local function clear_entries ()
144+ for j = 0 , 6 , 6 do
145+ for i = 1 , 3 do
146+ local n = (j == 0 and i == 1 ) and 4 or 0
147+ box [j + i ]:SetText (n )
148+ box [j + i + 3 ]:SetInteger (n )
149+ save_text [j + i ] = n
150+ save_text [j + i + 3 ] = n
151+ end
152+ end
153+ box [1 ]:SetKeyboardFocus ()
154+ end
155+ local function key_check (id )
156+ local s = box [id ]:GetText ():lower ()
157+ if s :find (" [^0-9+]" ) then
158+ if s :find (" x" ) then
159+ clear_entries ()
160+ else
161+ if s :find (" [?q]" ) then
162+ show_info ()
163+ elseif s :find (" r" ) then
164+ local n = respace_check :GetCheck ()
165+ respace_check :SetCheck ((n + 1 ) % 2 )
166+ end
167+ box [id ]:SetText (save_text [id ])
168+ end
169+ elseif s ~= " " then
170+ save_text [id ] = s -- save newly entered text
171+ end
172+ end
173+
174+ cstat (1 , y + 1 , x [3 ], " TIME SIGNATURE" , " main" )
104175 cstat (3 , y , x [3 ], " TOP" )
105176 cstat (4 , y , x [3 ], " BOTTOM" )
106177 cstat (6 , 25 , 80 , " Selection:" )
107178 cstat (6 , 40 , 80 , message )
179+ dialog :CreateButton (x [5 ], y ):SetWidth (80 ):SetText (" Clear All (x)" )
180+ :AddHandleCommand (function () clear_entries () end )
108181 y = y_middle
109182 cstat (x [2 ] - 30 , y - 30 , 330 , " 'TOP' entries can include integers joined by '+' signs" )
110183 dialog :CreateHorizontalLine (x [1 ], y - 9 , x [5 ] + 50 )
111184 dialog :CreateHorizontalLine (x [1 ], y - 8 , x [5 ] + 50 )
112- shadow = cstat (" 1" , y + 1 , x [3 ], " DISPLAY SIGNATURE" )
113- if shadow .SetTextColor then shadow :SetTextColor (153 , 153 , 153 ) end
114- cstat (1 , y , x [3 ], " DISPLAY SIGNATURE" )
185+ cstat (1 , y + 1 , x [3 ], " DISPLAY SIGNATURE" , " second" )
115186 cstat (3 , y , 150 , " (set to '0' for none)" )
116- dialog :CreateButton (x [5 ] + 60 , y ):SetText (" ?" ):SetWidth (20 )
117- :AddHandleCommand (function ()
118- finenv .UI ():AlertInfo (finaleplugin .Notes :gsub (" %s+" , " " ), " About " .. plugindef ())
119- end )
120- -- USER EDIT BOXES
187+
188+ -- COMPOSITE TIME SIG BOXES
121189 y = 0
122- local box = {} -- user's edit-box entry responses
123190 for jump = 0 , 6 , 6 do
124191 local t_sig = (jump == 0 ) and meter .main or meter .display
125192 for group = 1 , 3 do
126193 y = y + 20
127194 local id = group + jump
128- box [id ] = dialog :CreateEdit (x [3 ], y - offset , tostring ( id ) )
195+ box [id ] = dialog :CreateEdit (x [3 ], y - offset )
129196 :SetText (join (t_sig .top [group ])):SetWidth (65 )
130- box [id + 3 ] = dialog :CreateEdit (x [4 ], y - offset , tostring (id + 3 ))
197+ :AddHandleCommand (function () key_check (id ) end )
198+ box [id + 3 ] = dialog :CreateEdit (x [4 ], y - offset )
131199 :SetInteger (t_sig .bottom [group ] or 0 ):SetWidth (65 )
132- cstat (x [2 ] + label_off [group ], y , 56 - label_off [group ], label [group ])
200+ :AddHandleCommand (function () key_check (id + 3 ) end )
201+ cstat (x [2 ] + label [group ][2 ], y , 56 - label [group ][2 ], label [group ][1 ])
202+ save_text [id ] = box [id ]:GetText ()
203+ save_text [id + 3 ] = box [id + 3 ]:GetText ()
133204 end
134205 y = y_middle
135206 end
136- dialog :CreateCheckbox (x [3 ], y_middle + 85 , " spacing" ):SetText (" Respace notes on completion" )
137- :SetCheck (config .note_spacing and 1 or 0 ):SetWidth (170 )
138- local clear_button = dialog :CreateButton (x [5 ], 0 ):SetWidth (80 ):SetText (" Clear All" )
139- clear_button :AddHandleCommand (function ()
140- box [1 ]:SetText (" 4" )
141- box [4 ]:SetInteger (4 )
142- for _ , i in ipairs ({2 , 3 , 7 , 8 , 9 }) do -- "clear" the other edit boxes
143- box [i ]:SetText (" 0" )
144- box [i + 3 ]:SetInteger (0 )
145- end
146- box [1 ]:SetKeyboardFocus ()
147- end
148- )
207+ y = y + 60
208+ dialog :CreateButton (x [5 ] + 60 , y , " q" ):SetText (" ?" ):SetWidth (20 )
209+ :AddHandleCommand (function () show_info () end )
210+ y = y + 25
211+ respace_check = dialog :CreateCheckbox (x [3 ], y ):SetText (" Respace notes on completion (r)" )
212+ :SetCheck (config .note_spacing and 1 or 0 ):SetWidth (185 )
149213 dialog :CreateOkButton ()
150214 dialog :CreateCancelButton ()
151215 local choices = {} -- convert edit box values to 12-element table
152- dialog :RegisterInitWindow (function () box [1 ]:SetKeyboardFocus () end )
153- -- "TOP" values are strings, "BOTTOMS" are integers
154- dialog :RegisterHandleOkButtonPressed (function (self )
155- for count = 0 , 6 , 6 do -- "main" then "display" t_sig values
216+ dialog :RegisterInitWindow (function (self )
217+ box [1 ]:SetKeyboardFocus ()
218+ local q = self :GetControl (" q" )
219+ local bold = q :CreateFontInfo ():SetBold (true )
220+ q :SetFont (bold )
221+ self :GetControl (" main" ):SetFont (bold )
222+ self :GetControl (" second" ):SetFont (bold )
223+ end )
224+ dialog :RegisterHandleOkButtonPressed (function ()
225+ for count = 0 , 6 , 6 do -- "main" then "display" time_sig values
156226 for group = 1 , 3 do
157227 local id = count + group
158- choices [id ] = self : GetControl ( tostring ( id )) :GetText () or " 0"
228+ choices [id ] = box [ id ] :GetText () or " 0"
159229 if choices [id ] == " " then choices [id ] = " 0" end
160- choices [id + 3 ] = math.abs ( self : GetControl ( tostring ( id + 3 )) :GetInteger () ) or 0
230+ choices [id + 3 ] = box [ id + 3 ] :GetInteger () or 0
161231 end
162232 end
163- config .note_spacing = (self :GetControl (" spacing" ):GetCheck () == 1 )
164- dialog_save_position (self ) -- save window position and config choices
233+ config .note_spacing = (respace_check :GetCheck () == 1 )
165234 end )
235+ dialog :RegisterCloseWindow (function (self ) dialog_save_position (self ) end )
166236 dialog_set_position (dialog )
167237 local ok = dialog :ExecuteModal (nil ) -- run the dialog
168238 return ok , choices
169239end
170240
171- function encode_current_meter (time_sig , sub_meter )
241+ local function encode_current_meter (time_sig , sub_meter )
172242 local function numerators_treble (top_table )
173243 for j = 1 , # top_table do
174244 top_table [j ] = top_table [j ] * 3
@@ -211,7 +281,7 @@ function encode_current_meter(time_sig, sub_meter)
211281 end
212282end
213283
214- function copy_meter_from_score (measure_number )
284+ local function copy_meter_from_score (measure_number )
215285 local measure = finale .FCMeasure ()
216286 measure :Load (measure_number )
217287 local meter = blank_meter ()
@@ -222,16 +292,16 @@ function copy_meter_from_score(measure_number)
222292 return meter
223293end
224294
225- function is_power_of_two (num )
295+ local function is_power_of_two (num )
226296 local current = 1
227297 while current < num do
228298 current = current * 2
229299 end
230300 return current == num
231301end
232302
233- function convert_choices_to_meter (choices , meter )
234- if choices [1 ] == " 0" or choices [4 ] == 0 then
303+ local function convert_choices_to_meter (choices , meter )
304+ if choices [1 ] == " 0" or choices [1 ] == " " or choices [ 4 ] == 0 then
235305 return " Primary time signature cannot be zero"
236306 end
237307 for jump = 0 , 6 , 6 do -- 'main' meter then 'display' meter
@@ -273,7 +343,7 @@ function convert_choices_to_meter(choices, meter)
273343 return " " -- no error
274344end
275345
276- function new_composite_top (sub_meter )
346+ local function new_composite_top (sub_meter )
277347 local composite_top = finale .FCCompositeTimeSigTop ()
278348 for count = 1 , 3 do
279349 if not sub_meter [count ] or sub_meter [count ][1 ] == 0 then break end
@@ -286,7 +356,7 @@ function new_composite_top(sub_meter)
286356 return composite_top
287357end
288358
289- function new_composite_bottom (sub_meter )
359+ local function new_composite_bottom (sub_meter )
290360 local composite_bottom = finale .FCCompositeTimeSigBottom ()
291361 for count = 1 , 3 do
292362 if not sub_meter [count ] or sub_meter [count ] == 0 then break end
@@ -297,8 +367,7 @@ function new_composite_bottom(sub_meter)
297367 return composite_bottom
298368end
299369
300-
301- function fix_new_top (composite_top , time_sig , numerator )
370+ local function fix_new_top (composite_top , time_sig , numerator )
302371 if composite_top ~= nil then -- COMPOSITE top
303372 time_sig :SaveNewCompositeTop (composite_top )
304373 else
@@ -310,7 +379,7 @@ function fix_new_top(composite_top, time_sig, numerator)
310379 end
311380end
312381
313- function fix_new_bottom (composite_bottom , time_sig , denominator )
382+ local function fix_new_bottom (composite_bottom , time_sig , denominator )
314383 if composite_bottom ~= nil then -- COMPOSITE bottom
315384 time_sig :SaveNewCompositeBottom (composite_bottom )
316385 else
@@ -322,7 +391,7 @@ function fix_new_bottom(composite_bottom, time_sig, denominator)
322391 end
323392end
324393
325- function create_new_meter ()
394+ local function create_new_meter ()
326395 local region = mixin .FCMMusicRegion ()
327396 region :SetRegion (finenv .Region ()):SetStartMeasurePosLeft ():SetEndMeasurePosRight ()
328397
@@ -338,7 +407,7 @@ function create_new_meter()
338407 main = { top = nil , bottom = nil },
339408 display = { top = nil , bottom = nil }
340409 }
341- for _ , kind in ipairs ( {" main" , " display" }) do
410+ for _ , kind in ipairs {" main" , " display" } do
342411 if meter [kind ].top [1 ] and (# meter [kind ].top > 1 or # meter [kind ].top [1 ] > 1 ) then
343412 composites [kind ].top = new_composite_top (meter [kind ].top )
344413 if # meter [kind ].bottom > 1 then
0 commit comments