-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPakettiActionSelector.lua
386 lines (354 loc) · 12 KB
/
PakettiActionSelector.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
-- Paketti files list (keep your existing list)
local paketti_files = {
"Paketti0G01_Loader",
"PakettieSpeak",
"PakettiPlayerProSuite",
"PakettiChordsPlus",
"PakettiLaunchApp",
"PakettiSampleLoader",
"PakettiCustomization",
"PakettiDeviceChains",
"base64float",
"PakettiLoadDevices",
"PakettiSandbox",
"PakettiTupletGenerator",
"PakettiLoadPlugins",
"PakettiPatternSequencer",
"PakettiPatternMatrix",
"PakettiInstrumentBox",
"PakettiYTDLP",
"PakettiStretch",
"PakettiBeatDetect",
"PakettiStacker",
"PakettiRecorder",
"PakettiControls",
"PakettiKeyBindings",
"PakettiPhraseEditor",
"PakettiOctaMEDSuite",
"PakettiWavetabler",
"PakettiAudioProcessing",
"PakettiPatternEditorCheatSheet",
"PakettiThemeSelector",
"PakettiMidiPopulator",
"PakettiImpulseTracker",
"PakettiGater",
"PakettiAutomation",
"PakettiUnisonGenerator",
"PakettiMainMenuEntries",
"PakettiMidi",
"PakettiDynamicViews",
"PakettiEightOneTwenty",
"PakettiExperimental_Verify",
"PakettiLoaders",
"PakettiPatternEditor",
"PakettiTkna",
"PakettiRequests",
"PakettiSamples",
"Paketti35"
}
local dialog = nil
function ActionSelectorDialog()
if dialog and dialog.visible then
dialog:close()
return
end
local actions = {}
local selected_actions = {}
local vb = renoise.ViewBuilder()
-- Pre-declare all helper functions
local ActionSelectorSaveToFile
local ActionSelectorLoadPreferences
local ActionSelectorReset
-- Helper function to scan a file for menu entries and keybindings
local function ActionSelectorScanFile(filename)
local file = io.open(renoise.tool().bundle_path .. filename .. ".lua", "r")
if not file then
print("Could not open file: " .. filename)
return
end
local content = file:read("*all")
file:close()
-- Look for menu entries with their invoke functions
for entry, invoke_func in content:gmatch('add_menu_entry%s*{%s*name%s*=%s*"([^"]+)"%s*,%s*invoke%s*=%s*([^}]-)}') do
print("Adding menu entry:", entry)
table.insert(actions, {
type = "Menu Entry",
name = entry:gsub("^%-%-%s*", ""), -- Remove leading --
invoke = invoke_func:match("^%s*(.-)%s*$") -- Trim whitespace
})
end
-- Look for keybindings with their invoke functions
for binding, invoke_func in content:gmatch('add_keybinding%s*{%s*name%s*=%s*"([^"]+)"%s*,%s*invoke%s*=%s*([^}]-)}') do
print("Adding keybinding:", binding)
table.insert(actions, {
type = "Keybinding",
name = binding:gsub("^%-%-%s*", ""), -- Remove leading --
invoke = invoke_func:match("^%s*(.-)%s*$") -- Trim whitespace
})
end
end
-- First, require all files to ensure functions are in scope
for _, filename in ipairs(paketti_files) do
require(filename)
end
-- Then scan for menu entries and keybindings
for _, filename in ipairs(paketti_files) do
ActionSelectorScanFile(filename)
end
-- Sort actions: Menu Entries first (alphabetically), then Keybindings (alphabetically)
table.sort(actions, function(a,b)
if a.type ~= b.type then
return a.type == "Menu Entry"
else
return a.name < b.name
end
end)
print("Total actions found:", #actions)
-- Define helper functions
ActionSelectorSaveToFile = function()
local file = io.open(renoise.tool().bundle_path .. "action_selector_settings.txt", "w")
if file then
for i = 1, 50 do
local dropdown = vb.views["dropdown_" .. i]
local value = dropdown.value
local selected_item = dropdown.items[value]
file:write(selected_item .. "\n")
-- Also save to preferences with correct index naming
preferences.ActionSelector["Index" .. string.format("%02d", i)].value =
value > 1 and (actions[value - 1].type .. ": " .. actions[value - 1].name .. "||" .. actions[value - 1].invoke) or ""
end
file:close()
renoise.app():show_status("Action Selector settings saved")
end
end
ActionSelectorLoadPreferences = function()
-- First try to load from file
local file = io.open(renoise.tool().bundle_path .. "action_selector_settings.txt", "r")
if file then
for i = 1, 50 do
local line = file:read("*line")
if line then
local dropdown = vb.views["dropdown_" .. i]
-- Find matching item in dropdown
for idx, item in ipairs(dropdown.items) do
if item == line then
dropdown.value = idx
selected_actions[i] = idx > 1 and actions[idx - 1] or nil
-- Also update preferences
preferences.ActionSelector["Index" .. string.format("%02d", i)].value =
idx > 1 and (actions[idx - 1].type .. ": " .. actions[idx - 1].name .. "||" .. actions[idx - 1].invoke) or ""
break
end
end
end
end
file:close()
renoise.app():show_status("Action Selector settings loaded from file")
else
-- Fall back to preferences if file doesn't exist
for i = 1, 50 do
local pref_value = preferences.ActionSelector["Index" .. string.format("%02d", i)].value
if pref_value and pref_value ~= "" then
local display_text, invoke_func = pref_value:match("(.+)||(.+)")
if display_text and invoke_func then
local dropdown = vb.views["dropdown_" .. i]
-- Find matching item by display text
for idx, item in ipairs(dropdown.items) do
if item == display_text then
dropdown.value = idx
-- Store the actual action data
selected_actions[i] = {
type = display_text:match("^([^:]+):"),
name = display_text:match(": (.+)$"),
invoke = invoke_func
}
break
end
end
end
end
end
end
end
ActionSelectorReset = function()
for i = 1, 50 do
local dropdown = vb.views["dropdown_" .. i]
dropdown.value = 1
selected_actions[i] = nil
preferences.ActionSelector["Index" .. string.format("%02d", i)].value = ""
end
renoise.app():show_status("Action Selector reset complete")
end
-- Create dialog content
local dialog_content = vb:column {
margin = 0,
spacing = 0,
vb:row {
margin = 0,
spacing = 2,
vb:button {
text = "Debug All Actions",
width = 150,
notifier = function()
print("\n=== Starting Debug Test of All Actions ===\n")
local failed_actions = {}
for i, action in ipairs(actions) do
local success = false
-- Wrap everything in pcall to prevent any errors from stopping the process
local ok, err = pcall(function()
-- Method 1: Try direct evaluation
local func = load("return " .. action.invoke)
if func then
local ok, result = pcall(func)
if ok and type(result) == "function" then
success = true
end
end
-- Method 2: Try global lookup if Method 1 failed
if not success and _G[action.invoke] and type(_G[action.invoke]) == "function" then
success = true
end
end)
-- If either the test failed or there was an error, record it
if not (ok and success) then
table.insert(failed_actions, {
index = i,
type = action.type,
name = action.name,
invoke = action.invoke,
error = not ok and err or "Could not execute action"
})
end
end
-- Print summary of failures only
print(string.format("\nTotal actions tested: %d", #actions))
print(string.format("Failed actions: %d", #failed_actions))
if #failed_actions > 0 then
print("\nFailed Actions:")
for _, fail in ipairs(failed_actions) do
print(string.format("\n#%d: %s: %s", fail.index, fail.type, fail.name))
print("Invoke code:", fail.invoke)
print("Error:", fail.error)
end
end
print("\n=== End of Debug Report ===\n")
end
},
vb:button {
text = "Save",
width = 60,
notifier = function()
ActionSelectorSaveToFile()
end
},
vb:button {
text = "Load",
width = 60,
notifier = function()
ActionSelectorLoadPreferences()
end
},
vb:button {
text = "Reset",
width = 60,
notifier = function()
ActionSelectorReset()
end
},
vb:button {
text = "Random Fill",
width = 80,
notifier = function()
local available = {}
for i, action in ipairs(actions) do
table.insert(available, i)
end
for i = 1, 50 do
if #available > 0 then
local rand_idx = math.random(1, #available)
local action_idx = available[rand_idx]
table.remove(available, rand_idx)
local dropdown = vb.views["dropdown_" .. i]
dropdown.value = action_idx + 1 -- +1 because of <None>
end
end
ActionSelectorSaveToFile()
end
}
}
}
-- Create 50 rows of dropdown + button
for i = 1, 50 do
local row = vb:row {
margin = 0,
spacing = 2,
vb:text {
text = string.format("%02d. ", i),
style="strong",
font="bold",
width = 24,
},
vb:popup {
id = "dropdown_" .. i,
width = 650,
items = {"<None>"},
value = 1,
notifier = function(idx)
selected_actions[i] = idx > 1 and actions[idx - 1] or nil
preferences.ActionSelector["Index" .. string.format("%02d", i)].value =
idx > 1 and (actions[idx - 1].type .. ": " .. actions[idx - 1].name .. "||" .. actions[idx - 1].invoke) or ""
end
},
vb:button {
text = "Run",
width = 50,
notifier = function()
local action = selected_actions[i]
if action then
local func = load("return " .. action.invoke)
if func then
local success, result = pcall(func)
if success and type(result) == "function" then
result()
elseif _G[action.invoke] and type(_G[action.invoke]) == "function" then
_G[action.invoke]()
end
end
end
renoise.app().window.active_middle_frame=renoise.app().window.active_middle_frame
end
}
}
dialog_content:add_child(row)
end
-- Add all actions to each dropdown
for i = 1, 50 do
local dropdown = vb.views["dropdown_" .. i]
local items = {"<None>"}
for _, action in ipairs(actions) do
table.insert(items, action.type .. ": " .. action.name)
end
dropdown.items = items
end
-- Load initial values from preferences
ActionSelectorLoadPreferences()
dialog = renoise.app():show_custom_dialog(
string.format("Paketti Action Selector (%d actions available)", #actions),
dialog_content, keyhandlerfuncActionSelector
)
-- if renoise.app().window.active_middle_frame==1 then
--- renoise.app().window.active_middle_frame=1 end
renoise.app().window.active_middle_frame=renoise.app().window.active_middle_frame
end
function keyhandlerfuncActionSelector(dialog, key)
local closer = "esc"
if key.modifiers == "" and key.name == closer then
dialog:close()
dialog = nil
return nil
else
return key
end
end
renoise.tool():add_menu_entry {name = "Main Menu:Tools:Paketti..:Paketti Action Selector",invoke = ActionSelectorDialog}
renoise.tool():add_keybinding {name = "Global:Paketti:Paketti Action Selector",invoke = ActionSelectorDialog}