Ok managed to get something working (alpha-ish)
Choose group you want to extend from the popup, then nudge with buttons.
Bonus buttons bypass or enable all the DSPs in the selected group track.
https://forum.renoise.com/t/new-tool-3-1-group-nudge-sep-2018/49540
Shortcut Group Nudge
Menu: Tools: Ledger`s Scripts: Group Nudge
and here`s the magic function, just pass in the group index of the group that you want to nudge left:
It required juggling two blank “dummy tracks” to get around the swapping problems stated in this thread.
--extend left border of group leftwards by one track
---------------------------------------
function extend_group_left(group_index)
---------------------------------------
local song = renoise.song()
local group_track = song:track(group_index)
--get the index of the track to the left of the group. This is the track we want to add to the group
local left_of_grp_idx = (group_index - #group_track.members - 1)
--if left_of_grp_idx == 0 then there are no more tracks to the left so return early
if left_of_grp_idx == 0 then
return
end
--if group has no members already, then it is a simple add track to group operation and
--no re-organisation is needed
if #group_track.members == 0 then
song:add_track_to_group(left_of_grp_idx,group_index)
return
end
--1) Add 2 dummy tracks into group
----------------------------------
--NOTE We need to insert 2 dummy tracks to account for bugs with the renoise swap tracks behaviour within groups
--see: https://forum.renoise.com/t/lua-api-bug-swap-tracks/49534
--set member_idx to the first member track of the group
local member_idx = left_of_grp_idx + 1
--loop group members to find insert point for dummy tracks (after first legitimate member; i.e. not a track in a further sub-group)
for i = 1,#group_track.members do
--check if member is not in a sub group by checking its parent
if rawequal(song:track(member_idx).group_parent,group_track) == true then
break
end
--if here then member was in a sub- group so increment member_idx until we get to a legitimate track
member_idx = member_idx + 1
end
--again due to insert_track behaviour we need to insert the dummy tracks after the first present and legitimate member of the group
--so + 1
local insert_point = member_idx + 1
--insert 2 dummy tracks at member_idx
local dummy_trk_2 = song:insert_track_at(insert_point)
local dummy_trk_1 = song:insert_track_at(insert_point)
--name the tracks for bug hunting i.e. if they are not deleted we will see them in pattern ed
dummy_trk_1.name = "Dummy Track 1"
dummy_trk_2.name = "Dummy Track 2"
--2) Swap Dummy tracks to members 1 and 2 of group (this works re. the swap bug as we are not swapping two groups)
song:swap_tracks_at(member_idx,member_idx+1)
song:swap_tracks_at(member_idx+1,member_idx+2)
--3) Preparation done, so bring the track we want into the group
--Swap dummy member 2 with target track outside group (left_of_grp_idx was found near beginning of function)
--note: if we were to swap dummy member 1 then both tracks get ejected from group
song:swap_tracks_at(left_of_grp_idx,(left_of_grp_idx + 2))
--4) Tidy up: delete dummy tracks by looping objects
--(as we are deleting tracks it is safer to loop and find the dummy track objects specifically as
--we might lose wanted song tracks if we were calculating indexes and a mistake made)
--As indexes can change when a track is deleted, it is simpler to use 2 loops
for i = 1,#song.tracks do
if rawequal(song:track(i),dummy_trk_1) then
--delete dummy 1
renoise.song():delete_track_at(i)
break
end
end
for i = 1,#song.tracks do
if rawequal(song:track(i),dummy_trk_2) then
--delete dummy 2
renoise.song():delete_track_at(i)
break
end
end
end