sTitle = 'Default_6.0 theme adjuster'
reaper.ClearConsole()

_desired_sizes = { { 590, 757}, { 850, 800 } }
gfx.ext_retina = 1
drawScale,drawScale_nonmac,drawScale_inv_nonmac,drawScale_inv_mac = 1,1,1,1

_gfxw,_gfxh = table.unpack(_desired_sizes[reaper.GetExtState(sTitle,'showHelp') == 'false' and 1 or 2])

gfx.init(sTitle, _gfxw,_gfxh,
tonumber(reaper.GetExtState(sTitle,"dock")) or 0,
tonumber(reaper.GetExtState(sTitle,"wndx")) or 100,
tonumber(reaper.GetExtState(sTitle,"wndy")) or 50)

function debugTable(t)
  local str = ''
  reaper.ShowConsoleMsg('------ debug ------ \n')
  for i, v in pairs(t) do
    str = str..i..' = '..tostring(v)..'\n'
  end
  reaper.ShowConsoleMsg(str..'\n')
end

globalBorderX, globalBorderY = 6,4 -- docked border
activeTcpLayout, activeMcpLayout = 'A', 'A'

  --------- COLOURS ---------

palette = {}
palette.idx = {'Rv6','Pride','Warm','Cool','Vice','Eeek'}
palette.current = tonumber(reaper.GetExtState(sTitle,'paletteCurrent')) or 1
palette.Rv6 = {{84,84,84},{105,137,137},{129,137,137},{168,168,168},{19,189,153},{51,152,135},{184,143,63},{187,156,148},{134,94,82},{130,59,42}}
palette.Pride = {{84,84,84},{138,138,138},{155,55,55},{155,129,55},{105,155,55},{55,155,81},{55,155,155},{55,81,155},{105,55,155},{155,55,129}}
palette.Warm = {{128,67,64},{184,82,46},{239,169,81},{230,204,143},{231,185,159},{208,193,180},{176,177,161},{108,120,116},{128,114,98},{97,87,74}}
palette.Cool = {{35,75,84},{58,79,128},{95,88,128},{92,102,112},{67,104,128},{91,125,134},{95,92,85},{131,135,97},{55,118,94},{75,99,32}}
palette.Vice = {{255,0,111},{255,89,147},{254,152,117},{255,202,193},{249,255,168},{122,242,178},{87,255,255},{51,146,255},{168,117,255},{99,77,196}}
palette.Eeek = {{255,0,0},{255,111,0},{255,221,0},{179,255,0},{0,255,123},{0,213,255},{0,102,255},{93,0,255},{204,0,255},{255,0,153}}

function getCurrentPalette()
  return palette.idx[palette.current] or 'Rv6'
end

function setCol(col)
  local r = col[1] / 255
  local g = col[2] / 255
  local b = col[3] / 255
  local a = 1
  if col[4] ~= nil then a = col[4] / 255 end
  gfx.set(r,g,b,a)
end

function setCustCol(track, r,g,b)
  reaper.SetTrackColor(reaper.GetTrack(0, track),reaper.ColorToNative(r,g,b))
end

function applyCustCol(col)
  if type(col) ~= "table" or #col < 3 then
    return
  end
  reaper.Undo_BeginBlock()
  for i=0, reaper.CountTracks(0)-1 do
    if reaper.IsTrackSelected(reaper.GetTrack(0, i)) then
      setCustCol(i, table.unpack(col))
    end
  end
  reaper.Undo_EndBlock('custom color changes',-1)
end

function paletteChoose(p)
  local _v = palette.current + p[2]
  if _v < 1 then _v = 1
  elseif _v > #palette.idx then _v = #palette.idx end
  palette.current = _v
end

function getCustCol(track)
  local c = reaper.GetTrackColor(reaper.GetTrack(0, track))
  if c == 0 then return nil end
  return reaper.ColorFromNative(c)
end

