local Version = "1.1.1"
local F = far.Flags
----[[*D*]]local le=require"le"
local TreeTbl
local Info = package.loaded.regscript or function(...) return ... end
local nfo = Info {_filename or ...,
name = "TableAsTree";
description = "Представление таблицы в виде дерева в диалоге";
version = Version;
author = "BAX";
url = "";
id = "64220FEB-E3C4-480B-B939-294D02067EA9";
history = [[
22.04.2021 v1.1.1 - стартовая версия
]];
--parent_id = "";
--minfarversion = {3,0,0,4744,0};
--files = "*.cfg;*.ru.lng";
--config = function(nfo,name) end;
help = function(nfo,name) TreeTbl('?')end;
execute = function(...) return TreeTbl(...) end;
--disabled = false;
--options = {};
restriction = [[
Внимание!
В случае циклических ссылок программа зациклится!
]]
}
if not nfo then return end
--local O = nfo.options
-----------------------------------------------------
local DialogTools = require"BAX.DialogTools"
local DialogItem = DialogTools.DialogItem
-----------------------------------------------------
local function ValToStr(v)
if type(v) == 'table' or v == nil then
return ''
elseif type(v) == 'string' and string.len(v) == 16 and
win.Uuid(v) and not utf8.utf8valid(v) then
return ' = ' .. v .. ' (GUID="' .. win.Uuid(v):upper() .. '")', true
else
return ' = ' ..tostring(v)
end
end -- ValToStr
-----------------------------------------------------
local function SubTree(sName, Tbl, Prnt, sPref, nDepth, ChkDbl, Title)
ChkDbl = ChkDbl or {}
sName = sName or '…'
Title = Title or sName
local aSubTree = {}
do
local aIndexes = {}
local j = next(Tbl)
while j do
aIndexes[#aIndexes+1] = j
j = next(Tbl, j)
end
table.sort(aIndexes,
function(a1, a2)
if type(a1) ~= type(a2) then
return type(a1) < type(a2)
else
return a1 < a2
end
end
)
for _,i in ipairs(aIndexes) do
local v = Tbl[i]
local _Name
if type(i) == 'number' then
_Name = '['..i..']'
elseif type(i) == 'string' then
_Name = i
else
_Name = '['..tostring(i)..']'
end
aSubTree[#aSubTree+1] = {Name=_Name; v=v; UserData={}; Parent=Prnt; Level=nDepth+1}
if Prnt then Prnt.CanCollapse = true end
end
end
if #aSubTree > 0 then
for i=1, #aSubTree-1 do
local v = aSubTree[i]
local vStr,IsGUID = ValToStr(v.v)
v.Text = sPref .. '├─' .. v.Name .. vStr
v.lc = string.len(v.Text) - string.len(vStr)
v.Guid = IsGUID
v.UserData.ShowName = v.Text
v.UserData.FullName = sName .. '.' .. v.Name
end
do
local vStr,IsGUID = ValToStr(aSubTree[#aSubTree].v)
local iSubTree = aSubTree[#aSubTree]
iSubTree.Text = sPref .. '└─' .. iSubTree.Name .. vStr
iSubTree.lc = string.len(iSubTree.Text) - string.len(vStr)
iSubTree.Guid = IsGUID
iSubTree.UserData.ShowName = iSubTree.Text
iSubTree.UserData.FullName = sName .. '.' .. iSubTree.Name
end
end
do
local i = 1
while i <= #aSubTree do
local vRoot = aSubTree[i].v
if type(vRoot) == 'table' then
if not ChkDbl[vRoot] then
ChkDbl[vRoot] = {FullName=aSubTree[i].UserData.FullName or Title; RefItem=aSubTree[i]}
local aa = SubTree(sName .. '.' .. aSubTree[i].Name, vRoot, aSubTree[i], sPref .. (i==#aSubTree and ' ' or '│ '), nDepth+1, ChkDbl, Title)
for _,v in ipairs(aa) do
i = i + 1
table.insert(aSubTree, i, v)
end
else
aSubTree[i].Text = aSubTree[i].Text .. ' ┄┄┄┄╛ …' .. ChkDbl[vRoot].FullName:sub(Title:len()+1)
aSubTree[i].RefItem = ChkDbl[vRoot].RefItem
end
end
i = i + 1
end
end
return aSubTree
end -- SubTree
-----------------------------------------------------
local function ShowTree(sName, aTbl, a_Tree, CanEdit)
local w,h = math.floor(Far.Width * 2 / 3), math.floor(Far.Height * 2 / 3)
local aDlg = {
DialogItem{'DI_LISTBOX', 3,0,w-4,h-4, Flags='DIF_LISTNOCLOSE|DIF_LISTWRAPMODE', List=a_Tree; Text=sName; Name='Tree'};
DialogItem{'DI_TEXT', 3,h-3; Name='Text'};
DialogItem{'DI_EDIT', 3, h-2, w-4, Flags=(CanEdit and 'DIF_NONE' or 'DIF_READONLY'); Name='Value'}
}
local function fDlg(hDlg, Msg, Param1, Param2)
local function ByName(name)
for i,v in ipairs(aDlg) do
if v.Name == name then return i end
end
end
local function ASCAN(tbl, what)
for i,v in ipairs(tbl) do
if v == what then return i end
end
end
local function ChkVis(iCurr)
local parnt, Visible = a_Tree[iCurr].Parent, true
while parnt do
if parnt.Collapsed then
Visible = false
break
end
parnt = parnt.Parent
end
return Visible
end -- ChkVis
local Cont = true
while Cont do
Cont = false
if Msg == F.DN_INITDIALOG then
Msg = F.DN_LISTCHANGE
--Param1 = 1
Param2 = 1
Cont = true
elseif Msg == F.DN_LISTCHANGE then
local iEdit = ByName'Value'
local OldPos = hDlg:send(F.DM_LISTGETCURPOS, ByName"Tree").SelectPos
local iType = ASCAN({'boolean','number','string'}, type(a_Tree[OldPos].v))
if CanEdit and iType then
-- Запоминание изменения
local NewValue = hDlg:send(F.DM_GETTEXT, iEdit)
local Changed = NewValue ~= a_Tree[OldPos].v
if Changed then
NewValue = ({function(a) return a == 'true' or a == 1 end;
function(a) return tonumber(a) end;
function(a) return a end;
})[iType](NewValue)
if NewValue ~= nil then
-- Отдельная обработка GUID-а
if a_Tree[OldPos].Guid then NewValue = win.Uuid(NewValue) end
a_Tree[OldPos].v = NewValue
a_Tree[OldPos].Text = string.sub(a_Tree[OldPos].Text, 1, a_Tree[OldPos].lc) .. ValToStr(NewValue)
hDlg:send(F.DM_SETTEXT, ByName"Tree", a_Tree[OldPos].Text)
end
end
end
iType = ASCAN({'boolean','number','string'}, type(a_Tree[Param2].v))
if Param2 > 1 then
hDlg:send(F.DM_SETTEXT, ByName('Text'), far.TruncStr(('%2d. %s'):format(a_Tree[Param2].Level, a_Tree[Param2].UserData.FullName), w-6) )
hDlg:send(F.DM_SETTEXT, ByName('Value'), (a_Tree[Param2].Guid and win.Uuid(a_Tree[Param2].v):upper() or tostring(a_Tree[Param2].v)))
hDlg:send(F.DM_ENABLE, ByName('Value'), (iType and 1 or 0) )
else
hDlg:send(F.DM_SETTEXT, ByName('Text'), ('%2d. %s'):format(a_Tree[Param2].Level, sName) )
hDlg:send(F.DM_SETTEXT, ByName('Value'), '')
hDlg:send(F.DM_ENABLE, ByName('Value'), 0 )
end
elseif Msg == F.DN_CONTROLINPUT and Param2.EventType == F.KEY_EVENT and Param1 == ByName"Tree" then
local aCurrListPos = hDlg:send(F.DM_LISTGETCURPOS, ByName('Tree'))
local CurrListPos = aCurrListPos.SelectPos
local CurrLvl = a_Tree[CurrListPos].Level
local key = far.InputRecordToName(Param2)
if key == 'Enter' then
if a_Tree[CurrListPos].CanCollapse then
a_Tree[CurrListPos].Collapsed = not a_Tree[CurrListPos].Collapsed
local sStr, Lvl = a_Tree[CurrListPos].Text, (a_Tree[CurrListPos].Level-1)*2
hDlg:send(F.DM_ENABLEREDRAW, 0)
for i=CurrListPos+1, #a_Tree do
if a_Tree[i].Level <= CurrLvl then break end
if not not a_Tree[i].Flags.LIF_HIDDEN == ChkVis(i) then
a_Tree[i].Flags.LIF_HIDDEN = not a_Tree[i].Flags.LIF_HIDDEN
hDlg:send(F.DM_LISTUPDATE, ByName('Tree'), a_Tree[i])
end
end
if Lvl > 0 then
sStr = sStr:sub(1,Lvl-1) .. (a_Tree[CurrListPos].Collapsed and '╥' or '─') .. sStr:sub(Lvl+1)
a_Tree[CurrListPos].Text = sStr
hDlg:send(F.DM_SETTEXT, ByName('Tree'), sStr)
end
hDlg:send(F.DM_ENABLEREDRAW, 1)
elseif a_Tree[CurrListPos].RefItem then
local RefIndex = ASCAN(a_Tree, a_Tree[CurrListPos].RefItem)
if RefIndex then
local aPos = hDlg:send(F.DM_LISTGETCURPOS, ByName"Tree")
aPos.SelectPos = RefIndex
hDlg:send(F.DM_LISTSETCURPOS, ByName"Tree", aPos)
end
end
return true
elseif key == 'Left' then
for i=CurrListPos-1, 1, -1 do
if a_Tree[i].Level < CurrLvl then
hDlg:send(F.DM_LISTSETCURPOS, ByName('Tree'), {TopPos=aCurrListPos.TopPos, SelectPos=i})
return true
end
end
hDlg:send(F.DM_LISTSETCURPOS, ByName('Tree'), {SelectPos=1})
return true
elseif key == 'Right' then
for i=CurrListPos+1, #a_Tree do
if a_Tree[i].Level <= CurrLvl then
hDlg:send(F.DM_LISTSETCURPOS, ByName('Tree'), {aCurrListPos.TopPos,SelectPos=i})
return true
end
end
hDlg:send(F.DM_LISTSETCURPOS, ByName('Tree'), {SelectPos=#a_Tree})
return true
elseif key == 'CtrlUp' or key == 'CtrlDown' then
local iEnd, iStep = key == 'CtrlUp' and 1 or #a_Tree, key == 'CtrlUp' and -1 or 1
local iStart = CurrListPos + iStep
for i=iStart, iEnd, iStep do
if a_Tree[i].Level == CurrLvl then
local aPos = hDlg:send(F.DM_LISTGETCURPOS, ByName"Tree")
aPos.SelectPos = i
hDlg:send(F.DM_LISTSETCURPOS, ByName'Tree', aPos)
return true
end
end
--elseif key == 'CtrlF1' then
-- le(a_Tree[CurrListPos], '201: a_Tree['..CurrListPos..']')
-- *D* }
end
end -- case Msg of
end -- while Cont do
end -- fDlg
far.Dialog('7E1C96F8-D1DA-4154-8CB6-51C6F5F67F46', -1,-1, w,h,nil,aDlg, nil, fDlg)
end -- ShowTree
-----------------------------------------------------
TreeTbl = function(Tbl, Title, CanEdit)
local aTree = SubTree(Title, Tbl, nil, '', 1)
table.insert(aTree, 1, {Text=Title, UserData={FullName=Title}; Level=1})
for i,v in ipairs(aTree) do
v.Index = i
v.Flags = {}
end
ShowTree(Title, Tbl, aTree, CanEdit)
end -- TreeTbl
-----------------------------------------------------
return {
Version = Version;
TreeTable = TreeTbl;
Prompt = 'Вызов: require"BAX.TreeTable".TreeTable(Table, Title)';
}