--[[Macro Browser alt.]]--
local _KEY,_NAME,_VER = "JD","Macro Browser alt.","1.2"
local _forumtopic = "http://forum.farmanager.com/viewtopic.php?f=15&t=7964"
local F = far.Flags
local minfarbuild = 3971 --LuaMacro 387: GetMoonscriptLineNumber
local build = select(4,far.AdvControl(F.ACTL_GETFARMANAGERVERSION,true)) --3,0,0,3971,0
if build<minfarbuild
and 2~=far.Message(("FAR 3 build %s or newer required.\nYour build: %s")
:format(minfarbuild,build),_NAME,"Cancel;Continue","lw")
then return
end
local Help = [[
Info:
• F3 — Краткая информация о макросе
• CtrlPgDn — Открыть таблицу макроса в lua explorer
• CtrlUp — Upvalues функции action
• CtrlDown — Environment функции action
Edit/Goto:
• F4 — Открыть макрос в редакторе, установить курсор на функцию action
• AltF4 — То же в модальном редакторе (при закрытии возврат к списку)
• CtrlPgUp — Закрыть список и перейти к файлу с макросом
Execute
• Enter, CtrlEnter — Выполнить макрос
Filters:
• Активировать фильтр /критерий запрашивается/
│1 │2 │3 │4 │5 │6 │7 │9 │0
│Key │Area │FileName │Mask │Flags │Priority │EventGroups │Misc. │Custom
• ` — Выбор фильтра из списка
• BS — Сбросить/восстановить фильтр
• Ctrl-H — Показать только активные макросы (Area.Current и Common)
Sort
• упорядочить по:
Ctrl .. │F1 │F2 │F3 │F4 │F5 │F6 │F10
│Area │Key │Descr │Id │-- │File │Custom
• CtrlF12 — выбор критерия сортировки из списка
• CtrlF11 — задать критерий пользовательской сортировки
Прочее
• CtrlR, CtrlShiftR — перезагрузить макросы
• CtrlRight/Left — управление шириной колонки клавиш
]]
local Bottom = [[
Info:F3,Enter | Edit:F4,AltF4 | Goto:CtrlPgUp | Filters:` | Sort:CtrlF12 | Help: F1]]
local _path = Macro and mf.fsplit(...,0x1+0x2) --todo: get rid of mf; check what if required from curdir
or win.GetEnv"FARPROFILE"..[[\Macros\modules\]]
local _hlf = "MacroBrowserAlt.hlf"
local S = mf.mload(_KEY,_NAME) or {} -- Settings
local curSort = S.Sort or "Area"
local sortInv = S.SortInv
local maxKeyW = S.maxKeyW or 25
local success,res = pcall(require,"le")
local le = type(res)=="function" and res or function()far.Message([[
Lua Explorer „Advanced“ is required for this function.
http://forum.farmanager.com/viewtopic.php?f=60&t=7988
More info: ]].._forumtopic,"")end
local lua_explorer = le
local success,res = pcall(require,"rebind")
local rebind = success and res
local menuFlagsSA = {FMENU_SHOWAMPERSAND=1,FMENU_WRAPMODE=1}
local menuFlagsAH = {FMENU_WRAPMODE=1,FMENU_AUTOHIGHLIGHT=1}
---------------------------------------
--common helpers
local areaStr =
[[Shell Info QView Tree Search FindFolder Viewer Editor Dialog Menu MainMenu
UserMenu Disks ShellAutoCompletion DialogAutoCompletion Help Other Common]]
local function makeArray(Str)
local A = {}; for S in Str:gmatch("%S+") do A[#A+1] = S end; return A
end
local function inAreas(area,Areas)
if string.find(Areas:lower(),"%f[%a]"..area:lower().."%f[%A]") then return true end
end
local function menuMessage(text,title,bottom)
local items = {}
for s in text:gmatch"[^\r\n]+" do
items[#items+1] = (s=="\1") and {separator=true} or {text=s}
end
local props = {Title=title, Bottom=bottom, Flags="FMENU_SHOWAMPERSAND", -- or menuFlagsSA
HelpTopic=("<%s%s>"):format(_path,_hlf)} --todo hlf
local res = far.Menu(props,items)
return res and res.text
end
local function MessagePopup(msg,title,flags,delay)
local s = far.SaveScreen()
far.Message(msg,title or "","",flags)
win.Sleep(delay or 500); far.RestoreScreen(s)
end
local macropath = win.GetEnv"FARPROFILE"..[[\Macros\]]
local scriptspath = macropath..[[scripts\]]
local function normpath(path)
return path:gsub(scriptspath,""):gsub(macropath,"")
end
-->> from LuaMacro\utils.lua
local ExpandKey, StringToFlags, FlagsToString, GetMoonscriptLineNumber do
local function getUpvalue(f,name)
for i=1,1000 do
local k,v = debug.getupvalue (f, i)
if k==nil then break end
if k==name then return v, i end
end
end
local utils = getUpvalue(eval,"utils")
FlagsToString = utils.FlagsToString
GetMoonscriptLineNumber = utils.GetMoonscriptLineNumber
local AddRecordedMacro = getUpvalue(utils.LoadMacros,"AddRecordedMacro")
ExpandKey = getUpvalue(AddRecordedMacro,"ExpandKey")
StringToFlags = getUpvalue(AddRecordedMacro,"StringToFlags")
assert(ExpandKey and StringToFlags and FlagsToString and GetMoonscriptLineNumber)
end
local function CheckFileName (mask, name)
return far.ProcessName("PN_CMPNAMELIST", mask, name, "PN_SKIPPATH")
end
--<<
---------------------------------------
--error handlers
local pcall_last_err
local function xxcall (f,err,...) --proxy helper similar to xpcall, but different
local res = {pcall(f,...)}
if res[1] then return unpack(res,2) else return err(res[2],...) end
end
local function err_ex(msg,...)
local msg = msg.."\n\2"..debug.traceback("",2):gsub("\t"," ")
local buttons = select('#',...)>0 and "Ok;&Inspect args" or "Ok"
repeat
local a = far.Message(msg,"Catching error:",buttons,"wl")
if a==2 then
lua_explorer({...},tostring(f)) --to view args
end
until a~=2
return "break2" --todo make it other way
end
local getInfoStr --function [forward declaration]
local function err_showmacro(msg,m)
msg = msg.."\n\2"..getInfoStr(m)
repeat
local a = far.Message(msg,"User function error:","Ok;&Inspect macro;Cancel","wl")
if a==2 then lua_explorer(m)
elseif a==3 or a==-1 then
--pcall_last_err = msg;
return false,"break"
end
until a~=2
end
local function err_customsort(msg,m1,m2,customsort)
msg = msg..("\n\nCustom sort: %q"):format(customsort)
.."\n\2"..getInfoStr(m1).."\n\2"..getInfoStr(m2)
repeat
local a = far.Message(msg,"User sort error:","Ok;&Inspect macros;Cancel","wl")
if a==2 then lua_explorer{m1=m1,m2=m2,customsort=customsort}
elseif a==3 or a==-1 then
pcall_last_err = msg; return "error"
end
until a~=2
end
---------------------------------------
--custom expressions prompt/check/load
local function luaexp_check(expr)
if not expr then return end
expr = expr:find("return") and expr or "return "..expr
local f,err = loadstring(expr)
if not f then
far.Message(err,"Error in expression",nil,"wl")
else return f
end
end
local function luaexp_prompt(Title,Prompt)
local expr
repeat
expr = far.InputBox (nil, Title, Prompt, Title, nil, nil, nil, F.FIB_NONE) --history
until not expr or luaexp_check(expr)
return expr
end
---------------------------------------
--prepare info for filters
local areas = makeArray(areaStr)
areas.Title = "Areas"; areas.flags = menuFlagsAH
local flags = makeArray[[
EnableOutput RunAfterFARStart EmptyCommandLine NotEmptyCommandLine
NoFilePanels NoFilePPanels NoPluginPanels NoPluginPPanels NoFolders NoPFolders
NoFiles NoPFiles Selection PSelection NoSelection NoPSelection
EVSelection NoEVSelection NoSendKeysToPlugins]]
flags.Title = "Flags"; flags.flags = menuFlagsAH
local eventgroups = {"ViewerEvent", "EditorEvent", "EditorInput", "DialogEvent", "ExitFAR"}
eventgroups.Title = "Event groups"; eventgroups.flags = menuFlagsAH
local masks,files,prios --= {Title="filemasks:"},{Title="filenames:"},{Title="priorities:"}
local nomask,nofile,noprio = "[no mask]","[no file]","[not specified]"
local function collectInfo(m) --callback
local mask
if m.filemask then mask = m.filemask else mask = nomask end
if not masks[mask] then
masks[#masks+1] = mask
masks[mask] = 1
else
masks[mask] = masks[mask]+1
end
local name
if m.FileName then name = normpath(m.FileName) else name = nofile end
if not files[name] then
files[#files+1] = name
files[name] = 1
else
files[name] = files[name]+1
end
local prio = tostring(m.priority or noprio)
if not prios[prio] then
prios[#prios+1] = prio
prios[prio] = 1
else
prios[prio] = prios[prio]+1
end
end
---------------------------------------
--filters
local curfilter
local items
local function keymatch(m,key)
if m.keyregex then
return m.keyregex:match(key)
elseif m.key then
for w in (m.key):gmatch("%S+") do
local Expanded,n = ExpandKey(w)
for i=1,n do if Expanded[i]==key then return true end end
end
end
end
--[[
lua:far.MacroAdd ("MACROAREA_SHELL", nil, "CtrlF3", "msgbox('added on the fly')")
lua:far.MacroAdd ("MACROAREA_SHELL", "KMFLAGS_ENABLEOUTPUT", "CtrlF3", "msgbox('added on the fly')","MCTL_ADDMACRO sample")--,far.Show)
--]]
local misc_arr = {
{"internal", function(m) return m.code and not m.callbackId end};
{"MCTL_ADDMACRO", function(m) return m.callbackId end};
{"MoonScript", function(m) return m.FileName and CheckFileName("*.moon", m.FileName) end};
{"Lua", function(m) return m.FileName and CheckFileName("*.lua", m.FileName) end};
{"no key", function(m) return m.key and (m.key=="none" or m.key=="") end};
{"non-empty environment", function(m) return m.action and next(getfenv(m.action)) end};
{"using upvalues", function(m) return m.action and debug.getupvalue(m.action,1) end};
{"RunAfterFARStart",function(m) return m.flags and 0~=band(m.flags,StringToFlags"RunAfterFARStart") end};
{'\1'};
{"rebinded", function(m) return m.rebinded end};
{"disabled", function(m) return m.disabled end};
{"(db)", function(m) return (m.rebinded or m.disabled) and not m.bindfrom end};
{"(file)", function(m) return m.bindfrom end};
{"rebindable" , function(m) return m.uid end};
{"no uid", function(m) return m.area and m.action and not m.uid end};
}
local misc,misc_flt = {Title = "Misc.",flags = menuFlagsAH},{} --fixme: add actions, define breakkeys, macros
for i=1,(rebind and #misc_arr or 8) do
local k,v = unpack(misc_arr[i]) --change default bindings file to Macros\_bingdings.lua
misc[#misc+1] = k
misc_flt[k] = v
end --todo consider refactoring
local filters = {
Key = function(m,key) return m.area and keymatch(m,key) end;
Area = function(m,area)
if not m.area then return end
for area in area:lower():gmatch("%w+") do
if inAreas(area,m.area) then return true end
end
end;
File = function(m,name) return ((m.FileName or nofile):find(name,1,"plain")) end; --name is any substring --??
Mask = function(m,mask) return ((m.filemask or nomask):find(mask,1,"plain")) end;
Priority = function(m,prio) return tostring(m.priority or noprio)==prio end;
Flags = function(m,flags) local f = StringToFlags(flags); return band(m.flags or 0,f)==f end;
Events = function(m,event) return m.group and m.group:lower():find(event:lower(),1,"plain") end;
Custom = function(m,f)
local env = setmetatable({},{__index=function(_,i) return m[i] or _G[i] end})
f = setfenv(f,env) --todo get rid of setfenv in cycle??
return xxcall(f,err_showmacro,m)
end;
Reset = function() return true end;
Misc = function(m,n) return xxcall(misc_flt[n],err_showmacro,m) end;
--Last = true;
--list = true;
}
--Last = function() return filterBy(unpack(LastFilter)) end
local prefiltered --flag
local LastFilter = {}
local function filterBy(fname,criteria,param)
--if type(criteria)=="function" then criteria = criteria(param) end --todo remove this?
local f = (fname=="Custom") and luaexp_check(criteria) --todo move outside
if not (criteria or f) and fname~="Reset" then return "break2" end
LastFilter = (fname=="Last" or fname=="Reset") and LastFilter --не запоминать Last и Reset
or {fname,criteria}
curfilter = criteria
criteria = f or criteria
local visibles = 0
local brk,res
for i=1,#items do
local it = items[i]
if not it.separator and it.macro then
if brk~="break" then res,brk = filters[fname](it.macro,criteria) end
it.hidden = not res
visibles = res and visibles+1 or visibles
end
end
if visibles==0 then
mf.postmacro(MessagePopup,curfilter.." not found!",nil,nil,1000)
if prefiltered then return "break2" end
end
end
---------------------------------------
--get filters
local function ask4Key()
local s = far.SaveScreen()
far.Message(""," Press key...","")
local key = mf.waitkey()
far.Message(key," Press key...","")
win.Sleep(500); far.RestoreScreen(s)
return string.gsub(key,"%f[%u]Alt","LAlt"):gsub("^Ctrl","LCtrl"):lower()
end
local function makeMenuItems(t,str)
local Items,pos = {}
for i=1,#t do
local item = t[i]
local separator = item:find("^\1")
local hidden = item:find("^%z")
Items[i] = { separator=separator, hidden=hidden, item=item,
text=separator and item:sub(2,-1) or
("[%3d] %s"):format(t[item] or i,item) }
pos = pos or str and item:find(str,1,"plain") and i
end
return Items,pos
end
local function ask4(t,str)
local Items,pos = makeMenuItems(t,str)
local i = far.Menu({Title=t.Title,Flags=t.flags or menuFlagsSA,SelectIndex=pos},Items)
return i and i.item
end
local function getCurFile()
local name = (Area.Editor and Editor.FileName or
Area.Viewer and Viewer.FileName or
APanel.Path.."\\"..APanel.Current) --todo check far/e
return name and CheckFileName("*.lua,*.moon",name) and normpath(name)
end
local getCriteria = {
Reset = function() return false end;
Key = ask4Key;
Area = function() return ask4(areas,Area.Current) end;
File = function() return ask4(files,getCurFile()) end;
Mask = function() return ask4(masks) end;
Flags = function() return ask4(flags) end;
Priority = function() return ask4(prios) end;
Events = function() return ask4(eventgroups) end;
Custom = function()
return luaexp_prompt("Custom filter", "Enter condition (e.g: priority and priority<50)") --todo hlf
end;
Misc = function() return ask4(misc) end; --todo
}
--[[
local doFilter = {}
for k,v in pairs(filters) do
doFilter[k] = function(criteria) return filterBy(k,criteria or getCriteria[k]()) end
end
--]]
local doFilter = setmetatable({},{__index=function(_,k)
return function(criteria) return filterBy(k,criteria or getCriteria[k]()) end
end})
--[[
doFilter.Custom = function(criteria) --todo loadstring custom function itself here
return filterBy("Custom",criteria or getCriteria.Custom())
end
--]]
doFilter.Last = function() return filterBy(unpack(LastFilter)) end
local filtersOrd = makeArray"Key Area File Mask Priority Flags Events \1 Misc Custom Reset Last"
filtersOrd.Title = "Choose filter:"; filtersOrd.flags = menuFlagsAH
function doFilter.list()
local flt = ask4(filtersOrd,LastFilter and LastFilter[1])
return not flt and "break2" or doFilter[flt]()
end
---------------------------------------
--get macros
local getMacros do --function
local areaArr = makeArray(areaStr:lower())
local areaCodes = makeArray"S I Q T s F V E D m M U d A a H O C"
--assert(#areaArr==#areaCodes and #areaArr==18)
--codedArea = S.codedArea and assert(loadstring(S.codedArea)) or function(areas)
local function codedArea (areas)
local a = ""
for i=1,#areaArr do a = a..(inAreas(areaArr[i],areas) and areaCodes[i] or "·") end
return a
end
function getMacros()
local events,macros = {},{}
masks,files,prios = {Title="filemasks:"},{Title="filenames:"},{Title="priorities:"}
for k=1,math.huge do
local m = mf.GetMacroCopy(k)
if not m then break end
collectInfo(m)
if m.area then
m.codedArea = codedArea(m.area)
if (not m.disabled or m.action) -- exclude disabled internal macros
and not m.hidden then
macros[#macros+1] = m
end
else
events[#events+1] = m
end
end
return events,macros
end
end
---------------------------------------
--make list
local getItemsGroupedByFile --function, forward declaration
local sortBy --table, forward declaration
local getItems do --function
local formatMacro = S.formatMacro and assert(loadstring(S.formatMacro)) or function(m)
local fmt = "%18s│%s"..("%%-%s.%ss"):format(maxKeyW,maxKeyW).."%s %s"
local border = m.key:len()>maxKeyW and "…" or "│" --…~»
local rebinded = m.rebinded and "*" or " "
return
fmt:format(m.codedArea, rebinded, m.key, border, m.description or "id="..m.id)
end
local formatEvent = S.formatEvent and assert(loadstring(S.formatEvent)) or function(m)
return ("%-18s│ %s"):format(m.group, m.description or "id="..m.id)
end
function getItems(sortf,events,macros)
--if sortf=="grpFile" then return getItemsGroupedByFile() end --todo
sortf = type(sortf)=="function" and sortf or sortBy[sortf]
if sortf then
if sortInv then
local f = sortf; sortf = function(a,b) return not (f(a,b) or not f(b,a)) end
end
table.sort(macros, sortf); table.sort(events, sortf)
end
local items = {}
--items[#items+1] = { separator=true, text="Macros" }
for i,m in ipairs(macros) do
items[#items+1] = { text=formatMacro(m), grayed=m.disabled,
checked=inAreas(Area.Current,m.area),
macro=m }
end
items[#items+1] = { separator=true, text="Event handlers" }
for i,m in ipairs(events) do
items[#items+1] = { text=formatEvent(m), macro=m }
end
return items
end
end
--table.insert(items,{disable=true, text=""})
--table.insert(items,{disable=true, text=""})
--table.insert(items,{separator=true,
-- text=(" ══════════════════╤•%s "..("═"):rep(150)):format(normpath(m.FileName))})
function getItemsGroupedByFile() --declared before --todo kill empty sections
local events,macros = getMacros()
local items = getItems("",events,macros)
table.remove(items,#macros+1)
table.sort(items,function(a,b)
return (a.macro.FileName or ""):lower()<(b.macro.FileName or ""):lower() -- todo use sortBy.File function??
end)
local i=1; items[0]={}
while i<#items do
local m0,m = items[i-1].macro or {},items[i].macro
if m0.FileName~=m.FileName then
local fname = normpath(m.FileName)
table.insert(items,i,{disable=true,
text=("══════════════════╤►%s "..("═"):rep(75-fname:len())):format(fname)})
i=i+1
end
i=i+1
end
curSort = "grpFile"; sortInv = false --todo save??
return items
end
---------------------------------------
--sort
local CustomSortingCriteria --function, forward declaration
sortBy = { --declared before
--Area =function(a,b) return (a.area or a.group) <(b.area or b.group) end;
Area =function(a,b) return (a.codedArea or a.group) <(b.codedArea or b.group) end;
Key =function(a,b) return (a.key or a.group) <(b.key or b.group) end; --todo check
Descr=function(a,b) return (a.description or "") <(b.description or "") end;
Id =function(a,b) return a.id < b.id end;
File =function(a,b) return (a.FileName or ""):lower()<(b.FileName or ""):lower() end; --todo need FileName column
Custom =function(a,b) return xxcall(function(a,b)
return pcall_last_err or CustomSortingCriteria(a)<CustomSortingCriteria(b)
end,err_customsort,a,b,curSort) end;
}
--[[custom expressions samples:
description and description:len() or 0
(key or group):len()
area and area:len() or 0
area and select(2,area:gsub("%w+","") or 0
--]]
local function askCustomSortingCriteria()
local expr = luaexp_prompt("Custom sort", "Enter criteria (e.g.: id)") --todo hlf
if expr then --??show in title
local f = luaexp_check(expr)
CustomSortingCriteria = function(m)
local env = setmetatable({},{__index=function(_,i) return m[i] or _G[i] end})
f = setfenv(f,env)
return xxcall(f,err_showmacro,m)
end
return true
end
end
local sort = makeArray"Area Key Descr Id \0 File \1 \0 \0 Custom Setup"
sort.Title = "Sort by"; sort.flags = menuFlagsAH
local function doSort(criteria)
if not criteria then return end
--if criteria=="File" then items = getItemsGroupedByFile(); return end --fixme position in list (grpFile)
if criteria=="Setup" then return askCustomSortingCriteria() end
if criteria=="Custom" then
if (not CustomSortingCriteria or pcall_last_err)
and not askCustomSortingCriteria() then
return
end
pcall_last_err = false
end
if criteria==curSort then sortInv = not sortInv else sortInv = false end
items = getItems(sortBy[criteria],getMacros())
curSort = criteria
if curSort~="Custom" then
S.Sort=curSort; S.SortInv=sortInv; mf.msave(_KEY,_NAME,S)
end --todo save custom too
if curfilter then doFilter.Last() end
end
---------------------------------------
--actions with list items
local function execute(m)
local EnableOutput = m.group or band(m.flags,StringToFlags"EnableOutput")~=0
if m.action then
mf.postmacro(function()
mmode(1,EnableOutput and 0 or 1)
m.action() --condition? args? AKey?
--mmode(1,1)
end)
else
far.MacroPost(m.code,EnableOutput and F.KMFLAGS_ENABLEOUTPUT or nil)
end
MessagePopup"OK"
end
--[=[
([[
callbackId: %s
callback: %s
guid: %s
]]):format(tostring(m.callbackId),tostring(m.callback),win.Uuid(m.guid[1]))
--]=]
--local
function getInfoStr(m) --declared before
local str,title
if m.area then
local flags,filemask,priority,condition,action,code = "n/a","n/a","n/a","n/a","n/a","n/a"
if not m.code then
title = "Macro" -- loaded from user files
flags = FlagsToString(m.flags)
filemask,priority = m.filemask or "", m.priority or ""
action,condition = tostring(m.action),m.condition and tostring(m.condition) or ""
else
if m.guid then
title = "Macro: MCTL_ADDMACRO" -- far.AddMacro
else
title = "Macro: internal" -- recorded via Ctrl.
flags = FlagsToString(m.flags)
end
code = m.code and m.code:gsub("\r?\n"," ") or title~="Macro" and "n/a" or ""
if code:len() > 50 then code = code:sub(1,47).."..." end
end
str = ([[
description │ %s
area │ %s
key │ %s
flags │ %s
filemask │ %s
priority │ %s
condition │ %s
action │ %s
code │ %s
%s
%s]]) :format(m.description or "",
m.area,
m.key,
flags,
filemask,
priority,
condition,
action,
code,
"\1",
m.FileName or "n/a")
else
title = "Event handler"
str = ([[
description │ %s
group │ %s
filemask │ %s
priority │ %s
condition │ %s
action │ %s
%s
%s]]) :format(m.description or "",
m.group,
m.filemask or "",
m.priority or "",
m.condition and tostring(m.condition) or "",
m.action and tostring(m.action) or "",
"\1",
m.FileName)
end
return str,title
end
local function showInfo(m)
--local msg, title = getInfoStr(m)
--far.Message(msg,title,nil,"ln")
menuMessage(getInfoStr(m))
end
local le_process = setmetatable({},{__index=function(t,key)
return function(m)
lua_explorer(m.action,m.description or "id="..m.id,key)
end
end})
--todo prompt for editor reload
local function Edit(m,flags)
local fname = m.FileName
if not fname then showInfo(m); return end
local pos = m.defline or m.action and debug.getinfo(m.action,"S").linedefined
pos = CheckFileName("*.moon",fname) and GetMoonscriptLineNumber(fname,pos) or pos --
editor.Editor(fname,nil,nil,nil,nil,nil,flags,pos)
end
local function GotoPanel(m)
if not m.FileName then showInfo(m); return end
local path,name = m.FileName:match"^(.-)([^\\/]+)$"
far.Show(m.FileName,path,name)
panel.SetPanelDirectory (nil, 1, path)
Panel.SetPath(0,path,name)
local w = far.AdvControl (F.ACTL_GETWINDOWINFO)
if band(w.Flags,F.WIF_MODAL)==0 then
far.AdvControl (F.ACTL_SETCURRENTWINDOW,1)
far.AdvControl (F.ACTL_COMMIT)
return "break"
end
end
local function disableMacro(m,item) --toggle
if m.bindfrom then
return Edit(m.bindfrom)
elseif m.uid then
item.grayed = rebind.MacroDisable_Msg(m.uid)
end
end
local function rebindMacro(m,item)
if m.bindfrom then
return Edit(m.bindfrom)
elseif m.uid and rebind.MacroRebind_Prompt(m.uid) then
far.MacroLoadAll()
rebind = require"rebind" --important!!
items = getItems(curSort,getMacros())
if curfilter then doFilter.Last() end
end
end
local function insertRebindTemplate(m)
local UTF8 = 65001
if m.bindfrom then
return Edit(m.bindfrom)
elseif m.uid then
local file = win.GetEnv"FARPROFILE" .. [[\Macros\scripts\bindings]]
local res = editor.Editor(file,nil,nil,nil,nil,nil,F.EF_NONMODAL+F.EF_IMMEDIATERETURN,nil,nil,UTF8)
local info = editor.GetInfo()
if res==F.EEC_OPEN_ERROR or not Area.Editor or info.FileName~=file then return end
local TotalLines = info.TotalLines
editor.SetPosition (nil, {CurLine=TotalLines})
editor.SetPosition (nil, {CurPos=editor.GetString().StringLength+1})
local template = ("\n\n"..[[
Rebind {
uid=%q;
key=%q;
disabled=false;
}
]]):format(m.uid,m.rebinded or m.key)
local template_autouids = ("\n\n"..[[
Rebind {
FileName=%q;
description=%q;
key=%q;
disabled=false;
}
]]):format(m.FileName:match"[^\\]+$",m.description,m.rebinded or m.key)
MessagePopup("Inserting template...")
print(m.uid:find("^≈") and template_autouids or template)
editor.SetPosition (nil, {CurLine=TotalLines+2})
editor.Select (nil, {BlockType=F.BTYPE_STREAM,BlockStartPos=1,BlockHeight=10})
return "break"
end
end
---------------------------------------
--actions with list
local function reloadMacros()
if far.MacroLoadAll() then far.Message("Macros reloaded!","") end
--rebind = require"rebind" --todo?
items = getItems(curSort,getMacros())
if curfilter then doFilter.Last() end
end
local function changeWidth(d)
maxKeyW = maxKeyW+d>4 and maxKeyW+d or maxKeyW
S.maxKeyW=maxKeyW; mf.msave(_KEY,_NAME,S)
items = getItems(curSort,getMacros())
if curfilter then doFilter.Last() end
end
local function showHelp()
local res = menuMessage(Help,_NAME..' r'.._VER)
if not res or not res:find("Info") then return end --todo
if win.GetFileAttr (_path.._hlf) then --todo hlf
far.ShowHelp (_path.._hlf, "Contents", F.FHELP_CUSTOMFILE)
else
win.ShellExecute (nil, nil, forumtopic)
end
end
---------------------------------------
--break keys
local function expandBreakKeys(BreakKeysArr)
local newBreakKeysArr = {}
for _,item in ipairs(BreakKeysArr) do
for key in item.BreakKey:gmatch("%S+") do
local newitem = {}
for k,v in pairs(item) do newitem[k]=v end
newitem.BreakKey = key
table.insert(newBreakKeysArr,newitem)
end
end
return newBreakKeysArr
end
--virtual keys: http://msdn.microsoft.com/library/dd375731
local BreakKeys = expandBreakKeys {
{BreakKey="RETURN", itemAct=function(m) execute(m) return "break" end},
{BreakKey="C+RETURN", itemAct=execute}, --return to list
{BreakKey="F3", itemAct=showInfo},
{BreakKey="F4", itemAct=function(m)
Edit(m,F.EF_NONMODAL+F.EF_IMMEDIATERETURN)
return "break"
end},
{BreakKey="A+F4", itemAct=function(m)
Edit(m)
end},
{BreakKey="C+PRIOR C+NUMPAD9",itemAct=GotoPanel}, --break
{BreakKey="C+NEXT C+NUMPAD3",itemAct=function(m) lua_explorer(m,m.description or "") end},
{BreakKey="C+Up C+NUMPAD8", itemAct=le_process.upvalues},
{BreakKey="C+Down C+NUMPAD2", itemAct=le_process.env},
{BreakKey="OEM_3 C+OEM_3",listAct=doFilter.list}, -- `~
{BreakKey="1 C+NUMLOCK", listAct=doFilter.Key},
{BreakKey="2", listAct=doFilter.Area},
{BreakKey="3", itemAct=function(m) return doFilter.File(normpath(m.FileName))end},
{BreakKey="C+3", listAct=doFilter.File},
{BreakKey="4", listAct=doFilter.Mask},
{BreakKey="5", listAct=doFilter.Flags},
{BreakKey="6", listAct=doFilter.Priority},
{BreakKey="7", listAct=doFilter.Events},
-- {BreakKey="8", listAct=doFilter.xxx},
{BreakKey="9", listAct=doFilter.Misc},
{BreakKey="0", listAct=doFilter.Custom},
{BreakKey="BACK CANCEL", listAct=function() -- Backspace CtrlBreak
if curfilter then doFilter.Reset() else doFilter.Last() end end},
{BreakKey="C+H", listAct=function() --"active macros only" filter toggle
local active = "Common "..Area.Current
if curfilter~=active then return doFilter.Area(active) else return doFilter.Reset() end
end},
{BreakKey="C+R", listAct=reloadMacros},
{BreakKey="CS+R CA+R", listAct=function() _G.RecreateLuaState = true; return "break" end},
{BreakKey="C+F1", listAct=function() doSort(sort[1]) end},
{BreakKey="C+F2", listAct=function() doSort(sort[2]) end},
{BreakKey="C+F3", listAct=function() doSort(sort[3]) end},
{BreakKey="C+F4", listAct=function() doSort(sort[4]) end},
--{BreakKey="C+F5", listAct=function() doSort(sort[5]) end},
{BreakKey="CS+F6", listAct=function() doSort(sort[6]) end},
{BreakKey="C+F6", listAct=function()
items = getItemsGroupedByFile()
if curfilter then doFilter.Last() end
end},
{BreakKey="C+F10", listAct=function() doSort(sort[10]) end},
{BreakKey="C+F11", listAct=function()
if askCustomSortingCriteria() then doSort(sort[10]) end
end},
{BreakKey="C+F12", listAct=function() doSort(ask4(sort,curSort)) end},
{BreakKey="C+Left C+NUMPAD4",listAct=function() changeWidth(-1) end},
{BreakKey="C+Right C+NUMPAD6",listAct=function() changeWidth(1) end},
{BreakKey="F1"; listAct=showHelp},
{BreakKey="ESCAPE", listAct=function() --break
if curfilter and curfilter~="Common "..Area.Current and not prefiltered then
doFilter.Reset()
else
curfilter = false; return "break"
end
end},
-- rebind module
{BreakKey="SPACE", itemAct=disableMacro},
{BreakKey="S+F4", itemAct=rebindMacro},
{BreakKey="CS+F4", itemAct=insertRebindTemplate},
}
---------------------------------------
--Main function
local MB_isRunning
local MBid = win.Uuid"C5CB672A-104B-4402-B501-B31FC34714D0"
local Pos = {}
Event { description="MBA: internal (preserve cursor position between launches)";
group="DialogEvent";
condition=function(Event,param)
if Event==F.DE_DLGPROCEND and (param.Msg==F.DN_INITDIALOG or param.Msg==F.DN_CLOSE) then
local info = param.hDlg:send(F.DM_GETDIALOGINFO)
return info and info.Id==MBid
end
end;
action=function(Event,param)
if param.Msg==F.DN_INITDIALOG then
param.hDlg:send(F.DM_LISTSETCURPOS,1,Pos)
else --param.Msg==F.DN_CLOSE
Pos = param.hDlg:send(F.DM_LISTGETCURPOS)
end
end;
}
local function browseMacros(flt,...)
mmode(1,0)
if MB_isRunning then Keys"AKey"; return end -- forbid nested calls
items = getItems(curSort,getMacros())
curfilter = false
prefiltered = flt
if flt then
local f = doFilter[flt] -- prefilter function
if not f then
far.Message("Specified function is not defined: '"..tostring(flt).."'","Error:",";Ok","wl")
return
end
if xxcall(f,err_ex,...)=="break2" then return end
end
MB_isRunning = true
local Props = {Bottom=Bottom,Id=MBid,Flags=menuFlagsSA}
local pos,bk
repeat
local sortsymb = sortInv and "↓" or "↑"
Props.Title = (" %s %-7s [%s] ")
:format(_NAME, sortsymb..curSort..sortsymb,
curfilter and curfilter or "no filter")
Props.SelectIndex = pos
bk,pos = far.Menu(Props, items, BreakKeys)
if bk then
local item = pos~=0 and items[pos]
if bk.itemAct then
if item and "break"==xxcall(bk.itemAct,err_ex,item.macro,item) then break end
elseif bk.listAct and "break"==xxcall(bk.listAct,err_ex) then--,nil,m) then
break
end
end
until not bk
MB_isRunning = false
return true
end
---------------------------------------
--using as module
if not Macro then return browseMacros end
--[[--------------------------------------------
Usage
in macro definition:
action=require"MacroBrowserAlt"
in UserMenu:
lua:require"MacroBrowserAlt"()
in scripts:
local browseMacros = require"MacroBrowserAlt"
browseMacros(filter,param)
see more samples in macros definitions (below) and in FarMenu.ini.sample
--]]--------------------------------------------
---------------------------------------
--macros definitions
Macro { description="MBA: Show available macros and event handlers";
area="Common"; key="LCtrlShiftF11"; priority=30;
action=function()
browseMacros()
end;
}
Macro { description="MBA: Choose filter:";
area="Common"; key="RCtrlShift` LCtrl`"; priority=30;
action=function()
browseMacros("list")
end;
}
Macro { description="MBA: list macros by key [choice]";
area="Common"; key="RCtrlShift1 LCtrlNumLock"; priority=30;
action=function()
browseMacros("Key")
end;
}
Macro { description="MBA: list macros by area [choice]";
area="Common"; key=""; priority=30;
action=function()
browseMacros("Area")
end;
}
Macro { description="MBA: list macros in current area";
area="Common"; key="RCtrlShift2"; priority=30;
action=function()
browseMacros("Area",Area.Current.." Common")
end;
}
Macro { description="MBA: list macros by file [choice]";
area="Common"; key=""; priority=30;
action=function()
browseMacros("File")
end;
}
Macro { description="MBA: list macros in current file";
area="Common"; filemask="*.lua,*.moon"; key="RCtrlShift3"; priority=30;
action=function()
browseMacros("File",getCurFile())
end;
}
Macro { description="MBA: list macros by mask [choice]";
area="Common"; key="RCtrlShift4"; priority=30;
action=function()
browseMacros("Mask")
end;
}
Macro { description="MBA: list macros by priority [choice]";
area="Common"; key="RCtrlShift5"; priority=30;
action=function()
browseMacros("Priority")
end;
}
Macro { description="MBA: list macros by flags [choice]";
area="Common"; key=""; priority=30;
action=function()
browseMacros("Flags")
end;
}
Macro { description="MBA: list autostart macros (flag 'RunAfterFARStart')";
area="Common"; key="RCtrlShift6"; priority=30;
action=function()
browseMacros("Flags","RunAfterFARStart")
--browseMacros("Misc","RunAfterFARStart")
end;
}
Macro { description="MBA: list event handlers by group [choice]";
area="Common"; key="RCtrlShift7"; priority=30;
action=function()
browseMacros("Events")
end;
}
Macro { description="MBA: list macros by misc. filters [list]";
area="Common"; key="RCtrlShift9"; priority=30;
action=function()
browseMacros("Misc")
end;
}
Macro { description="MBA: list macros by custom filter [choice]";
area="Common"; key="RCtrlShift0"; priority=30;
action=function()
browseMacros("Custom")
end;
}
Macro { description="MBA: list internal macros";
area="Common"; key=""; priority=30;
action=function()
--browseMacros("Custom","code and not callbackId")
browseMacros("Misc","internal")
end;
}
Macro { description="MBA: list MCTL_ADDMACRO macros (created with far.MacroAdd)";
area="Common"; key=""; priority=30;
action=function()
browseMacros("Custom","callbackId")
--browseMacros("Misc","MCTL_ADDMACRO")
end;
}
Macro { description="MBA: list MoonScript macros";
area="Common"; key=""; priority=30;
action=function()
browseMacros("Custom","FileName and FileName:find'%.[Mm][Oo][Oo][Nn]$'")
--browseMacros("Misc","MoonScript")
end;
}
Macro { description="MBA: list Lua macros";
area="Common"; key=""; priority=30;
action=function()
browseMacros("Custom","FileName and FileName:find'%.[Ll][Uu][Aa]$'")
--browseMacros("Misc","Lua")
end;
}
Macro { description="MBA: list macros w/o key";
area="Common"; key=""; priority=30;
action=function()
browseMacros("Custom","key and (key=='none' or key=='')")
--browseMacros("Misc","no key")
end;
}
Macro { description="MBA: list macros with non-empty environment";
area="Common"; key=""; priority=30;
action=function()
browseMacros("Custom","action and next(getfenv(action))")
--browseMacros("Misc","non-empty environment")
end;
}
Macro { description="MBA: list macros using upvalues";
area="Common"; key=""; priority=30;
action=function()
browseMacros("Custom","action and debug.getupvalue(action,1)")
--browseMacros("Misc","using upvalues")
end;
}
Macro { description="MBA: list macros filtered with last used filter";
area="Common"; key="RCtrlShift= Break"; priority=30;
action=function()
browseMacros(next(LastFilter) and "Last")
end;
}
Macro { description="MBA: Reload macros"; -- see advanced version here: http://forum.farmanager.com/viewtopic.php?f=60&t=8008
area="Editor"; key="CtrlEnter CtrlR"; filemask="*.lua,*.moon"; priority=40;
action=function() if far.MacroLoadAll() then far.Message("Macros reloaded","") end end;
}
Macro { description="MBA: Lua Explorer „Advanced“"; -- see here http://forum.farmanager.com/viewtopic.php?f=60&t=7988
area="Common"; key="CtrlShiftF12"; priority=30;
action=function() lua_explorer(_G,"_G") end
}