function addRandPalette(pal, curpal)
  local pass = math.floor(#pal/#curpal)
  local offs, adj, wadj = #pal, math.floor((pass+2)/3), 1 + (pass%3)
  for i = 1, #curpal do
    local a = { table.unpack(curpal[i]) }
    if a[wadj] > 128 then a[wadj] = math.max(a[wadj] - adj,0) else a[wadj] = math.min(a[wadj] + adj,255) end
    pal[#pal+1] = a
  end
  for i = #curpal, 2, -1 do
    local j = math.random(i)+offs
    pal[offs+i], pal[j] = pal[j], pal[offs+i]
  end
end

function applyPalette()
  local curpal = palette[getCurrentPalette()] or palette.Rv6
  local randpal = {}

  reaper.Undo_BeginBlock()
  local cnt, colmap = 1, {}
  for i = 0, reaper.CountTracks(0)-1 do
    local r, g, b = getCustCol(i)
    if b ~= nil then
      local colkey = (r<<16)|(g<<8)|b
      if colmap[colkey] == nil then
        if cnt > #randpal then
          addRandPalette(randpal,curpal)
        end
        colmap[colkey] = cnt
        cnt = cnt + 1
      end
      local wc=colmap[colkey]
      setCustCol(i, table.unpack(randpal[wc]))
    end
  end
  reaper.Undo_EndBlock('Recolor using palette',-1)
end


  ---------- TEXT -----------

textPadding = 3
if reaper.GetOS():match("^Win") == nil then
  gfx.setfont(1, "Verdana", -9)
  gfx.setfont(2, "Verdana", -10)
  gfx.setfont(3, "Tahoma", -10)
  gfx.setfont(4, "Verdana", -14)
  gfx.setfont(5, "Tahoma", -11)
  gfx.setfont(11, "Verdana", -18)
  gfx.setfont(12, "Verdana", -20)
  gfx.setfont(13, "Tahoma", -20)
  gfx.setfont(14, "Verdana", -28)
  gfx.setfont(15, "Tahoma", -22)
else
  gfx.setfont(1, "Calibri", 13)
  gfx.setfont(2, "Calibri", 15)
  gfx.setfont(3, "Calibri", 16)
  gfx.setfont(4, "Calibri", 20) -- used in : undocked palette title
  gfx.setfont(5, "Calibri", 16) -- IMPORTANT : match the font and size (by eye, oops!) of TCP & EnvCP labels
  gfx.setfont(11, "Calibri", 26)
  gfx.setfont(12, "Calibri", 30)
  gfx.setfont(13, "Calibri", 32)
  gfx.setfont(14, "Calibri", 40) -- used in : undocked palette title
  gfx.setfont(15, "Calibri", 32)
end


if reaper.LocalizeString ~= nil then
  translate = function(s) return reaper.LocalizeString(s or '---',"Default_6.0_Theme_Adjuster") end
else
  translate = function(s) return s or '---' end
end


function text(str,x,y,w,h,align,col,style,lineSpacing,vCenter,wrap)
  local lineSpace = drawScale*(lineSpacing or 11)
  setCol(col or {255,255,255})
  gfx.setfont(style or 1)

  local lines = nil
  str = translate(str)
  if wrap == true then
    lines = textWrap(str,drawScale * 105)
  else
    lines = {}
    for s in string.gmatch(str, "([^#]+)") do
      table.insert(lines, s)
    end
  end
  if vCenter ~= false and #lines > 1 then
    y = y - lineSpace/2
  end
  for k,v in ipairs(lines) do
    gfx.x, gfx.y = x,y
    gfx.drawstr(v,align or 0,x+(w or 0),y+(h or 0))
    y = y + lineSpace
  end
end

function textWrap(str,w) -- returns array of lines
  local lines,curlen,curline,last_sspace = {}, 0, "", false
  -- enumerate words
  for s in str:gmatch("([^%s-/]*[-/]* ?)") do
    local sspace = false -- set if space was the delimiter
    if s:match(' $') then
      sspace = true
      s = s:sub(1,-2)
    end
    local measure_s = s
    if curlen ~= 0 and last_sspace == true then
      measure_s = " " .. measure_s
    end
    last_sspace = sspace

    local length = gfx.measurestr(measure_s)
    if length > w then
      if curline ~= "" then
        table.insert(lines,curline)
        curline = ""
      end
      curlen = 0
      while length > w do
        -- split up a long word, decimating measure_s as we go
        local wlen = string.len(measure_s) - 1
        while wlen > 0 do
          local sstr = string.format("%s%s",measure_s:sub(1,wlen), wlen>1 and "-" or "")
          local slen = gfx.measurestr(sstr)
          if slen <= w or wlen == 1 then
            table.insert(lines,sstr)
            measure_s = measure_s:sub(wlen+1)
            length = gfx.measurestr(measure_s)
            break
          end
          wlen = wlen - 1
        end
      end
    end
    if measure_s ~= "" then
      if curlen == 0 or curlen + length <= w then
        curline = curline .. measure_s
        curlen = curlen + length
      else
        -- word would not fit, add without leading space and remeasure
        table.insert(lines,curline)
        curline = s
        curlen = gfx.measurestr(s)
      end
    end
  end
  if curline ~= "" then
    table.insert(lines,curline)
  end
  return lines
end

  --------- IMAGES ----------

function loadImage(idx, name)
  local str = debug.getinfo(1, "S").source:match[[^@(.*[\/])[^\/]-$]].."Default_6.0_theme_adjuster_images/"
  if gfx.loadimg(idx, str..name) == -1 then reaper.ShowConsoleMsg("image "..name.." not found") end
end

image_idx,image_idx_size = {},0
function getImage(img,drawScale)
  if drawScale ~= 2 then drawScale = 1 end -- snap to 1 if not 2, for now

  local cache_rec = image_idx[img]
  if cache_rec ~= nil then
    if cache_rec.scale == drawScale then return cache_rec.idx end
  else
    cache_rec = { idx=image_idx_size }
    image_idx[img] = cache_rec
    image_idx_size = image_idx_size + 1
  end
  if drawScale == 2 then img = img .. "@2x" end
  loadImage(cache_rec.idx,img .. ".png")
  cache_rec.scale = drawScale
  return cache_rec.idx
end


  --------- OBJECTS ---------

function adoptChild(parent,o)
  if parent ~= nil then
    if parent.children == nil then parent.children = { o }
    else parent.children[#parent.children+1] = o end

    if parent.has_children_outside ~= 1 then
      if o.has_children_outside == 1 then
        parent.has_children_outside = 1
      else
        if o.x ~= nil and o.y ~= nil and parent.w ~= nil and parent.h ~= nil then
          if o.x < 0 or o.y < 0 or o.x+(o.w or 1) > parent.w or o.y+(o.h or 1) > parent.h then
            parent.has_children_outside = 1
          end
        end
      end
    end
  end
end

Element = {}
function Element:new(parent,o)
local o = o or {}
  self.__index = self
  self.x, self.y = self.x or 0, self.y or 0
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

Button = Element:new()
function Button:new(parent,o)
  self.__index = self
  o.x, o.y, self.w, self.h, self.border = o.x or 0, o.y or 0, o.w or 30,o.h or 30, o.border or ''
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

ButtonLabel = Element:new()
function ButtonLabel:new(parent,o)
  self.__index = self
  self.flow = true
  o.text={str=o.text.str, col={129,137,137}, align=4, style=1}
  self.x, self.h, self.w, self.border = 2, 30, o.w or 73, o.border or ''
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

Readout = Element:new()

Spinner = Element:new()
function Spinner:new(parent,o)
  self.__index = self
  self.x, self.y, self.w, self.h = 0,0,o.w or 119,o.h or 30
  self.flow = o.flow
  self.border = o.border or ''
  local spinStyle = o.spinStyle or 'light'
  local i = getImage(spinStyles[spinStyle].buttonLimage,1)
  self.buttonW = gfx.getimgdim(i) /3
  if spinStyles[spinStyle].title ~= false then
    local topBar = Element:new(o,{x=self.buttonW/2,y=2,w=self.w-self.buttonW,h=spinStyles[spinStyle].title.h,color=spinStyles[spinStyle].label.col,interactive=false})
  end
  if spinStyles[spinStyle].readout ~= false then
    local bottomBar = Element:new(o,{x=self.buttonW/2,y=spinStyles[spinStyle].readout.y,w=self.w-self.buttonW,h=spinStyles[spinStyle].readout.h,color=spinStyles[spinStyle].readout.col,interactive=false})
  end
  if o.spinStyle == 'image' then
    local ir = Readout:new(o,{x=self.buttonW,y=spinStyles[spinStyle].readout.y,w=self.w-(2*self.buttonW),h=spinStyles[spinStyle].readout.h,border='',
                      valsImage = o.valsImage, action = o.action, param={0}})
  else
    if spinStyles[spinStyle].readout ~= false then
      local r = Readout:new(o,{x=self.buttonW,y=spinStyles[spinStyle].readout.y,w=self.w-(2*self.buttonW),h=spinStyles[spinStyle].readout.h,border='',
                  text={str='---',align=5,val=o.value, col=spinStyles[spinStyle].readout.strCol, style=2},
                  action = o.action, param={o.param}, valsTable=o.valsTable})
    end
  end
  local hitBox = Element:new(o,{x=0,y=0,w=self.w,h=self.h,action = o.action, param={o.param},helpR=o.helpR,helpL=o.helpL})
  local l = Button:new(hitBox,{x=0,y=2,w=self.buttonW,h=self.h,img=spinStyles[spinStyle].buttonLimage,imgType=3,action=o.action,param={o.param,-1},helpR=o.helpR,helpL=o.helpL})
  local r = Button:new(hitBox,{x=self.w-self.buttonW,y=2,w=self.buttonW,h=self.h,img=spinStyles[spinStyle].buttonRimage,imgType=3,action=o.action,param={o.param,1},helpR=o.helpR,helpL=o.helpL})
  if o.title~=nil and spinStyles[spinStyle].title ~= false then
    Element:new(o,{x=self.buttonW,y=2,w=self.w-(2*self.buttonW),h=spinStyles[spinStyle].title.h,action=o.action,param={o.param}, -- label
                  text={str=(o.title or'LABEL STR'), align = 5, col=spinStyles[spinStyle].label.strCol}})
  end
  o.valsImage = nil -- was only there temporarily, to be passed to the readout child
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

spinStyles = {
  light = {buttonLimage = 'button_left', buttonRimage = 'button_right',
          title = {h=13},
          label = {strCol={51,51,51}, col={129,137,137}},
          readout = {y=15,h=13,strCol={129,137,137},col=nil}
  },
  image = {buttonLimage = 'left', buttonRimage = 'right', title = false,
          readout = {y=6,h=20,strCol={129,137,137},col=nil}
  }
}

Fader = Element:new()
function Fader:new(parent,o)
  o.parent = parent;
  self.__index = self
  o.x, o.y, self.w, self.h = o.x or 0, o.y or 0, o.w or 21,o.h or 27
  self.img, self.imgType ='slider', 3
  self.range, self.action, self.param, self.helpR = o.range, o.action, o.param, o.helpR
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

FaderBg = Element:new()
function FaderBg:new(parent,o)
  self.__index = self
  o.x, o.y, self.w, self.h = o.x or 0, o.y or 0, o.w or 21,o.h or 27
  o.parent = parent
  self.action, self.param, self.helpR = o.action, o.param, o.helpR
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

ParamTable = Element:new()
function ParamTable:new(parent,o)
  self.__index = self
  self.h = o.h
  for i, v in ipairs(o.valsTable.columns) do
    if v.text.col ~= nil then thisCol = {204,69,114} else thisCol = {129,137,137} end
    Element:new(o, {x=44+i*82,y=0,w=80,h=25,text={str=v.text.str,style=1,align=1,col=thisCol}}) --column titles
  end
  for i=1, #o.valsTable.rows do ParamRow:new(o,o.valsTable,i) end
  Element:new(o, {x=124,y=0,w=1,h=o.h,color={129,137,137,64}}) --
  Element:new(o, {x=206,y=0,w=1,h=o.h,color={129,137,137,64}}) -- column
  Element:new(o, {x=288,y=0,w=1,h=o.h,color={129,137,137,64}}) -- dividers
  Element:new(o, {x=370,y=0,w=1,h=o.h,color={129,137,137,64}}) --
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

ParamRow = Element:new()
function ParamRow:new(parent,valsTable,rowIdx)
  local o = {}
  self.__index = self
  if (rowIdx%2==0) then rowBgCol = {51,51,51} else rowBgCol = {44,44,44} end
  local row = Element:new(parent, {x=0,y=rowIdx*25+5,w=453,h=25,color=rowBgCol})
  local titleW = 114
  if valsTable.rows[rowIdx].img ~= nil then
    Element:new(row, {x=91,y=0,w=23,h=25,img=valsTable.rows[rowIdx].img}) --row title images
    titleW = 80
  end
  Element:new(row, {x=0,y=0,w=titleW,h=25,text={str=valsTable.rows[rowIdx].text.str,style=1,align=6,col={129,137,137}}})  --row titles
  for i, v in ipairs(valsTable.columns) do
    Button:new(row, {x=44+i*82,y=0,w=81,h=25,img=valsTable.img,imgType=3,action=doFlagParam,param={valsTable.rows[rowIdx].param,v.visFlag}})
  end
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

Palette = Element:new()
function Palette:new(parent,o)
  o.w = o.cellW * 10
  self.__index = self
  for i=1,10 do
    local p = Button:new(o,{flow=true,x=0,y=0,w=o.cellW, h=o.h, img=o.img or 'color_apply',imgType=3, action=o.action}) -- used to be x=2 to make the dividers
  end
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

Swatch = Element:new()
function Swatch:new(parent,o)
  self.__index = self
  self.x, self.y, self.w, self.h = o.x or 0,o.y or 0,200,30
  local SwatchHitbox = SwatchHitbox:new(o, {paletteIdx = o.paletteIdx})
  for i,v in pairs(palette[palette.idx[o.paletteIdx] or 'Rv6']) do
    local p = Element:new(o,{x=((i-1)*20),y=0,w=20, h=20,color=v})
  end
  local div = Element:new(o, {x=0,y=30,w=200,h=1})
  gfx.setfont(2)
  local palStr,tmp = undockPaletteNamesVals[o.paletteIdx]
  local palStrW = gfx.measurestr(palStr)+12
  local label = Element:new(o, {x=100-(palStrW/2),y=20,w=palStrW,h=17,text={str=palStr,style=2,align=9},color={38,38,38}})
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

SwatchHitbox = Element:new()
function SwatchHitbox:new(parent,o)
  o.parent = parent;
  self.__index = self
  self.x, self.y, self.w, self.h = 0,0,200,34
  self.helpR=helpR_choosePalette
  adoptChild(parent,o)
  setmetatable(o, self)
  return o
end

function Swatch:doParamGet()
  if self.paletteIdx == palette.current then
    self.children[12].color = {19,189,153}
    self.children[13].text.col = {19,189,153}
  else
    self.children[12].color = {51,51,51}
    self.children[13].text.col = {129,137,137}
  end
end

function Fader:doParamGet()
  local lx = self.x;
  local tmp,title,value,defValue,min,max = reaper.ThemeLayout_GetParameter(self.param)
  if max > min then
    self.x = 432 * ((value - min) / (max - min))
  else
    self.x = 432/2
  end
  if lx ~= self.x then
    self.parent:onSize()
    redraw = 1
  end
end

-------------- PARAMS --------------

function indexParams()
  
  paramsIdx ={['A']={},['B']={},['C']={},['global']={}}
  local i=1
  while reaper.ThemeLayout_GetParameter(i) ~= nil do
    local tmp,desc = reaper.ThemeLayout_GetParameter(i)
    local layout, paramDesc = string.sub(desc, 1, 1), string.sub(desc, 3)
    if paramsIdx[layout] ~= nil then
      paramsIdx[layout][paramDesc] = i
    end
    i = i+1
  end
  redraw = 1
end

function paramIdxGet(param)
  if paramsIdx == nil then reaper.ShowConsoleMsg('paramsIdx is nil\n') end
  local panel = param and string.sub(param,0,(string.find(param, '%_')-1))
  if param == 'tcp_indent' or param == 'tcp_control_align' or param == 'mcp_indent' or param == 'tcp_LabelMeasure'
      or panel == 'envcp' or panel == 'trans' or panel == 'glb' then --params which act on ALL layouts
    local p = paramsIdx['A'][param]
    if p ~= nil then return p end
  else
    if panel ~= nil and param ~= nil then
      local p = paramsIdx[activeLayout[panel]][param]
      if p ~= nil then return p end
    end
  end
end

function paramToVal(param,v)
  local val,suffix
  if param == -1000 then val, suffix = v / 1000, '' end
  if param <= -1001 and param >= -1003 then val, suffix = v / 256, '' end
  if param == -1004 then val, suffix = math.floor(v / 2.56 + .5), ' %' end
  if param == -1005 then val, suffix = math.floor(v * 0.9375 - 180 + .5),' °' end
  if param >= 0 then val, suffix = v, '' end
  return val, suffix
end

function valToParam(param,v)
  local val
  if param == -1000 then val = v * 1000 end
  if param <= -1001 and param >= -1003 then val = v * 256 end
  if param == -1004 then val = math.floor(v * 2.56 + .5) end
  if param == -1005 then val = math.floor((v + 180) / 0.9375 + .5) end
  if param >= 0 then val = v end
  return val
end

function Element:doParamGet()
  if self.visible ~= false then paramGetChildren(self.children) end
end

function paramGetChildren(ch)
  if ch ~= ni then
    for i, v in ipairs(ch) do
      v:doParamGet() --get all the param values for your children.
    end
  end
end

function Button:doParamGet()
  if self.action == paramToggle then                          -- then you're a toggle state
    if type(self.param) ~= 'number' then
      self.param = paramIdxGet(self.param)
    end
    local tmp,tmp,v = reaper.ThemeLayout_GetParameter(self.param or -1)
    if v == 1 then
      self.drawImg = tostring(self.img..'_on')
    else self.drawImg = nil
    end
  end
  if self.action == doFlagParam  then                         --param table cells
    local p = paramIdxGet(self.param[1])
    local name,desc,value = reaper.ThemeLayout_GetParameter(p)
    if value & self.param[2] ~= 0 then
      if self.param[2] == 8 and self.img == 'cell_hide' then    -- use red hide images on column 4 of the tcp's table
       self.drawImg = tostring(self.img..'_all')
      else
        self.drawImg = tostring(self.img..'_on')
      end
    else self.drawImg = nil
    end
  end
end

function Readout:doParamGet()
  if self.param ~= nil and self.param[1]~= nil then
    if self.valsTable ~= nil then
      if self.action == nil then -- then you're just a palette
        self.text.str = self.valsTable[palette.current]
      else -- if you're not a palette you must be a paramSet spinner
        local p = paramIdxGet(self.param[1])
        local tmp,tmp,value = reaper.ThemeLayout_GetParameter(p or 0)
        if value <= #self.valsTable then
          self.text.str = self.valsTable[value]
        else self.text.str = 'ERR '..p..' '..value
        end
      end
    elseif self.action == doPageSpin then self.imgValueFrame = getEditPageIndex()-1
    elseif self.action == doFader then
      local tmp,tmp,value = reaper.ThemeLayout_GetParameter(self.param[1]) --< color faders have param as number, no need to lookup 
      local v, suffix = paramToVal(self.param[1],value)
      self.text.str = string.format(suffix == "" and "%.2f" or "%d%s",v,suffix);
    elseif self.action == doGenericFader then
      local tmp,desc,value = reaper.ThemeLayout_GetParameter(self.param[1])
      if tmp ~= nil then
        if self.userEntry ~= nil then
          self.text.str = value
        else
          self.text.str = desc
        end
      end
    end
  end
end

function paramSet(param)
  local p,v = param[1], param[2]
  --reaper.ShowConsoleMsg('paramSet '..p..' to '..v..'\n')
  if type(p) ~= 'number' then p = paramIdxGet(p) or 0 end
  local name,desc,value,defvalue,minvalue,maxvalue = reaper.ThemeLayout_GetParameter(p)
  newValue = value + v
  if newValue < minvalue then newValue = minvalue end
  if newValue > maxvalue then newValue = maxvalue end
  reaper.ThemeLayout_SetParameter(p, newValue, true)
  reaper.ThemeLayout_RefreshAll()
  paramGet = 1
end

function doFlagParam(param) --param name, visFlag
  local p = paramIdxGet(param[1])
  local name,desc,value = reaper.ThemeLayout_GetParameter(p)
  reaper.ThemeLayout_SetParameter(p, value ~ param[2], true)
  reaper.ThemeLayout_RefreshAll()
  paramGet = 1
end

function doGenericParams()
  for i=2,#_themeParameterPage_und.children do
    if reaper.ThemeLayout_GetParameter(i-1) ~= nil then _themeParameterPage_und.children[i].visible = true
    else _themeParameterPage_und.children[i].visible = false
    end
  end
end

function paramToggle(p)
  if p ~= nil then
    local tmp,tmp,v = reaper.ThemeLayout_GetParameter(p)
    if v == 1 then
      reaper.ThemeLayout_SetParameter(p, 0, true)
    else
      reaper.ThemeLayout_SetParameter(p, 1, true)
    end
    reaper.ThemeLayout_RefreshAll()
  end
end

function actionToggle(p)
  reaper.Main_OnCommand(p, 0)
  needReaperStateUpdate=1
end

------------ doUpdateState gets/refreshes values. it should set redraw if a value changed. 
------------ needReaperStateUpdate is 1 if the project has changed (implies redraw)

function Element:doUpdateState()
  if self.updateState ~= nil then
    self:updateState()
  end

  if self.children ~= nil then
    for i, v in ipairs(self.children) do
      if v.visible ~= false then v:doUpdateState() end
    end
  end
end

function Button:doUpdateState()

  if self.updateState ~= nil then   --<< swap over to this for image buttons
    self:updateState()
  end

  local old = self.drawImg
  if self.action == actionToggle then
    local v = reaper.GetToggleCommandState(self.param)
    if v == 1 then
      self.drawImg = tostring(self.img..'_on')
    else self.drawImg = nil
    end
  end
  if self.action == doActiveLayout then
    local p, a = 'P_TCP_LAYOUT', ''
    if self.param[1] == 'mcp' then p = 'P_MCP_LAYOUT' end
    if self.param[2] == activeLayout[self.param[1]] then a = '_on' end  --you are the button for the active layout

    self.drawImg = nil
    if a ~= nil then self.drawImg = self.img..a end
  end
  if self.children ~= nil then
    for i, v in ipairs(self.children) do
      if v.visible ~= false then v:doUpdateState() end
    end
  end
  if old ~= self.drawImg then redraw = 1 end
end

function Fader:doUpdateState()
  if self.updateState ~= nil then
    self:updateState()
  end
  local tmp,title,value,defValue,min,max = reaper.ThemeLayout_GetParameter(self.param)
  local lx = self.x;
  if self.dragStart ~= nil then
    local dX = gfx.mouse_x - self.dragStart
    local v = math.floor(dX * ((max - min)/(432 * drawScale)))
    newValue = self.dragStartValue + v
    if newValue < min then newValue = min end
    if newValue > max then newValue = max end
    if max > min then
      self.x = 432 * ((newValue - min) / (max - min))
    else
      self.x = 432/2
    end
  else
    self:doParamGet()
  end
end

function Readout:doUpdateState()
  if self.updateState ~= nil then
    self:updateState()
  end
  self:doParamGet()
end

function anySelected(self) -- called by an object's updateState value, and uses its getParam values.
  if self.text ~= nil and self.text.colFalse ~= nil and needReaperStateUpdate==1 then
    local p  = 'P_TCP_LAYOUT'
    if self.getParam[1] == 'mcp' then p = 'P_MCP_LAYOUT' end --( NOTE :: param[1] tcp or mcp, param[2] A,B or C.)
    self.text.col = self.text.colFalse
    for i=0, reaper.CountTracks(0)-1 do
      local track = reaper.GetTrack(0, i)
      if reaper.IsTrackSelected(track) == true then
        local tmp, l = reaper.GetSetMediaTrackInfo_String(track, p, "", false)
        if string.sub(l,-1) == self.getParam[2] then -- a selected track is using this layout
          self.text.col = self.text.colTrue
          break
        end
      end
    end
  end
end

function noneSelected(self)
  if needReaperStateUpdate == 1 then
    local trackCount = reaper.CountTracks(0)-1
    local noneSelected = true
    for i=0, trackCount do
      if reaper.IsTrackSelected(reaper.GetTrack(0, i)) == true then
        noneSelected = false
        break
      end
    end
    if self.imgFalse ~= nil then
      if noneSelected == true then self.img = self.imgTrue else self.img = self.imgFalse end
    end
  end
end

function isDefault(self)
  local tmp, def = reaper.ThemeLayout_GetLayout(self.getParam[1], -1)
  if self.text ~= nil then
    if self.text.colFalse ~= nil then
      local oldcol = self.text.col
      if def == self.getParam[2] or (def == '' and self.getParam[2] == 'A') then
        self.text.col = self.text.colTrue
      else
        self.text.col = self.text.colFalse
      end
      if oldcol ~= self.text.col then redraw = 1 end
    end
  end
end

function read_ini(file, sec, ent)
  local insec, str, section = false, string.lower(ent), string.lower(sec)
  for l in io.lines(file) do
    local m = string.match(l,"^%s*[[](.-)[]]")
    if m ~= nil then
      insec = section == string.lower(m)
    else
      if insec then
        local a = string.match(l,"^%s*(.-)=")
        if a ~= nil and str == string.lower(a) then
          return string.match(l,"^.-=(.*)")
        end
      end
    end
  end
end

reaperDpi = {'tcp','mcp','envcp','trans'}
dpiParams = {{'apply_50','50%_'},{'apply_75','75%_'},{'apply_100',''},{'apply_150','150%_'},{'apply_200','200%_'}}
function getReaperDpi()

  for i, v in ipairs(reaperDpi) do
    local ok, dpi_str = reaper.ThemeLayout_GetLayout(v,-3)
    if reaperDpi[v] == nil then reaperDpi[v] = {} end
    local dpi = tonumber(dpi_str)
    if ok == true and dpi ~= nil and dpi > 0 then
      reaperDpi[v].new = dpi / 256
    else
      reaperDpi[v].new = 1.0
    end

    local p = {3,4,5}
    if reaperDpi[v].old == nil or reaperDpi[v].old ~= reaperDpi[v].new then
      if reaperDpi[v].new > 1.34 then
        p = {2,3,4}
        if reaperDpi[v].new > 1.74 then p = {1,2,3} end
      end
      for i=1,3 do
        if apply[v][i]==nil then apply[v][i] = {} end
        apply[v][i].img, apply[v][i].param = dpiParams[p[i]][1], {v,dpiParams[p[i]][2]}
        apply.und[v][i].img, apply.und[v][i].param = apply[v][i].img, apply[v][i].param
      end
      reaperDpi[v].old = reaperDpi[v].new
      redraw = true
    end
  end
end

function measureTrackNames(trackCount)
  local nameChanged = 0
  for i=0, trackCount do
    local tmp, trackName = reaper.GetSetMediaTrackInfo_String(reaper.GetTrack(0, i), 'P_NAME', "", false)
    if (trackNames[i] ~= trackName) then -- track name has changed
      trackNames[i] = trackName
      gfx.setfont(5)
      trackNamesW[i] = gfx.measurestr(trackName)
      nameChanged = 1
      redraw = 1
    end
  end
  if nameChanged == 1 then
    local trackNamesWMax = 25 -- setting a minimum size
    for k,v in pairs(trackNamesW) do
      if v > trackNamesWMax then trackNamesWMax = v end
    end
    local p = paramIdxGet('tcp_LabelMeasure')
    if p ~= nil then
      reaper.ThemeLayout_SetParameter(p, trackNamesWMax, true)
      reaper.ThemeLayout_RefreshAll()
    end
  end
end

function measureEnvNames(trackCount)
  for i=0, trackCount do
    local tr = reaper.GetTrack(0, i)
    local trEnvs = reaper.CountTrackEnvelopes(tr)
    if trEnvs > 0 then
      if envs[i] == nil then
        envs[i] = {}
      end
      while #envs[i] > trEnvs do
        table.remove(envs[i])
        redraw = 1
      end
    end
    for j=0, trEnvs-1 do
      local env = reaper.GetTrackEnvelope(tr,j)
      local b, envName = reaper.GetEnvelopeName(env,'')
      gfx.setfont(5)
      if b == true then
        if envs[i][j+1] ~= nil then
          if envs[i][j+1][name] ~= envName then -- env name has changed
            envs[i][j+1] = {name = envName, l = gfx.measurestr(envName)}
            redraw = 1
          end
        else envs[i][j+1] = {name = envName, l = gfx.measurestr(envName)}
        end
      end
    end
  end
  local envNamesWMax = 100
  for k,v in pairs(envs) do
    if v ~= nil then
      for kk,vv in pairs(v) do
        if vv.l > envNamesWMax then envNamesWMax = vv.l end
      end
    end
  end
  local l = paramIdxGet('envcp_LabelMeasure');
  if l ~= nil then
    reaper.ThemeLayout_SetParameter(l, envNamesWMax, true)
  end
end

------------- ACTIONS --------------

function toggleHelp()
  if _helpR.visible ~= false then doHelpVis(false) else doHelpVis(true) end
end

function doHelpVis(visible)
  if visible == nil then
    if reaper.GetExtState(sTitle,'showHelp') == 'false' then visible = false else visible = true end
  end
  _helpL.visible, _helpR.visible = visible, visible
  _gfxw,_gfxh = table.unpack(_desired_sizes[visible == true and 2 or 1])
  _buttonHelp.img = visible == true and 'help_on' or 'help'
  reaper.SetExtState(sTitle,'showHelp',tostring(visible),true)
  getDpi()
  if _dockedRoot.visible ~= true then
    _gfxw,_gfxh = table.unpack(_desired_sizes[_helpL.visible == true and 2 or 1])
    _gfxw,_gfxh = drawScale_nonmac*_gfxw,drawScale_nonmac*_gfxh
    gfx.init("",_gfxw,_gfxh)
  end
end

function themeCheck()
  local theme,tmp,tmp,theme_version = reaper.ThemeLayout_GetParameter(0)
  if theme ~= oldTheme or theme == nil then
    last_theme_filename = reaper.GetLastColorThemeFile()
    last_theme_filename_check = reaper.time_precise()
    indexParams()
    getDock() --it will decide which root to draw
    if theme ~= 'defaultV6' or theme_version < 1 then
      isGenericTheme = true
      _genericThemeTitle.text.str = string.match(string.match(last_theme_filename, '[^\\/]*$'),'(.*)%..*$')
    else
      isGenericTheme = false
    end
    oldTheme = theme
    doActivePage()
    paramGet = 1
    redraw = 1
  else
    local now = reaper.time_precise()
    if now > last_theme_filename_check+1 then
      -- once per second see if the theme filename changed and reload parameters
      last_theme_filename_check = now
      local tfn = reaper.GetLastColorThemeFile()
      if tfn ~= last_theme_filename then
        last_theme_filename = tfn
        paramGet = 1
        redraw = 1
      end
    end
  end
end

function switchTheme()
  local str = string.match(reaper.GetLastColorThemeFile(), '^(.*)[/\\].+$')
  if(reaper.file_exists(str.."/Default_6.0_unpacked.ReaperTheme")==true) then
    openTheme = reaper.OpenColorThemeFile(str.."/Default_6.0_unpacked.ReaperTheme")
  else if(reaper.file_exists(str.."/Default_6.0.ReaperThemeZip")==true) then
    openTheme = reaper.OpenColorThemeFile(str.."/Default_6.0.ReaperThemeZip")
    else reaper.ShowConsoleMsg("Default 6.0 theme not found")
    end
  end
  indexParams()
  redraw = 1
end

function doDock()
  local d = gfx.dock(-1)
  if d%2==0 then
    gfx.dock(d+1)
    _dockedRoot.visible, _undockedRoot.visible = true, false
  else
    gfx.dock(d-1)
    _dockedRoot.visible, _undockedRoot.visible = false, true
  end
  doActivePage()
  resize = 1
  paramGet = 1
end

function getDock()
 local d = gfx.dock(-1)
  if d%2==0 then
    _dockedRoot.visible, _undockedRoot.visible = false, true
  else _dockedRoot.visible, _undockedRoot.visible = true, false
  end
end

function getDpi()
  local newScale, os = 1, reaper.GetOS()
  if gfx.ext_retina>1.49 then newScale = 2 end

  if os ~= "OSX64" and os ~= "OSX32" and os ~= "macOS-arm64" then
    -- disable (non-macOS) hidpi if window is constrained in height or width
    local minw, minh = 500, 660
    if _dockedRoot.visible ~= false then  minw, minh = 400, 24 end

    if gfx.h < minh*newScale or gfx.w < minw*newScale then newScale = 1 end
    drawScale_nonmac = newScale
    drawScale_inv_nonmac = 1/newScale
  else
    drawScale_inv_mac = 1/newScale
  end

  if newScale ~= drawScale then
    drawScale = newScale
    resize = 1
  end
end

function getEditPageIndex()
  if isGenericTheme == true then
    if editPage2 == 2 then return 4 end
    if editPage2 == 3 then return 6 end
    return 1
  end
  if editPage<1 or editPage>6 then return 1 end
  return editPage
end

function doActivePage()
  local ep = getEditPageIndex()
  if _dockedRoot.visible ~= false then
    for i, v in ipairs(_dockedRoot.children) do
      if i>0 and i<7 then -- ignore the last child (the undock button)
        if i == ep then v.visible = true
        else v.visible = false end
      end
    end
  end

  if _undockedRoot.visible ~= false then
    if isGenericTheme == false and ep == 6 then ep = 5 end
    for i, v in ipairs(_subPageContainer.children) do
      if i>0 and i<=(#_subPageContainer.children) then
        if i == ep then v.visible = true
        else v.visible = false
        end
      end
    end
  end
  resize = 1
end

function doPageSpin(param)
  local val = param[2]

  if val == 0 then return end
  if val > 0 then val = 1 else val = -1 end -- one at a time

  local ep, limit
  if isGenericTheme == true then
    limit = 2
    ep = editPage2
    if _undockedRoot.visible == true then limit = 3 end
  else
    limit = 6
    ep = editPage
    if _undockedRoot.visible == true then limit = 5 end
  end
  
  if ep>=limit and val==1 then
    ep = 1
  else
    if ep==1 and val==-1 then ep = limit
    else ep = ep + val
    end
  end
  if isGenericTheme == true then
    editPage2 = ep
  else
    editPage = ep
  end

  doActivePage()
  needReaperStateUpdate = 1
  paramGet = 1
  root:onSize()
  redraw = 1
end

function doActiveLayout(param)
  function isLayoutName(n)
    if n ~= nil and (n == 'A' or n == 'B' or n == 'C') then return n end
  end
  if param ~= nil then
    if isLayoutName(param[2]) then
      activeLayout[param[1]] = param[2]
    end
  else
    activeLayout = {
      tcp = isLayoutName(reaper.GetExtState(sTitle,'activeLayoutTcp')) or 'A',
      mcp = isLayoutName(reaper.GetExtState(sTitle,'activeLayoutMcp')) or 'A'
    }
  end
  paramGet = 1
  redraw = 1
end

function applyLayout(param) --panel, size
  if param[1] == 'envcp' or param[1] == 'trans' then
    reaper.ThemeLayout_SetLayout(param[1], param[2]..'A')
  else
    local p =  'P_TCP_LAYOUT'
    if param[1] == 'mcp' then p = 'P_MCP_LAYOUT' end
    for i=0, reaper.CountTracks(0)-1 do
      local tr = reaper.GetTrack(0, i)
      if reaper.IsTrackSelected(tr) == true then
        reaper.GetSetMediaTrackInfo_String(tr, p, param[2]..tostring(activeLayout[param[1]]), true)
      end
    end
  end
end

function reduceCustCol(ifSelected)
  local ratio = 0.4
  local targetR, targetG, targetB = 84,84,84
  reaper.Undo_BeginBlock()
  for i=0, reaper.CountTracks(0)-1 do
    local selState = false
    if ifSelected == true then
      if reaper.IsTrackSelected(reaper.GetTrack(0, i)) == true then
        selState = true
      end
    end
    if (ifSelected ~= true or selState == true) and getCustCol(i)~=nil then
      local r,g,b = getCustCol(i)
      r = math.floor(r * (1-ratio) + targetR * ratio)
      g = math.floor(g * (1-ratio) + targetG * ratio)
      b = math.floor(b * (1-ratio) + targetB * ratio)
      setCustCol(i,r,g,b)
    end
  end
  reaper.Undo_EndBlock('Dimming of custom colors',-1)
end

function resetColorControls()
  for i=-1005,-1000,1 do
    local tmp,tmp,tmp,d = reaper.ThemeLayout_GetParameter(i)
    reaper.ThemeLayout_SetParameter(i, d, i == -1000)
  end
  paramGet = 1
  redraw = 1
end

function doFader(self,dX)
  if self.userEntry == true then --< the fader's readout
    local name,desc,value,defvalue,minvalue,maxvalue = reaper.ThemeLayout_GetParameter(self.param[1])
    local vValMin, vValMinSuffix = paramToVal(self.param[1],minvalue)
    local vValMax, vValMaxSuffix = paramToVal(self.param[1],maxvalue)
    local r,v = reaper.GetUserInputs(desc, 1, vValMin..vValMinSuffix..' to '..vValMax..vValMinSuffix, self.text.str)
    local val = tonumber(v:match("[-]?[%d.,]+"))
    if r ~= false and val ~= nil then
      local tmp,tmp,tmp,tmp,minvalue,maxvalue = reaper.ThemeLayout_GetParameter(self.param[1])
      val = math.floor(valToParam(self.param[1],val))
      if val < minvalue then val = minvalue end
      if val > maxvalue then val = maxvalue end
      reaper.ThemeLayout_SetParameter(self.param[1], val, true)
      paramGet = 1
      redraw = 1
    end
  else --see fader:mouseDown 
  end
end

function doGenericFader(self,dX)
  doFader(self,dX)
  reaper.ThemeLayout_RefreshAll()
end


--------- ARRANGE ELEMENTS ---------

function Element:onSize()

  local crop = self.crop or false
  if self.children ~= nil and self.visible ~= false and self.crop ~= true then
    for i, v in ipairs(self.children) do

      local bx,by = 0,0
      if v.border == 'xy' or v.border == 'x' then bx = globalBorderX end
      if v.border == 'xy' or v.border == 'y' then by = globalBorderY end

      local prevElX, prevElY, prevElW, prevElH = 0,0,0,0
      if i>1 then --there is a previous child
        prevElX, prevElY, prevElW, prevElH = self.children[i-1].drawx, self.children[i-1].drawy, self.children[i-1].drawW, self.children[i-1].drawH
        if self.children[i-1].visible == false then prevElW = 0 end
      end

      if v.flexW ~= nil then
        if v.flexW == 'fill' then
          v.drawW = self.drawx + (self.drawW or self.w) - ((prevElX or self.drawx) + (prevElW or 0)) + (v.w or 0)
        else v.drawW = (v.w or 0) + ((self.drawW or self.w) * (v.flexW:sub(1, -2) / 100))
        end
      else v.drawW = v.w
      end

      if v.flexH ~= nil then v.drawH = (v.h or 0) + ((self.drawH or self.h) * (v.flexH:sub(1, -2) / 100))
      else v.drawH = v.h end

      v:position(self.drawx,self.drawy,self.drawW,self.drawH,prevElX, prevElY, prevElW, prevElH, bx, by)
      v:onSize() -- this child sizes its children

    end
  end
end

function Element:position(parentX,parentY,parentW,parentH,prevElX, prevElY, prevElW, prevElH, bx, by)

  if parentX == nil then parentX = 0 end
  if parentY == nil then parentY = 0 end
  if parentW == nil then parentW = 0 end
  if parentH == nil then parentH = 0 end
  self.drawx, self.drawy = parentX  + self.x + bx, parentY + self.y + by

  if self.positionX ~= nil then
    if self.positionX == 'center' then
      parentW = parentW * drawScale_inv_nonmac
      self.drawx = (parentW - self.drawW)/2
    elseif self.positionX == 'right' then
      self.drawx = parentW - self.w
    end
  end
  if self.positionY ~= nil then
    if self.positionY == 'center' then
      parentH = parentH * drawScale_inv_nonmac
      self.drawy = math.max((parentH - self.drawH)/2,0)
    end
  end
  if self.flow ~= nil and self.flow ~= false then
    if prevElX == nil or prevElW == 0 then                                                  -- you're the first child
      self.drawx, self.drawy = parentX + self.x + bx, parentY + self.y + by
      self.crop = false
      if (parentX + parentW) < (self.drawx + self.drawW + bx) then
        self.crop = true
      end
    else                                                                    -- there is a previous child
      if (prevElX + prevElW + bx + self.drawW + bx) <= (parentX + parentW) then           -- place you as next element
        self.drawx, self.drawy = prevElX + prevElW + bx + self.x, prevElY + self.y
        self.debug = 'prevElX : '..prevElX..'    prevElW : '..prevElW..'    prevElY : '..prevElY
        self.crop = false
      elseif (parentY + parentH) > (prevElY + prevElH + self.y + self.drawH + by) then    -- flow you to next row
        self.drawx, self.drawy = parentX + self.x + globalBorderX, prevElY + prevElH + self.y + globalBorderY
        self.debug = 'FLOW! prevElX : '..prevElX..'    prevElW : '..prevElW..'    prevElY : '..prevElY
        self.crop = false
       else
        self.crop = true                                                          -- don't fit, crop you
      end
    end

  end
  self.crop = false

end


  ---------- DRAW -----------

function Element:draw()

  gfx.set(0,0,0,1) -- opacity reset
  local crop = self.crop or false
  if self.debug == true then debugTable(self) end
  if self.visible ~= false and crop ~= true then
    local thisX = drawScale * (self.drawx or self.x)
    local thisY = drawScale * (self.drawy or self.y)
    local thisW = drawScale * (self.drawW or self.w or 0)
    local thisH = drawScale * (self.drawH or self.h or 0)
    local thisDrawW = drawScale * (self.drawW or 0)
    local thisDrawH = drawScale * (self.drawH or 0)

    local thisCol = self.drawColor or self.color or nil
    if thisCol ~= nil then
      setCol(thisCol)
      if self.shape == nil then
        gfx.rect(thisX,thisY,thisDrawW,thisDrawH)
      else 
        local r = thisDrawW/2
        gfx.circle(thisX+r,thisY+r,r,true)
      end
    end

    if self.img ~= nil or  self.valsImage ~= nil then
      local img = self.img or self.valsImage
      if self.drawImg ~= nil then -- then this element's image isn't static
        img = self.drawImg
      end
      local i = getImage(img,drawScale)
      local iDw, iDh = gfx.getimgdim(i)

      if self.imgType ~= nil and iDw ~= nil and self.imgType == 3 then
        local yOffset = 0
        if thisW ~= iDw/3 then -- width stretching needed
          local pad = drawScale * 10
          gfx.blit(i, 1, 0, (iDw/3)*(self.imgFrame or 0), 0, pad, iDh, thisX, thisY, pad, iDh)
          gfx.blit(i, 1, 0, ((iDw/3)*(self.imgFrame or 0))+ pad, 0, (iDw / 3) -(2*pad), iDh, thisX+pad, thisY, thisW-(2*pad), iDh)
          gfx.blit(i, 1, 0, (iDw/3)*(self.imgFrame or 0) + (iDw/3 -pad), 0, pad, iDh, thisX + thisW-pad, thisY, pad, iDh)
        else
          gfx.blit(i, 1, 0, (iDw/3)*(self.imgFrame or 0), 0, iDw / 3, iDh, thisX, thisY, iDw / 3, iDh)
        end
      elseif self.valsImage ~= nil then
        gfx.blit(i, 1, 0, thisW*(self.imgValueFrame or 0), 0, thisW, iDh, thisX, thisY, thisW, iDh)
      else
        gfx.blit(i, 1, 0, 0, 0, iDw, iDh, thisX, thisY, thisDrawW, thisDrawH)
      end

    end

    if self.text ~= nil then
      if self.text.val ~=nil then
        self.text.str = self.text.val()
      end
    local txtScaleOffs = ''
    if drawScale == 2 then txtScaleOffs = 1 end
      local tx,tw = thisX + (drawScale*textPadding), thisW - 2*(drawScale*textPadding)
      text(self.text.str,tx,thisY,tw,thisDrawH,self.text.align,self.text.col,txtScaleOffs..(self.text.style or 1),self.text.lineSpacing,self.text.vCenter,self.text.wrap)
    end

    drawChildren(self.children)
  end
end

function Palette:draw()
  local crop = self.crop or false
  if self.visible ~= false and crop ~= true then
    local p = getCurrentPalette()
    for i, v in ipairs(self.children) do
      v.color = palette[p][i]
      v.param = palette[p][i]
    end
    drawChildren(self.children)
  end
end

function drawChildren(ch)
  if ch ~= nil then
    for i, v in ipairs(ch) do
      v:draw() -- this box draws its children
    end
  end
end

  --------- MOUSE ---------
  
function Element:mouseOver()
  if self.moColor ~= nil then 
    self.moColorOff, self.color = self.col, self.moColor
    redraw = 1
  end
end

function Element:mouseAway()
  if self.imgFrame ~= nil then
    self.imgFrame = 0
  end
  if self.moColor ~= nil then
    self.color, self.moColorOff = self.moColorOff, nil
  end
  _helpL.y, _helpR.y = 10000,10000
  redraw = 1
end

function Element:mouseDown(x,y) end 
function Element:mouseUp(x,y) end
function Element:doubleClick() end

function Element:mouseWheel(v)
  if self.action ~= nil and type(self.param) == "table" then
    self.action({self.param[1],v})
    root:doUpdateState() -- doing a complete root:doUpdateState because other buttons might have changed state as a result of my actions.
    root:doParamGet()
    redraw = 1
  end
end

function Button:mouseOver()
  if self.img ~= nil then
    if self.imgType ~= nil and self.imgType == 3 and self.imgFrame ~= 1 then
      self.imgFrame = 1
      redraw = 1
    end
  end
end

function Fader:mouseOver()
  if self.img ~= nil then
    if self.imgType ~= nil and self.imgType == 3 and self.imgFrame ~= 1 then
      self.imgFrame = 1
      redraw = 1
    end
  end
end

function Fader:mouseDown(x,y)
  local name,desc,value,defvalue,minvalue,maxvalue = reaper.ThemeLayout_GetParameter(self.param)
  if self.dragStart == nil then
    self.dragStart = x
    self.dragStartValue = value
  end
  local dX = x - self.dragStart
  
  if dX ~= 0 then
    local v = math.floor(dX * ((maxvalue - minvalue)/(432 * drawScale)))
    local newValue = math.max(math.min(self.dragStartValue + v,maxvalue),minvalue)
    if newValue ~= value then
      reaper.ThemeLayout_SetParameter(self.param, newValue,false)
      ctheme_param_needsave = { self.param }
      self:doUpdateState()
      self.parent:onSize()
      if self.onChange ~= nil then self:onChange() end
      if self.readout ~= nil then self.readout:doParamGet() end
      redraw = 1
    end
  end
end

function FaderBg:mouseDown(x,y)
  local tmp,tmp,value,tmp,minvalue,maxvalue = reaper.ThemeLayout_GetParameter(self.param)
  local v = minvalue + math.floor(((x/drawScale-self.drawx-10)/432)*(maxvalue-minvalue))
  v = math.max(math.min(v,maxvalue),minvalue)
  if v ~= value then
    reaper.ThemeLayout_SetParameter(self.param,v,false)
    ctheme_param_needsave = { self.param }
    self:doUpdateState()
    self.parent:onSize()
    if self.onChange ~= nil then self:onChange() end
    if self.readout ~= nil then self.readout:doParamGet() end
    redraw = 1
  end
end

function Fader:doubleClick() 
  local tmp,title,value,defValue = reaper.ThemeLayout_GetParameter(self.param)
  reaper.ThemeLayout_SetParameter(self.param,defValue, true)
  ctheme_param_needsave = nil
  if self.onChange ~= nil then self:onChange() end
  if self.readout ~= nil then self.readout:doParamGet() end
end

function Fader:mouseWheel(v)
  local tmp,tmp,value,tmp,minvalue,maxvalue = reaper.ThemeLayout_GetParameter(self.param)
  newValue = value + v
  if newValue < minvalue then newValue = minvalue end
  if newValue > maxvalue then newValue = maxvalue end
  reaper.ThemeLayout_SetParameter(self.param, newValue, false)
  ctheme_param_needsave = { self.param, reaper.time_precise() + .5 }
  self:doUpdateState()
  self.parent:onSize()
  if self.onChange ~= nil then self:onChange() end
  if self.readout ~= nil then self.readout:doParamGet() end
  redraw = 1
end

FaderBg.mouseWheel = Fader.mouseWheel

function Readout:doubleClick() 
  if self.userEntry == true then
    self.action(self)
    root:doUpdateState()
    resize = 1
    paramGet = 1
    if self.onChange ~= nil then self:onChange() end
  end
end

function doHelp()
  if _undockedRoot.visible ~= false then
    local help_hit = root:hitTestHelp(gfx.mouse_x,gfx.mouse_y);
    if lastHelpElem ~= help_hit then
      if help_hit ~= nil and help_hit.helpL ~= nil then
        _helpL.y = math.max((help_hit.drawy or help_hit.y) - 36,30)
        _helpL.text.str = help_hit.helpL
      end
      if help_hit ~= nil and help_hit.helpR ~= nil then
        _helpR.y = math.max(help_hit.drawy - 36,30)
        _helpR.text.str = help_hit.helpR
      end
      resize = 1
      lastHelpElem = help_hit
    end
  end
end

function Button:mouseUp()
  if self.img ~= nil then
    if self.imgType ~= nil and self.imgType == 3 then
      self.imgFrame = 2
    end
  end
  if self.action ~= nil then self.action(self.param) end
  root:doUpdateState() -- doing a complete root:doUpdateState because other buttons might have changed state as a result of my actions.
  paramGet = 1
  redraw = 1
end

function SwatchHitbox:mouseOver()
  self.parent.children[12].color = {180,180,180}
  self.parent.children[13].text.col = {180,180,180}
end

function SwatchHitbox:mouseUp()
  palette.current = self.paletteIdx
  paramGet = 1
  redraw = 1
end

function SwatchHitbox:mouseAway()
  self.parent:doParamGet()
end

function Element:hitTest(x,y)
  local thisX, thisY, thisW, thisH = self.drawx or self.x, self.drawy or self.y, self.drawW or self.w or 0, self.drawH or self.h
  local xS,yS = x / drawScale, y / drawScale
  if self.visible ~= false then
    local inrect = xS >= thisX and yS >= thisY and xS < thisX + thisW and yS < thisY + thisH
    if self.children ~= nil and (inrect == true or self.has_children_outside == 1) then
      for i,v in pairs(self.children) do
        local s = v:hitTest(x,y)
        if s ~= nil then return s end
      end
    end
    if inrect and (self.interactive ~= false or self.helpL ~= nil or self.helpR ~= nil) then
      return self
    end
  end
  return nil
end

function Element:hitTestHelp(x,y)
  local thisX, thisY, thisW, thisH = self.drawx or self.x, self.drawy or self.y, self.drawW or self.w or 0, self.drawH or self.h
  local xS,yS = x / drawScale, y / drawScale
  if self.visible ~= false then
    local inrect = xS >= thisX and yS >= thisY and xS < thisX + thisW and yS < thisY + thisH
    if self.children ~= nil and (inrect == true or self.has_children_outside == 1) then
      for i,v in pairs(self.children) do
        local s = v:hitTestHelp(x,y)
        if s ~= nil then return s end
      end
    end
    if inrect and (self.helpL ~= nil or self.helpR ~= nil) then
      return self
    end
  end
  return nil
end

-- Spinner label values
folderIndentVals = {'NONE','1/8','1/4','1/2',1,2,'MAX' }
tcpLabelVals = {'AUTO',20,50,80,110,140,170}
tcpVolVals = {'KNOB',40,70,100,130,160,190}
tcpMeterVals = {4,10,20,40,80,160,320}
tcpInVals = {'MIN',25,40,60,90,150,200}
mcpMeterExpVals = {'NONE',2,4,8}
envcpLabelVals = {'AUTO',20,50,80,110,140,170}
transRateVals = {'KNOB',80,130,160,200,250,310,380}
mcpBorderVals = {'NONE', 'LEFT EDGE', 'RIGHT EDGE', 'ROOT FOLDERS', 'AROUND FOLDERS'}
dockedMcpMeterExpVals = {'NONE','+ 2 PIXELS','+ 4 PIXELS','+ 8 PIXELS'}
undockPaletteNamesVals = {'REAPER v6', 'PRIDE','WARM','COOL','VICE','EEEK'}
controlAlignVals = {'FOLDER INDENT','ALIGNED'}
trackControlAlignVals = {'FOLDER INDENT','ALIGNED','EXTEND NAME'}
seperateSendsVals = {'OFF','ON'}
tcpMeterLocVals = {'LEFT','RIGHT','LEFT IF ARMED'}

helpL_layout = 'These settings are automatically saved to your REAPER install, and '
             ..'will be used whenever you use this theme.'
helpL_customCol = 'Any assigned custom colors will be saved with your '
             ..'project, when it is saved.'
helpL_colDimming = 'This theme draws custom colors at full strength. Old '
                 ..'projects may appear very bright, dim them here.'
helpL_dock = 'REAPER will remember whether you docked this script, and where.'
helpL_applySize = 'Layout and scale assignments are part of your REAPER '
                ..'project, and will be saved when it is saved.'

helpR_help = 'Turn off this help text.'
helpR_dock = 'Dock this script in its condensed format.'
helpR_trackLabelColor = 'If a track has a custom color, use that color on the track name.'
helpR_colAdj = 'Adjust how REAPER draws the theme colors.  Mousewheel for fine '
              ..'adjustment. Double click the fader to reset. Double click the value to enter a new value.'
helpR_resetColAdj = 'Reset all of these color controls to return the theme to its unaltered state.'
helpR_indent = 'Amount to indent a panel due to its depth within your project folder '
             ..'structure.  Value chosen is used by all layouts.'
helpR_control_align = 'Choose whether to indent the panel controls when '
                   ..'folders indent, or to keep controls aligned.  '
                    ..'Value chosen is used by all layouts.'
helpR_layoutButton = 'Select which of the three layouts you wish to edit or apply.'
helpR_default = 'Indicates whether this layout is the \'default\' layout. '
              ..'To choose your default use the Options > Layouts menu or the '
              ..'Screensets/Layouts window.'
helpR_selected = 'Indicates whether one or more of the selected tracks is using this layout.'
helpR_applySize = 'Applies this layout to any selected tracks, at this size. '
                ..'REAPER may already be using a non-100% size, depending on your '
                ..'HiDPI settings.'
helpR_nameSizeEnv = 'Size of the envelopes\' name field.  If set to AUTO then, '
                  ..'while the script is running, it will adjust this to fit the '
                  ..'longest envelope name currently in your project.'
helpR_meterScale = 'Requires \'DO METER EXPANSION\' to be ticked below, and '
                 ..'the conditions you set to be met. Tracks with greater '
                 ..'than 2 channels will then expand the width of their '
                 ..'meters by the set amount of pixels (per channel), and '
                 ..'enlarge the track width to fit.'
helpR_borders = 'Adds visual separation to your mixer with borders. '
              ..'LEFT EDGE and/or RIGHT EDGE allow you to manually use layout '
              ..'assignments to add these as needed. ROOT FOLDERS draws a border on '
              ..'the left edge of root level folders, and on the right hand '
              ..'edge of the end of that folder if it is one level deep. '
              ..'AROUND FOLDERS draws borders at the start and end of every folder.'
help_pref = 'These buttons set REAPER preferences. Their settings are automatically '
          ..'saved to your REAPER install.'

help_proj_extmix="These settings are part of your REAPER project, and will be saved "
               .."when it is saved (except for 'Scroll to selected track', which is "
               .."a REAPER preference)"

helpR_recolProject = 'Assigns random colors from the palette. Tracks which share '
                   ..'a color will be given the same new color.'
helpR_choosePalette = 'Click to select a palette.'

helpR_emvMatchIndent = 'Indent with folders, matching the \'Folder Indent '
                     ..'Width\' setting on the Track Control Panel Page.'
help_playRate = 'If \'Show Play Rate\' is on, sets the size of the play rate control. '
              ..'TIP : right-click the play rate control to adjust its range.'


  --------- POPULATE ---------

apply = {}
root = Element:new(nil, {x=0,y=0,drawx=0,drawy=0,w=_gfxw,h=_gfxh,color={38,38,38}})


  ------ DOCKED LAYOUT -------

_dockedRoot = Element:new(root, {flexW='100%',h=_gfxh})

_pageGlobal = Element:new(_dockedRoot, {flow=true,title='GLOBAL',y=0,flexW='fill',x=0,w=-16,h=_gfxh})
Spinner:new(_pageGlobal, {flow=true,spinStyle='image',valsImage='page_titles_small',x=6,y=0,w=133,border='',action=doPageSpin})
_gammaUnd = Element:new(_pageGlobal, {flow=true,x=0,y=6,w=453,h=19,color={90,94,94},helpR=helpR_colAdj,interactive=false})
_gammaUndBg =  FaderBg:new(_gammaUnd, {x=1,y=1,w=451,h=17,img='faderbg_gamma',action=doFader,param=-1000,helpR=helpR_colAdj,interactive=false})
Element:new(_gammaUndBg, {x=195,y=2,w=1,h=13,color={255,255,255,64}}) --zero line
Fader:new(_gammaUndBg, {x=1,y=-4,action=doFader,param=-1000,helpR=helpR_colAdj})
_gammaBox = Element:new(_pageGlobal, {flow=true,x=0,y=-6,w=72,h=30,border='x'})
Element:new(_gammaBox, {x=0,y=4,w=72,h=11,text={str='GAMMA',style=2,align=4,col={129,137,137}}})
Readout:new(_gammaBox,{x=0,y=16,w=52,h=11,userEntry=true,action=doFader,moColor={60,60,60},param={-1000},text={str='',align=4,col={129,137,137}}})

_saturationUnd = Element:new(_pageGlobal, {flow=true,x=0,y=6,w=453,h=19,color={90,94,94},helpR=helpR_colAdj,interactive=false})
_saturationUndBg =  FaderBg:new(_saturationUnd, {x=1,y=1,w=451,h=17,img='faderbg_saturation',action=doFader,param=-1004,helpR=helpR_colAdj,interactive=false})
Element:new(_saturationUndBg, {x=226,y=2,w=1,h=13,color={255,255,255,64}}) --zero line
Fader:new(_saturationUndBg, {x=1,y=-4,action=doFader,param=-1004,helpR=helpR_colAdj})
_saturationBox = Element:new(_pageGlobal, {flow=true,x=0,y=-6,w=72,h=30,border='x'})
Element:new(_saturationBox, {x=0,y=4,w=72,h=11,text={str='SATURATION',style=2,align=4,col={129,137,137}}})
Readout:new(_saturationBox,{x=0,y=16,w=52,h=11,userEntry=true,action=doFader,moColor={60,60,60},param={-1004},text={str='',align=4,col={129,137,137}}})

_pageTrack = Element:new(_dockedRoot, {flow=true,title='TRACK',y=0,flexW='fill',x=0,w=-16,h=_gfxh})
Spinner:new(_pageTrack, {flow=true,spinStyle='image',valsImage='page_titles_small',x=6,y=0,w=133,border='',action=doPageSpin})
Spinner:new(_pageTrack, {flow=true,w=92,title='INDENT',action=paramSet,param='tcp_indent',valsTable=folderIndentVals})
Button:new(_pageTrack, {flow=true,w=45,img='layout_A',imgType=3,border='x',action=doActiveLayout,param={'tcp','A'}})
Button:new(_pageTrack, {flow=true,w=43,img='layout_B',imgType=3,action=doActiveLayout,param={'tcp','B'}})
Button:new(_pageTrack, {flow=true,w=45,img='layout_C',imgType=3,action=doActiveLayout,param={'tcp','C'}})
_applyBoxTcp = Element:new(_pageTrack, {flow=true,w=228,h=30, color={51,51,51}})
Element:new(_applyBoxTcp, {w=44,h=30,img='apply_to_sel_docked'})
apply.tcp = {Button:new(_applyBoxTcp, {flow=true,w=61,y=5,img='apply_100',imgType=3,action=applyLayout,param={'tcp',''}})}
apply.tcp[2] = Button:new(_applyBoxTcp, {flow=true,w=61,img='apply_150',imgType=3,action=applyLayout,param={'tcp','150%_'}})
apply.tcp[3] = Button:new(_applyBoxTcp, {flow=true,w=61,img='apply_200',imgType=3,action=applyLayout,param={'tcp','200%_'}})
Spinner:new(_pageTrack, {flow=true,w=104,title='NAME SIZE',border='x',action=paramSet,param='tcp_LabelSize',valsTable=tcpLabelVals})
Spinner:new(_pageTrack, {flow=true,w=118,title='VOLUME SIZE',border='x',action=paramSet,param='tcp_vol_size',valsTable=tcpVolVals})
Spinner:new(_pageTrack, {flow=true,w=110,title='METER SIZE',border='x',action=paramSet,param='tcp_MeterSize',valsTable=tcpMeterVals})
Spinner:new(_pageTrack, {flow=true,w=110,title='INPUT SIZE',border='x',action=paramSet,param='tcp_InputSize',valsTable=tcpInVals})

_pageMixer = Element:new(_dockedRoot, {flow=true,title='MIXER',y=0,flexW='fill',w=-16,h=_gfxh})
Spinner:new(_pageMixer, {flow=true,spinStyle='image',valsImage='page_titles_small',x=6,y=0,w=133,border='',action=doPageSpin})
Spinner:new(_pageMixer, {flow=true,w=92,title='INDENT',border='',action=paramSet,param='mcp_indent',valsTable=folderIndentVals})
Button:new(_pageMixer, {flow=true,w=45,img='layout_A',imgType=3,border='x',action=doActiveLayout,param={'mcp','A'}})
Button:new(_pageMixer, {flow=true,w=43,img='layout_B',imgType=3,action=doActiveLayout,param={'mcp','B'}})
Button:new(_pageMixer, {flow=true,w=45,img='layout_C',imgType=3,action=doActiveLayout,param={'mcp','C'}})
_applyBoxMcp = Element:new(_pageMixer, {flow=true,w=228,h=30, color={51,51,51}})
Element:new(_applyBoxMcp, {w=44,h=30,img='apply_to_sel_docked'})
apply.mcp = {Button:new(_applyBoxMcp, {flow=true,y=5,w=61,img='apply_100',imgType=3,action=applyLayout,param={'mcp',''}})}
apply.mcp[2] = Button:new(_applyBoxMcp, {flow=true,w=61,img='apply_150',imgType=3,action=applyLayout,param={'mcp','150%_'}})
apply.mcp[3] = Button:new(_applyBoxMcp, {flow=true,w=61,img='apply_200',imgType=3,action=applyLayout,param={'mcp','200%_'}})
Spinner:new(_pageMixer, {flow=true,w=154,title='ADD BORDER',border='x',action=paramSet,param='mcp_border',valsTable=mcpBorderVals})
Spinner:new(_pageMixer, {flow=true,w=108,title='METER EXP',border='x',action=paramSet,param='mcp_meterExpSize',valsTable=mcpMeterExpVals})
Button:new(_pageMixer, {flow=true,img='show_fx',imgType=3,border='x',action=actionToggle,param=40549})
Button:new(_pageMixer, {flow=true,img='show_param',imgType=3,border='',action=actionToggle,param=40910})
Button:new(_pageMixer, {flow=true,img='show_send',imgType=3,border='',action=actionToggle,param=40557})

_pageColors = Element:new(_dockedRoot, {flow=true,title='COLORS',x=0,y=0,flexW='fill',w=-16,h=_gfxh})
Spinner:new(_pageColors, {flow=true,spinStyle='image',valsImage='page_titles_small',x=6,y=0,w=133,border='',action=doPageSpin})
_palette = Palette:new(_pageColors, {flow=true,border='y',w=318,h=30,cellW=30,action=applyCustCol})
Spinner:new(_pageColors, {flow=true,border='x',w=96,title='PALETTE',action=paletteChoose,value=getCurrentPalette})
Button:new(_pageColors, {flow=true,img='color_apply_all',imgType=3,border='x',action=applyPalette})
Button:new(_pageColors, {flow=true,img='color_dim',imgType=3,border='x',action=reduceCustCol,param=true})
Button:new(_pageColors, {flow=true,img='color_dim_all',imgType=3,border='x',action=reduceCustCol,param=false})

_pageEnv = Element:new(_dockedRoot, {flow=true,title='ENVELOPE',y=0,flexW='fill',w=-16,h=_gfxh})
Spinner:new(_pageEnv, {flow=true,spinStyle='image',valsImage='page_titles_small',x=6,y=0,w=133,border='',action=doPageSpin})
_applyBoxEnv = Element:new(_pageEnv, {flow=true,w=188,h=30, color={51,51,51}})
apply.envcp = {Button:new(_applyBoxEnv, {x=5,y=5,w=61,img='apply_100',imgType=3,action=applyLayout,param={'envcp',''}})}
apply.envcp[2] = Button:new(_applyBoxEnv, {flow=true,w=61,img='apply_150',imgType=3,action=applyLayout,param={'envcp','150%_'}})
apply.envcp[3] = Button:new(_applyBoxEnv, {flow=true,w=61,img='apply_200',imgType=3,action=applyLayout,param={'envcp','200%_'}})
Spinner:new(_pageEnv, {flow=true,w=106,title='NAME SIZE',border='x',action=paramSet,param='envcp_labelSize',valsTable=envcpLabelVals})
Spinner:new(_pageEnv, {flow=true,w=106,title='FADER SIZE',border='x',action=paramSet,param='envcp_fader_size',valsTable=tcpVolVals})
Button:new(_pageEnv, {flow=true,img='match_folder_indent',imgType=3,border='x',action=paramToggle,param='envcp_folder_indent'})

_pageTrans = Element:new(_dockedRoot, {flow=true,title='TRANSPORT',y=0,flexW='fill',w=-16,h=_gfxh})
Spinner:new(_pageTrans, {flow=true,spinStyle='image',valsImage='page_titles_small',x=6,y=0,w=133,border='',action=doPageSpin})
_applyBoxTrans = Element:new(_pageTrans, {flow=true,w=188,h=30, color={51,51,51}})
apply.trans = {Button:new(_applyBoxTrans, {x=5,y=5,w=61,img='apply_100',imgType=3,action=applyLayout,param={'trans',''},helpL=helpL_applySize})}
apply.trans[2] = Button:new(_applyBoxTrans, {flow=true,w=61,img='apply_150',imgType=3,action=applyLayout,param={'trans','150%_'},helpL=helpL_applySize})
apply.trans[3] = Button:new(_applyBoxTrans, {flow=true,w=61,img='apply_200',imgType=3,action=applyLayout,param={'trans','200%_'},helpL=helpL_applySize})
Spinner:new(_pageTrans, {flow=true,w=106,title='RATE SIZE',border='x',action=paramSet,param='trans_rate_size',valsTable=transRateVals})
Button:new(_pageTrans, {flow=true,img='show_play_rate',imgType=3,border='x',action=actionToggle,param=40531})
Button:new(_pageTrans, {flow=true,img='center_transport',imgType=3,border='',action=actionToggle,param=40533})
Button:new(_pageTrans, {flow=true,img='time_sig',imgType=3,border='',action=actionToggle,param=40680})
Button:new(_pageTrans, {flow=true,img='next_prev',imgType=3,border='',action=actionToggle,param=40868})
Button:new(_pageTrans, {flow=true,img='play_text',imgType=3,border='',action=actionToggle,param=40532})

Button:new(_dockedRoot, {positionX='right', img='docked_edit',imgType=3,-16,y=0,w=16,action=doDock})


  ----- UNDOCKED LAYOUT ------
_undockedRoot = Element:new(root, {flexW='100%',flexH='100%'})

_pageContainer = Element:new(_undockedRoot, {positionX='center', positionY='center',x=0,y=0,w=513,h=679})

_buttonHelp = Button:new(_pageContainer, {flow=false,x=0,y=0,w=30,img='help_on',imgType=3,w=30,action=toggleHelp,helpR=helpR_help})
_unDpageSpin = Spinner:new(_pageContainer, {spinStyle='image',valsImage='page_titles',x=130,y=0,w=253,action=doPageSpin})
Button:new(_pageContainer, {flow=false,x=483,y=0,img='dock',imgType=3,w=30,action=doDock,helpL=helpL_dock,helpR=helpR_dock})
Element:new(_pageContainer, {x=0,y=39,w=513,h=1,color={0,0,0}}) -- black title div

_subPageContainer = Element:new(_pageContainer, {x=0,y=40,w=513,h=639})

--GLOBAL PAGE
_pageGlobal_und = Element:new(_subPageContainer, {x=0,y=0,w=513,h=639})
Button:new(_pageGlobal_und, {flow=false,x=169,y=29,w=30,img='color_text',imgType=3,action=paramToggle,param='glb_track_label_color',helpR=helpR_trackLabelColor})
Element:new(_pageGlobal_und, {x=208,y=30,w=165,h=28,text={str='Custom color track names',style=2,align=4,col={129,137,137}},helpR=helpR_trackLabelColor})

_colAdjBoxStroke = Element:new(_pageGlobal_und, {x=0,y=88,w=513,h=475,color={51,51,51}}) -- stroke
_colAdjBox = Element:new(_colAdjBoxStroke, {x=1,y=1,w=511,h=473,color={51,51,51},helpL=helpL_layout}) -- fill
Element:new(_colAdjBox, {x=0,y=26,w=511,h=11,text={str='COLOR CONTROLS',style=2,align=5,col={129,137,137}},helpR=helpR_trackLabelColor})

_gamma = Element:new(_colAdjBox, {x=29,y=64,w=453,h=19,color={90,94,94},helpR=helpR_colAdj,interactive=false})
_gammabg =  FaderBg:new(_gamma, {x=1,y=1,w=451,h=17,img='faderbg_gamma',action=doFader,param=-1000,helpR=helpR_colAdj,interactive=false})
Element:new(_gammabg, {x=195,y=2,w=1,h=13,color={255,255,255,64}}) --zero line
Fader:new(_gammabg, {x=1,y=-4,action=doFader,param=-1000,helpR=helpR_colAdj})
Element:new(_colAdjBox, {x=29,y=90,w=72,h=11,text={str='GAMMA',style=2,align=4,col={129,137,137}},helpR=helpR_colAdj})
Readout:new(_colAdjBox,{x=101,y=88,w=52,h=15,userEntry=true,action=doFader,moColor={40,40,40},param={-1000},text={str='',align=4,col={129,137,137}},helpR=helpR_colAdj})

_hms = Element:new(_colAdjBox, {x=29,y=128,w=453,h=53,color={90,94,94},helpR=helpR_colAdj,interactive=false})
_highlightbg = FaderBg:new(_hms, {x=1,y=1,w=451,h=17,color={71,73,73},action=doFader,param=-1003,helpR=helpR_colAdj})
Element:new(_highlightbg, {x=226,y=2,w=1,h=13,color={255,255,255,64}}) --zero line
_midtonebg = FaderBg:new(_hms, {x=1,y=18,w=451,h=17,color={51,51,51},action=doFader,param=-1002,helpR=helpR_colAdj})
Element:new(_midtonebg, {x=226,y=2,w=1,h=13,color={255,255,255,64}}) --zero line
_shadowbg = FaderBg:new(_hms, {x=1,y=35,w=451,h=17,color={44,44,44},action=doFader,param=-1001,helpR=helpR_colAdj})
Element:new(_shadowbg, {x=226,y=2,w=1,h=13,color={255,255,255,64}}) --zero line
Fader:new(_highlightbg, {x=1,y=-4,action=doFader,param=-1003,helpR=helpR_colAdj})
Fader:new(_midtonebg, {x=1,y=-4,action=doFader,param=-1002,helpR=helpR_colAdj})
Fader:new(_shadowbg, {x=1,y=-4,action=doFader,param=-1001,helpR=helpR_colAdj})
Element:new(_colAdjBox, {x=29,y=188,w=72,h=11,text={str='HIGHLIGHTS',style=2,align=4,col={129,137,137}},helpR=helpR_colAdj})
Readout:new(_colAdjBox,{x=101,y=186,w=52,h=15,userEntry=true,action=doFader,moColor={40,40,40},param={-1003},text={str='',align=4,col={129,137,137}},helpR=helpR_colAdj})
Element:new(_colAdjBox, {x=192,y=188,w=72,h=11,text={str='MIDTONES',style=2,align=6,col={129,137,137}},helpR=helpR_colAdj})
Readout:new(_colAdjBox,{x=266,y=186,w=52,h=15,userEntry=true,action=doFader,moColor={40,40,40},param={-1002},text={str='',align=5,col={129,137,137}},helpR=helpR_colAdj})
Element:new(_colAdjBox, {x=358,y=188,w=72,h=11,text={str='SHADOWS',style=2,align=6,col={129,137,137}},helpR=helpR_colAdj})
Readout:new(_colAdjBox,{x=430,y=186,w=52,h=15,userEntry=true,action=doFader,moColor={40,40,40},param={-1001},text={str='',align=6,col={129,137,137}},helpR=helpR_colAdj})

_saturation = Element:new(_colAdjBox, {x=29,y=226,w=453,h=19,color={90,94,94},helpR=helpR_colAdj,interactive=false})
_saturationbg = FaderBg:new(_saturation, {x=1,y=1,w=451,h=17,img='faderbg_saturation',action=doFader,param=-1004,helpR=helpR_colAdj})
Element:new(_saturationbg, {x=226,y=2,w=1,h=13,color={255,255,255,64}}) --zero line
Fader:new(_saturationbg, {x=1,y=-4,action=doFader,param=-1004,helpR=helpR_colAdj})
Element:new(_colAdjBox, {x=29,y=252,w=72,h=11,text={str='SATURATION',style=2,align=4,col={129,137,137}},helpR=helpR_colAdj})
Readout:new(_colAdjBox,{x=101,y=250,w=62,h=15,userEntry=true,action=doFader,moColor={40,40,40},param={-1004},text={str='',align=4,col={129,137,137}},helpR=helpR_colAdj})

_tint = Element:new(_colAdjBox, {x=29,y=290,w=453,h=19,color={90,94,94},helpR=helpR_colAdj,interactive=false})
_tintbg = FaderBg:new(_tint, {x=1,y=1,w=451,h=17,img='faderbg_tint',action=doFader,param=-1005,helpR=helpR_colAdj})
Element:new(_tintbg, {x=226,y=2,w=1,h=13,color={255,255,255,64}}) --zero line
Fader:new(_tintbg, {x=1,y=-4,action=doFader,param=-1005,helpR=helpR_colAdj})
Element:new(_colAdjBox, {x=29,y=316,w=72,h=11,text={str='TINT',style=2,align=4,col={129,137,137}},helpR=helpR_colAdj})
Readout:new(_colAdjBox,{x=101,y=314,w=62,h=15,userEntry=true,action=doFader,moColor={40,40,40},param={-1005},text={str='',align=4,col={129,137,137}},helpR=helpR_colAdj})

Button:new(_colAdjBox, {x=140,y=356,w=30,img='color_apply_all',imgType=3,action=paramToggle,param=-1006})
Element:new(_colAdjBox, {x=179,y=356,w=330,h=30,text={str='Also affect project custom colors',style=2,align=4,col={129,137,137}}})
Button:new(_colAdjBox, {flow=false,x=140,y=415,w=30,img='bin',imgType=3,action=resetColorControls,helpR=helpR_resetColAdj})
Element:new(_colAdjBox, {x=179,y=415,w=165,h=28,text={str='Reset all color controls',style=2,align=4,col={129,137,137}},helpR=helpR_resetColAdj})

--TRACK PAGE
_pageTrack_und = Element:new(_subPageContainer, {x=0,y=0,w=513,h=639})
Spinner:new(_pageTrack_und, {x=61,y=19,w=165,title='FOLDER INDENT',action=paramSet,param='tcp_indent',valsTable=folderIndentVals,helpR=helpR_indent,helpL=helpL_layout})
Spinner:new(_pageTrack_und, {x=287,y=19,w=165,title='ALIGN CONTROLS',action=paramSet,param='tcp_control_align',valsTable=trackControlAlignVals,helpR=helpR_control_align,helpL=helpL_layout})
_layoutTrackStroke = Element:new(_pageTrack_und, {x=0,y=153,w=513,h=486,color={129,137,137}}) -- stroke

_tcpButLayA = Button:new(_pageTrack_und, {x=30,y=65,w=69,img='layout_docked_A',imgType=3,action=doActiveLayout,param={'tcp','A'},helpR=helpR_layoutButton})
_tcpButLayB = Button:new(_pageTrack_und, {x=103,y=65,w=69,img='layout_docked_B',imgType=3,action=doActiveLayout,param={'tcp','B'},helpR=helpR_layoutButton})
_tcpButLayC = Button:new(_pageTrack_und, {x=176,y=65,w=69,img='layout_docked_C',imgType=3,action=doActiveLayout,param={'tcp','C'},helpR=helpR_layoutButton})
Readout:new(_tcpButLayA, {x=7,y=53,w=55,h=12,text={str='DEFAULT',style=1,align=5,colFalse={45,79,91},colTrue={19,189,153}},updateState=isDefault,getParam={'tcp','A'},helpR=helpR_default})
Readout:new(_tcpButLayB, {x=7,y=53,w=55,h=12,text={str='DEFAULT',style=1,align=5,colFalse={45,79,91},colTrue={19,189,153}},updateState=isDefault,getParam={'tcp','B'},helpR=helpR_default})
Readout:new(_tcpButLayC, {x=7,y=53,w=55,h=12,text={str='DEFAULT',style=1,align=5,colFalse={45,79,91},colTrue={19,189,153}},updateState=isDefault,getParam={'tcp','C'},helpR=helpR_default})
Readout:new(_tcpButLayA, {x=7,y=65,w=55,h=12,text={str='SELECTED',style=1,align=5,colFalse={90,94,94},colTrue={193,197,197}},updateState=anySelected,getParam={'tcp','A'},helpR=helpR_selected})
Readout:new(_tcpButLayB, {x=7,y=65,w=55,h=12,text={str='SELECTED',style=1,align=5,colFalse={90,94,94},colTrue={193,197,197}},updateState=anySelected,getParam={'tcp','B'},helpR=helpR_selected})
Readout:new(_tcpButLayC, {x=7,y=65,w=55,h=12,text={str='SELECTED',style=1,align=5,colFalse={90,94,94},colTrue={193,197,197}},updateState=anySelected,getParam={'tcp','C'},helpR=helpR_selected})

_applyStroke_Track = Element:new(_pageTrack_und, {x=274,y=64,w=239,h=89,color={129,137,137}}) -- stroke
_applyBox_Track = Element:new(_applyStroke_Track, {x=1,y=1,w=237,h=89,color={51,51,51},helpL=helpL_applySize}) -- fill
apply.und = {tcp={Button:new(_applyBox_Track, {x=29,y=29,w=61,img='apply_100',imgType=3,action=applyLayout,param={'tcp',''},helpR=helpR_applySize,helpL=helpL_applySize})}}
apply.und.tcp[2] = Button:new(_applyBox_Track, {x=90,y=29,w=61,img='apply_150',imgType=3,action=applyLayout,param={'tcp','150%_'},helpR=helpR_applySize,helpL=helpL_applySize})
apply.und.tcp[3] = Button:new(_applyBox_Track, {x=151,y=29,w=61,img='apply_200',imgType=3,action=applyLayout,param={'tcp','200%_'},helpR=helpR_applySize,helpL=helpL_applySize})
Element:new(_applyBox_Track, {x=40,y=66,w=153,h=7,img='apply_to_sel'})

_layoutTrack = Element:new(_layoutTrackStroke, {x=1,y=1,w=511,h=484,color={51,51,51},helpL=helpL_layout}) -- fill
Spinner:new(_layoutTrack, {x=29,y=29,w=119,title='NAME SIZE',action=paramSet,param='tcp_LabelSize',valsTable=tcpLabelVals})
Spinner:new(_layoutTrack, {x=29,y=70,w=119,title='METER SIZE',action=paramSet,param='tcp_MeterSize',valsTable=tcpMeterVals})
Spinner:new(_layoutTrack, {x=363,y=29,w=119,title='INPUT SIZE',action=paramSet,param='tcp_InputSize',valsTable=tcpInVals})
Spinner:new(_layoutTrack, {x=192,y=29,w=127,title='VOLUME SIZE',action=paramSet,param='tcp_vol_size',valsTable=tcpVolVals})
Spinner:new(_layoutTrack, {x=182,y=70,w=147,title='METER LOCATION',action=paramSet,param='tcp_MeterLoc',valsTable=tcpMeterLocVals})
Spinner:new(_layoutTrack, {x=363,y=70,w=119,title='SENDS LIST',action=paramSet,param='tcp_sepSends',valsTable=seperateSendsVals,helpL=helpL_layout})

tcpTableVals = {img = 'cell_hide',
                columns = {{visFlag=1,text={str='IF MIXER#IS VISIBLE'}},{visFlag=2,text={str='IF TRACK#NOT SELECTED'}},
                        {visFlag=4,text={str='IF TRACK#NOT ARMED'}},{visFlag=8,text={str='ALWAYS#HIDE',col={204,69,114}}}},
                rows = {{param='tcp_Record_Arm',text={str='RECORD ARM'},img='key_recarm'},
                        {param='tcp_Monitor',text={str='MONITOR'},img='key_mon'},{param='tcp_Track_Name',text={str='TRACK NAME'}},
                        {param='tcp_Volume',text={str='VOLUME'},img='key_vol'},{param='tcp_Routing',text={str='ROUTING'},img='key_route'},
                        {param='tcp_Effects',text={str='INSERT FX'},img='key_fx'},{param='tcp_Envelope',text={str='ENVELOPE'},img='key_env'},
                        {param='tcp_Pan_&_Width',text={str='PAN & WIDTH'},img='key_pan'},{param='tcp_Record_Mode',text={str='RECORD MODE'},img='key_recmode'},
                        {param='tcp_Input',text={str='INPUT'},img='key_recinput'},{param='tcp_Values',text={str='LABELS & VALUES'}},
                        {param='tcp_Meter_Values',text={str='METER VALUES'}}}
}
_trackTable = ParamTable:new(_layoutTrack, {x=29,y=125,w=453,h=330,valsTable=tcpTableVals})

--MIXER PAGE
_pageMixer_und = Element:new(_subPageContainer, {x=0,y=0,w=513,h=639})
Spinner:new(_pageMixer_und, {x=61,y=19,w=165,title='FOLDER INDENT',action=paramSet,param='mcp_indent',valsTable=folderIndentVals,helpR=helpR_indent,helpL=helpL_layout})
Spinner:new(_pageMixer_und, {x=287,y=19,w=165,title='ALIGN CONTROLS',action=paramSet,param='mcp_control_align',valsTable=controlAlignVals,helpR=helpR_control_align,helpL=helpL_layout})
_mixerTopStroke = Element:new(_pageMixer_und, {x=0,y=153,w=513,h=245,color={129,137,137}}) -- stroke

_mcpButLayA = Button:new(_pageMixer_und, {x=30,y=65,w=69,img='layout_docked_A',imgType=3,action=doActiveLayout,param={'mcp','A'},helpR=helpR_layoutButton})
_mcpButLayB = Button:new(_pageMixer_und, {x=103,y=65,w=69,img='layout_docked_B',imgType=3,action=doActiveLayout,param={'mcp','B'},helpR=helpR_layoutButton})
_mcpButLayC = Button:new(_pageMixer_und, {x=176,y=65,w=69,img='layout_docked_C',imgType=3,action=doActiveLayout,param={'mcp','C'},helpR=helpR_layoutButton})
Readout:new(_mcpButLayA, {x=7,y=53,w=55,h=12,text={str='DEFAULT',style=1,align=5,colFalse={45,79,91},colTrue={19,189,153}},updateState=isDefault,getParam={'mcp','A'},helpR=helpR_default})
Readout:new(_mcpButLayB, {x=7,y=53,w=55,h=12,text={str='DEFAULT',style=1,align=5,colFalse={45,79,91},colTrue={19,189,153}},updateState=isDefault,getParam={'mcp','B'},helpR=helpR_default})
Readout:new(_mcpButLayC, {x=7,y=53,w=55,h=12,text={str='DEFAULT',style=1,align=5,colFalse={45,79,91},colTrue={19,189,153}},updateState=isDefault,getParam={'mcp','C'},helpR=helpR_default})
Readout:new(_mcpButLayA, {x=7,y=65,w=55,h=12,text={str='SELECTED',style=1,align=5,colFalse={90,94,94},colTrue={193,197,197}},updateState=anySelected,getParam={'mcp','A'},helpR=helpR_selected})
Readout:new(_mcpButLayB, {x=7,y=65,w=55,h=12,text={str='SELECTED',style=1,align=5,colFalse={90,94,94},colTrue={193,197,197}},updateState=anySelected,getParam={'mcp','B'},helpR=helpR_selected})
Readout:new(_mcpButLayC, {x=7,y=65,w=55,h=12,text={str='SELECTED',style=1,align=5,colFalse={90,94,94},colTrue={193,197,197}},updateState=anySelected,getParam={'mcp','C'},helpR=helpR_selected})

_applyStroke_Mixer = Element:new(_pageMixer_und, {x=274,y=64,w=239,h=89,color={129,137,137},helpL=helpL_applySize}) -- stroke
_applyBox_Mixer = Element:new(_applyStroke_Mixer, {x=1,y=1,w=237,h=89,color={51,51,51},helpL=helpL_applySize}) -- fill
apply.und.mcp = {Button:new(_applyBox_Mixer, {x=29,y=29,w=61,img='apply_100',imgType=3,action=applyLayout,param={'mcp',''},helpR=helpR_applySize,helpL=helpL_applySize})}
apply.und.mcp[2] = Button:new(_applyBox_Mixer, {x=90,y=29,w=61,img='apply_150',imgType=3,action=applyLayout,param={'mcp','150%_'},helpR=helpR_applySize,helpL=helpL_applySize})
apply.und.mcp[3] = Button:new(_applyBox_Mixer, {x=151,y=29,w=61,img='apply_200',imgType=3,action=applyLayout,param={'mcp','200%_'},helpR=helpR_applySize,helpL=helpL_applySize})
Element:new(_applyBox_Mixer, {x=40,y=66,w=153,h=7,img='apply_to_sel'})

_mixerTop = Element:new(_mixerTopStroke, {x=1,y=1,w=511,h=243,color={51,51,51},helpL=helpL_layout}) -- fill
Spinner:new(_mixerTop, {x=69,y=29,w=163,title='ADD BORDER',action=paramSet,param='mcp_border',valsTable=mcpBorderVals,helpR=helpR_borders})
Spinner:new(_mixerTop, {x=285,y=29,w=157,title='METER EXPANSION',action=paramSet,param='mcp_meterExpSize',valsTable=dockedMcpMeterExpVals,helpR=helpR_meterScale})

mcpTableVals = {img = 'cell_tick',
                columns = {{visFlag=1,text={str='IF TRACK#SELECTED'}},{visFlag=2,text={str='IF TRACK#NOT SELECTED'}},
                        {visFlag=4,text={str='IF TRACK#ARMED'}},{visFlag=8,text={str='IF TRACK#NOT ARMED'}}},
                rows = {{param='mcp_Sidebar',text={str='EXTEND WITH SIDEBAR', helpR=helpR_sidebar}},
                        {param='mcp_Narrow',text={str='NARROW FORM'}},{param='mcp_Meter_Expansion',text={str='DO METER EXPANSION'}},
                        {param='mcp_Labels',text={str='ELEMENT LABELS'}}}
}
_mixerTable = ParamTable:new(_mixerTop, {x=29,y=84,w=453,h=130,valsTable=mcpTableVals})

_mLower = Element:new(_pageMixer_und, {x=0,y=427,w=513,h=212,color={129,137,137}}) -- stroke
_extMixerf = Element:new(_mLower, {x=1,y=1,w=255,h=210,color={38,38,38},helpL=help_proj_extmix }) -- fill
Element:new(_extMixerf, {x=29,y=24,w=173,h=30,text={str='EXTENDED MIXER CONTROLS#when size permits',style=2,align=4,col={129,137,137}}})
Button:new(_extMixerf, {x=29,y=65,w=30,img='show_fx',imgType=3,action=actionToggle,param=40549})
Element:new(_extMixerf, {x=68,y=65,w=172,h=30,text={str='Show FX Inserts',style=2,align=4,col={129,137,137}}})
Button:new(_extMixerf, {x=29,y=108,w=30,img='show_param',imgType=3,action=actionToggle,param=40910})
Element:new(_extMixerf, {x=68,y=108,w=172,h=30,text={str='Show FX Parameters',style=2,align=4,col={129,137,137}}})
Button:new(_extMixerf, {x=29,y=151,w=30,img='show_send',imgType=3,action=actionToggle,param=40557})
Element:new(_extMixerf, {x=68,y=151,w=172,h=30,text={str='Show Sends',style=2,align=4,col={129,137,137}}})

_parmMixer = Element:new(_mLower, {x=256,y=1,w=256,h=210,color={51,51,51},helpL=help_proj_extmix}) -- fill
Button:new(_parmMixer, {x=29,y=65,w=30,img='multi_row',imgType=3,action=actionToggle,param=40371})
Element:new(_parmMixer, {x=68,y=65,w=172,h=30,text={str='Show Multiple-row Mixer#when size permits',style=2,align=4,col={129,137,137}}})
Button:new(_parmMixer, {x=29,y=108,w=30,img='scroll_sel',imgType=3,action=actionToggle,param=40221})
Element:new(_parmMixer, {x=68,y=108,w=172,h=30,text={str='Scroll to selected track',style=2,align=4,col={129,137,137}}})
Button:new(_parmMixer, {x=29,y=151,w=30,img='show_icons',imgType=3,action=actionToggle,param=40903})
Element:new(_parmMixer, {x=68,y=151,w=172,h=30,text={str='Show Icons',style=2,align=4,col={129,137,137}}})

--COLOR PAGE
_pageColor_und = Element:new(_subPageContainer, {x=0,y=40,w=513,h=639})
_paletteBoxStroke = Element:new(_pageColor_und, {x=0,y=0,w=513,h=425,color={129,137,137}}) -- stroke
_paletteBox = Element:new(_paletteBoxStroke, {x=1,y=1,w=511,h=205,color={51,51,51},helpL=helpL_customCol}) -- fill

Readout:new(_paletteBox, {x=124,y=19,w=263,h=30,param={'scriptVariable',palette.current},text={str='REAPER V6',style=4,align=5,col={129,137,137}},valsTable=undockPaletteNamesVals})

Element:new(_paletteBox, {x=30,y=49,w=450,h=1,color={35,35,35}}) --div above
_palette = Palette:new(_paletteBox, {x=30,y=58,w=450,h=45,cellW=45,img='color_apply_large',action=applyCustCol})
Element:new(_paletteBox, {x=30,y=111,w=450,h=1,color={35,35,35}}) --div below
Element:new(_paletteBox, {x=0,y=112,w=511,h=30,text={str='Click a color to apply it to all selected tracks',style=3,align=5,col={129,137,137}}})
Button:new(_paletteBox, {x=140,y=146,w=30,img='color_apply_all',imgType=3,action=applyPalette,helpR=helpR_recolProject})
Element:new(_paletteBox, {x=179,y=146,w=330,h=30,text={str='Recolor project using this palette',style=2,align=4,col={129,137,137}},helpR=helpR_recolProject})
_paletteMenuBox = Element:new(_paletteBoxStroke, {x=1,y=205,w=511,h=218,color={38,38,38}}) -- fill

-- add swatches
Swatch:new(_paletteMenuBox,{x=29,y=29,paletteIdx=1})
Swatch:new(_paletteMenuBox,{x=29,y=92,paletteIdx=3})
Swatch:new(_paletteMenuBox,{x=29,y=155,paletteIdx=5})
Swatch:new(_paletteMenuBox,{x=282,y=29,paletteIdx=2})
Swatch:new(_paletteMenuBox,{x=282,y=92,paletteIdx=4})
Swatch:new(_paletteMenuBox,{x=282,y=155,paletteIdx=6})

Button:new(_pageColor_und, {x=144,y=464,img='color_dim_all',imgType=3,action=reduceCustCol,param=false,helpR=helpL_colDimming})
Element:new(_pageColor_und, {x=183,y=464,w=330,h=30,text={str='Dim all assigned custom colors',style=2,align=4,col={129,137,137}},helpR=helpL_colDimming})
Button:new(_pageColor_und, {x=144,y=507,w=30,img='color_dim',imgType=3,action=reduceCustCol,param=true,helpR=helpL_colDimming})
Element:new(_pageColor_und, {x=183,y=507,w=330,h=30,text={str='Dim custom colors on selected tracks',style=2,align=4,col={129,137,137}},helpR=helpL_colDimming})

--ENV & TRANSPORT PAGE
_pageEnvTrans_und = Element:new(_subPageContainer, {x=0,y=40,w=513,h=639,helpL=helpL_layout})
apply.und.envcp = {Button:new(_pageEnvTrans_und, {x=166,y=2,w=61,img='apply_100',imgType=3,action=applyLayout,param={'envcp',''},helpL=helpL_applySize})}
apply.und.envcp[2] = Button:new(_pageEnvTrans_und, {x=228,y=2,w=61,img='apply_150',imgType=3,action=applyLayout,param={'envcp','150%_'},helpL=helpL_applySize})
apply.und.envcp[3] = Button:new(_pageEnvTrans_und, {x=290,y=2,w=61,img='apply_200',imgType=3,action=applyLayout,param={'envcp','200%_'},helpL=helpL_applySize})
Spinner:new(_pageEnvTrans_und, {x=97,y=55,w=120,title='NAME SIZE',action=paramSet,param='envcp_labelSize',valsTable=envcpLabelVals,helpR=helpR_nameSizeEnv})
Spinner:new(_pageEnvTrans_und, {x=283,y=55,w=120,title='FADER SIZE',action=paramSet,param='envcp_fader_size',valsTable=tcpVolVals})
Button:new(_pageEnvTrans_und, {flow=false,x=170,y=110,w=30,img='match_folder_indent',imgType=3,action=paramToggle,param='envcp_folder_indent',helpR=helpR_emvMatchIndent})
Element:new(_pageEnvTrans_und, {x=209,y=110,w=150,h=30,text={str='Match track folder indent',style=2,align=4,col={129,137,137}},helpR=helpR_emvMatchIndent})

Element:new(_pageEnvTrans_und, {x=210,y=252,w=94,h=23,img='transport_title'})
Element:new(_pageEnvTrans_und, {x=0,y=286,w=513,h=1,color={0,0,0}}) -- black title div
apply.und.trans = {Button:new(_pageEnvTrans_und, {x=166,y=318,w=61,img='apply_100',imgType=3,action=applyLayout,param={'trans',''},helpL=helpL_applySize})}
apply.und.trans[2] = Button:new(_pageEnvTrans_und, {x=228,y=318,w=61,img='apply_150',imgType=3,action=applyLayout,param={'trans','150%_'},helpL=helpL_applySize})
apply.und.trans[3] = Button:new(_pageEnvTrans_und, {x=290,y=318,w=61,img='apply_200',imgType=3,action=applyLayout,param={'trans','200%_'},helpL=helpL_applySize})
Spinner:new(_pageEnvTrans_und, {x=182,y=372,w=149,title='PLAY RATE SIZE',action=paramSet,param='trans_rate_size',valsTable=transRateVals,helpR=help_playRate,helpL=helpL_layout})

_transPrefsStroke = Element:new(_pageEnvTrans_und, {x=0,y=431,w=513,h=176,color={129,137,137}}) -- stroke
_transPrefs = Element:new(_transPrefsStroke, {x=1,y=1,w=511,h=174,color={38,38,38},helpL=helpL_pref}) -- fill

Button:new(_transPrefs, {x=192,y=26,img='center_transport',imgType=3,action=actionToggle,param=40533})
Element:new(_transPrefs, {x=231,y=26,w=150,h=30,text={str='Center transport',style=2,align=4,col={129,137,137}}})
Button:new(_transPrefs, {x=29,y=69,img='show_play_rate',imgType=3,action=actionToggle,param=40531})
Element:new(_transPrefs, {x=68,y=69,w=150,h=30,text={str='Show Play Rate',style=2,align=4,col={129,137,137}}})
Button:new(_transPrefs, {x=29,y=112,img='time_sig',imgType=3,action=actionToggle,param=40680})
Element:new(_transPrefs, {x=68,y=112,w=150,h=30,text={str='Show Time Signature',style=2,align=4,col={129,137,137}}})
Button:new(_transPrefs, {x=285,y=69,img='next_prev',imgType=3,action=actionToggle,param=40868})
Element:new(_transPrefs, {x=324,y=69,w=150,h=30,text={str='Use Home/End for Markers',style=2,align=4,col={129,137,137}}})
Button:new(_transPrefs, {x=285,y=112,img='play_text',imgType=3,action=actionToggle,param=40532})
Element:new(_transPrefs, {x=324,y=112,w=150,h=30,text={str='Show play state as text',style=2,align=4,col={129,137,137}}})

--THEME PARAMETERS

_themeParameterPage_und = Element:new(_subPageContainer, {x=0,y=40,w=513,h=639})
_genericThemeTitle = Element:new(_themeParameterPage_und, {x=158,y=-80,w=200,h=30,text={str='---',style=2,align=5,col={129,137,137}}})

for i=1,10 do
  local _genbox = Element:new(_themeParameterPage_und, {x=26,y=52*(i-1),w=451,h=42, visible=true})
  local _genFadebg = FaderBg:new(_genbox, {x=0,y=0,w=451,h=17,color={71,73,73},action=doFader,param=i})
  Element:new(_genFadebg, {x=226,y=2,w=1,h=13,color={255,255,255,64}}) --zero line
  local f = Fader:new(_genFadebg, {x=1,y=-4,action=doFader,param=i})
  f.onChange, _genFadebg.onChange = reaper.ThemeLayout_RefreshAll,reaper.ThemeLayout_RefreshAll
  Readout:new(_genbox, {x=0,y=22,w=300,h=11,action=doGenericFader,param={i},text={str='---',style=2,align=4,col={129,137,137}}})
  local ro = Readout:new(_genbox,{x=399,y=20,w=52,h=15,userEntry=true,action=doGenericFader,moColor={40,40,40},param={i},text={str='',align=6,col={129,137,137}}})
  f.readout, _genFadebg.readout = ro,ro
end

--HELP
_helpL = Element:new(_pageContainer, {x=-144,y=500,w=115,h=200,text={str='',style=2,align=2,wrap=true,vCenter=false,lineSpacing=14,col={129,137,137}}})
Element:new(_helpL, {x=18,y=-25,w=97,h=19,img='helpHeader_l'})
_helpR = Element:new(_pageContainer, {x=542,y=200,w=115,h=200,text={str='Show play state',style=2,align=0,wrap=true,vCenter=false,lineSpacing=14,col={129,137,137}}})
Element:new(_helpR, {x=0,y=-25,w=97,h=19,img='helpHeader_r'})

  --------- RUNLOOP ---------

needReaperStateUpdate = 1
paramGet = 1
resize = 1
redraw = 1
lastchgidx = 0
chgsel = 1
oldTheme = nil
isGenericTheme = true
mouseXold = 0
mouseYold = 0
mouseWheelAccum = 0 -- accumulated unused wheeling
trackNames = {}
trackNamesW = {}
envcp_LabelMeasureIdx = nil
tcpLayouts = {}
envs = {}
selectedTracks = {}
activeMouseElement = nil
_helpL.y, _helpR.y = 10000,10000
editPage = tonumber(reaper.GetExtState(sTitle,'editPage')) or 1
editPage2 = tonumber(reaper.GetExtState(sTitle,'editPage2')) or 1 -- for non-def themes
drawScale = 1

indexParams()
themeCheck()
getDock()
doActivePage()
doActiveLayout()
doHelpVis()

function runloop()

  themeCheck()
  getDock()
  getDpi()

  chgidx = reaper.GetProjectStateChangeCount(0)
  if chgidx ~= lastchgidx then
    if #trackNames ~= (reaper.CountTracks(0)-1) then -- the track count has changed, rebuild from scratch
      trackNames = {}
      envs = {}
      tcpLayouts = {}
      selectedTracks = {}
    end
    needReaperStateUpdate = 1
    lastchgidx = chgidx
  end

  if needReaperStateUpdate == 1 then
    doActivePage()
    local trackCount = reaper.CountTracks(0)-1
    measureTrackNames(trackCount)
    measureEnvNames(trackCount)
    redraw = 1
  end
  needReaperStateUpdate_cnt = (needReaperStateUpdate_cnt or 0) + 1
  if needReaperStateUpdate == 1 or needReaperStateUpdate_cnt > 3 then
    getReaperDpi()
    root:doUpdateState()
    needReaperStateUpdate_cnt = 0
    needReaperStateUpdate = 0
  end

  -- mouse stuff
  local isCap = (gfx.mouse_cap&1)
  now = reaper.time_precise()
  
  if gfx.mouse_x ~= mouseXold or gfx.mouse_y ~= mouseYold or (firstClick ~= nil and last_click_time ~= nil and last_click_time+.25 < now) then
    firstClick = nil
  end
  
  if gfx.mouse_x ~= mouseXold or gfx.mouse_y ~= mouseYold or isCap ~= mouseCapOld or gfx.mouse_wheel ~= 0  then
    local wheel_amt = 0
    if gfx.mouse_wheel ~= 0 then
      mouseWheelAccum = mouseWheelAccum + gfx.mouse_wheel
      gfx.mouse_wheel = 0
      wheel_amt = math.floor(mouseWheelAccum / 120 + 0.5)
      if wheel_amt ~= 0 then mouseWheelAccum = 0 end
    end

    local hit = root:hitTest(gfx.mouse_x,gfx.mouse_y)
    if isCap == 0 and mouseCapOld == 1 then -- mouse-up
      if activeMouseElement ~= nil and hit == activeMouseElement then -- still over element
        activeMouseElement:mouseUp(gfx.mouse_x,gfx.mouse_y)
      end
      if activeMouseElement ~= nil and activeMouseElement.dragStart ~= nil then
        activeMouseElement.dragStart, activeMouseElement.dragStartValue = nil, nil
      end
    end

    if isCap == 0 or mouseCapOld == 0 then -- uncaptured mouse-down or mouse-move
      if activeMouseElement ~= nil and activeMouseElement ~= hit then
        activeMouseElement:mouseAway()
      end
      activeMouseElement = hit 
      doHelp()
    end

    if activeMouseElement ~= nil then

      if isCap == 0 or mouseCapOld == 0 then -- uncaptured mouse-down or mouse-move
        activeMouseElement:mouseOver()
      end
      if wheel_amt ~= 0 then
        activeMouseElement:mouseWheel(wheel_amt)
      end
      
      if isCap == 1 then
        local x,y = gfx.mouse_x,gfx.mouse_y
        activeMouseElement:mouseDown(gfx.mouse_x,gfx.mouse_y)
        
        if firstClick == nil or last_click_time == nil then 
          firstClick = {gfx.mouse_x,gfx.mouse_y}
          last_click_time = now
        else if now < last_click_time+.25 and math.abs((x-firstClick[1])*(x-firstClick[1]) + (y- firstClick[2])*(y- firstClick[2])) < 4 then 
          activeMouseElement:doubleClick() 
          firstClick = nil
          else
            firstClick = nil
          end 
        end
          
      end
      
    end
    mouseXold, mouseYold, mouseCapOld = gfx.mouse_x, gfx.mouse_y, isCap
  end

  if paramGet == 1 then
    root:doParamGet()
    if isGenericTheme == true then doGenericParams() end
    paramGet = 0
  end

  if resize == 1 or root.drawW ~= gfx.w*drawScale_inv_mac or root.drawH ~= gfx.h*drawScale_inv_mac then -- window resized
    root.drawW, root.drawH = gfx.w*drawScale_inv_mac,gfx.h*drawScale_inv_mac
    root:onSize()
    root:draw()
    resize,redraw = 0,0
  elseif redraw == 1 then
    root:draw()
    redraw = 0
  end

  if ctheme_param_needsave ~= nil then
    if (gfx.mouse_cap&1)==0 and (ctheme_param_needsave[2] == nil or now > ctheme_param_needsave[2]) then
      local tmp,tmp,value = reaper.ThemeLayout_GetParameter(ctheme_param_needsave[1])
      reaper.ThemeLayout_SetParameter(ctheme_param_needsave[1],value,true) 
      ctheme_param_needsave = nil
    end
  end

  gfx.update()
  local c = gfx.getchar()
  if c >= 0 then
    if c == 25 or (c == 26 and (gfx.mouse_cap&8)==8) then  -- ctrl+y or ctrl+shift+z
      reaper.Main_OnCommand(40030,0) -- redo
    elseif c == 26 then -- ctrl+z
      reaper.Main_OnCommand(40029,0) -- undo
    end
    reaper.runloop(runloop)
  end
end

gfx.clear = 0x454545
getDpi()
runloop()
redraw = 1 -- temporary workaround of REAPER bug

function storeTable(title,table,parent)
  for i, v in pairs(table) do
    local p = ''
    if parent~=nil then p = parent..'.' end
    if type(v)=='table' then storeTable(title,v,i) else reaper.SetExtState(sTitle,title..'.'..p..i,v,true) end
  end
end

function Quit()
  d,x,y,w,h=gfx.dock(-1,0,0,0,0)
  reaper.SetExtState(sTitle,"dock",d,true)
  reaper.SetExtState(sTitle,"wndx",x,true)
  reaper.SetExtState(sTitle,"wndy",y,true)
  reaper.SetExtState(sTitle,'editPage',editPage,true)
  reaper.SetExtState(sTitle,'editPage2',editPage2,true)
  reaper.SetExtState(sTitle,'paletteCurrent',palette.current,true)
  reaper.SetExtState(sTitle,'activeLayoutTcp',activeLayout.tcp,true)
  reaper.SetExtState(sTitle,'activeLayoutMcp',activeLayout.mcp,true)
  gfx.quit()
end
reaper.atexit(Quit)
