Iterating preferences.xml

I know how to iterate through tables using pairs(), but how do we do this in a Document? (the document is tied to renoise.tool().preferences)

My preference file is seen below, and I’d like to iterate template1, template2 et c in a for loop when building a menu. Should I somehow convert the document into a table for better handling?

<?xml version="1.0" encoding="UTF-8"?>
<ScriptingToolPreferences doc_version="0">
  <template2>
    <volume_column_visible>true</volume_column_visible>
    <sample_effects_column_visible>false</sample_effects_column_visible>
    <color>false</color>
    <name>Four voices</name>
    <delay_column_visible>false</delay_column_visible>
    <visible_note_columns>4</visible_note_columns>
    <panning_column_visible>false</panning_column_visible>
    <visible_effect_columns>0.0</visible_effect_columns>
    <line_index>1.0</line_index>
  </template2>
  <template1>
    <volume_column_visible>true</volume_column_visible>
    <track_name>false</track_name>
    <sample_effects_column_visible>false</sample_effects_column_visible>
    <color>false</color>
    <name>Basic</name>
    <delay_column_visible>false</delay_column_visible>
    <visible_note_columns>1.0</visible_note_columns>
    <panning_column_visible>false</panning_column_visible>
    <visible_effect_columns>1.0</visible_effect_columns>
    <line_index>false</line_index>
  </template1>
</ScriptingToolPreferences>

The DocumentAPI is a bit of a pain to work with. You basically have to (re-)create, in code, the document model, in order to load and parse it.

If you are looking for a quick and dirty XML parser, you could try this one:

https://github.com/bjorn-nesby/xLib/blob/master/classes/xParseXML.lua

It’s based on one of the better ones out there, slightly modified by me.

And no, you don’t need to use it as a class if you don’t want - parse() is implemented as a static method with no dependencies.

I can’t get anything to work really. preferences only tells me it is “userdata”, and I don’t know how to access it.

Maybe I should load the file (preferences_file = renoise.tool().bundle_path … “preferences.xml”), iterate lines and make my own parser, or is there a simpler way?

Here is a file from an old discontinued tool from me. It doesn’t run as is, but peeking through the code should give you an idea on how to work with the document API.

Thanks beatslaughter. Not sure this will help me though, since my xml isn’t indexed 1, 2, 3, 4… I wonder if there is a way to iterate the top hierarchy anyway.

Well, in your XML i would get rid of and and replace it with , if you really need it numbered you could add a tag below named 1 or similar. I’d even go as far as not using the song preferences but have it structured as …stuff……stuff…, then you can use simple counting using the # operator on the templates node, which would allow you to easy loop over your XML. I’ve attached a basic XML file from my classes down to the track level, which makes it a bit easier to follow maybe. The interesting bits are in the init functions of each class, it shows you all the different node types one can use and how to build a structure out of them.

OK. This is a real struggle to me, so bare with me. I’m not yet very familiar with documents and OO programming :smashed:

If the structure is as follows:

Will each template be identified as myDoc.templates.template[1] ?

And in that case, how would I add a new template? Something like myDoc:add_property(myDoc.templates, new_template).

PS. My syntax is probably non-sensical, but just so you understand what I’m struggling with…

Here is a very basic tool example to toy around with. I have opted to use own file saving functions instead of assigning it to the Renoise tool preferences, so you are always in control when things are saved. This should get you started i think?

--[[----------------------------------------------------------------------------

  Author : Alexander Stoica
  Creation Date : 05/21/2016
  Last modified : 05/21/2016

----------------------------------------------------------------------------]]--

_AUTO_RELOAD_DEBUG = true

--[[locals]]--------------------------------------------------------------]]--

local rd = renoise.Document
local rt = renoise.tool()

--[[Template class]]------------------------------------------------------]]--

class "Template"(rd.DocumentNode)

  function Template:__init()

    rd.DocumentNode.__init(self)

    self:add_property("name", rd.ObservableString())
    self:add_property("volume_column_visible", rd.ObservableBoolean())
    self:add_property("panning_column_visible", rd.ObservableBoolean())
    self:add_property("delay_column_visible", rd.ObservableBoolean())
    self:add_property("sample_effects_column_visible", rd.ObservableBoolean())
    self:add_property("color", rd.ObservableBoolean())
    self:add_property("visible_note_columns", rd.ObservableNumber())
    self:add_property("visible_effect_columns", rd.ObservableNumber())
    self:add_property("line_index", rd.ObservableNumber())

  end

--[[preferences]]---------------------------------------------------------]]--

local filename = rt.bundle_path .. "templates.xml"
local preferences = rd.create("TestToolPreferences") {}

-- load templates from file
if io.exists(filename) then
  if not preferences:load_from(filename) then
    print("Failed to load templates from file.")
  end
end

-- attempt to get existing template list
local templates = preferences:property("Templates")

-- if no list exists, create one
if templates == nil then
  templates = preferences:add_property("Templates", rd.DocumentList())
end
 
-- for testing purposes make sure the templates list is always empty
if #templates > 0 then
  for i = 1, #templates do
    templates:remove(1)
  end
end
 
-- this should return 0 template entries
print(#templates)

-- create 3 new template entries based on our class and name them
for i = 1, 3 do
  templates:insert(i, Template())
  templates[i].name.value = "Name " .. i
end

-- loop over the template names and print them
for i = 1, #templates do
  print(templates[i].name.value)
end

-- this should return 3 template entries
print(#templates)

-- save the templates to a file
preferences:save_as(filename)

---------------------------------------------------------------------[[EOF]]--
1 Like

You’re a hero, Beatslaughter! Struggling with preferences has taken me an “absurd” amount of time, but this should get me going in the right direction!