Теперь задача оформлена в виде модуля; кроме собственно процедуры копирования в модуле есть пример создания классов и готовые классы для отрисовки прогрессбара и вывода сообщения при помощи far.Text (использование объекта этого класса вместо far.Message ускорило копирование в несколько раз(!)) -. Возможно, кому-то покажутся удобными те утилиты, которые этот модуль предоставляет.
- local C = far.Colors
- local F=far.Flags
- --------------------------------------------------------------------------------------------------
- local BAX_Round, far_InputBox, far_Menu, CopyFileEx_Lua, class_ProgressBar, class_ShowMessage,
- SparseIntToStr, MergeSettings
- --------------------------------------------------------------------------------------------------
- local class_def
- do
- local class_metatable = {}
- function class_metatable.__call(class, ...)
- local obj = setmetatable({}, class)
- if class.__init then
- class.__init(obj, ...)
- end
- return obj
- end
- function class_def()
- local class = setmetatable({}, class_metatable)
- class.__index = class
- return class
- end
- end
- local ffi = require("ffi")
- -- Формрование строки отображения целого с разделением на группы
- SparseIntToStr = function (n,g,c,w)
- -- n - выводимое число (целое)
- -- g - количество цифр в группе (по умолчанию - 3)
- -- c - символ-наполнитель (по умолчанию - chr(160))
- -- w - минимальная ширина числа. Отрицательное - прижато к левому краю
- g = g or 3
- c = c or mf.chr(160)
- local s=tostring(n)
- for i=s:len()-g+1,2,-g do
- s=s:sub(1,i-1)..c..s:sub(i)
- end
- if w then
- s = mf.strpad(s, mf.abs(w),' ', w<0 and 0 or 1)
- end
- return s
- end
- BAX_Round = function (v,c) -- v - значение; c - точность
- return c and (math.floor(v*c +.5) / c) or (math.floor(v +.5) )
- end
- far_InputBox = function (tbData)
- return far.InputBox(tbData.Id, tbData.Title, tbData.Prompt,
- tbData.HistoryName, tbData.SrcText,
- tbData.DestLength, tbData.HelpTopic, tbData.Flags )
- end
- MergeSettings = function (Old, New)
- if type(New) == 'function' then
- local _
- _, New = pcall(New)
- end
- if type(New) == 'table' then
- for i,v in pairs(New) do
- Old[i]=v
- end
- end
- return Old
- end
- local MenuColorIndex = {
- C.COL_MENUTEXT, -- подложка
- C.COL_MENUBOX, -- рамка
- C.COL_MENUTITLE, -- заголовок - верхний и нижний
- C.COL_MENUTEXT, -- Текст пункта
- C.COL_MENUHIGHLIGHT, -- HotKey
- C.COL_MENUTEXT, -- separator
- C.COL_MENUSELECTEDTEXT, -- Выбранный
- C.COL_MENUSELECTEDHIGHLIGHT,-- Выбранный - HotKey
- C.COL_MENUSCROLLBAR, -- ScrollBar
- C.COL_MENUDISABLEDTEXT, -- Disabled
- C.COL_MENUARROWS, -- '<' & '>' обычные
- C.COL_MENUARROWSSELECTED, -- '<' & '>' выбранные
- C.COL_MENUARROWSDISABLED, -- '<' & '>' Disabled
- C.COL_MENUGRAYTEXT, -- "серый"
- C.COL_MENUSELECTEDGRAYTEXT, -- выбранный "серый" -- 15
- }
- local MenuPalette = {}
- --for i,v in ipairs(MenuColorIndex) do MenuPalette[i] = v and far.AdvControl(F.ACTL_GETCOLOR,v) end
- for i,v in ipairs(MenuColorIndex) do MenuPalette[i] = far.AdvControl(F.ACTL_GETCOLOR,v) end
- local DialogPalette = {far.AdvControl(F.ACTL_GETCOLOR,far.Colors.COL_MENUTITLE),
- far.AdvControl(F.ACTL_GETCOLOR,far.Colors.COL_MENUTEXT),
- far.AdvControl(F.ACTL_GETCOLOR,far.Colors.COL_MENUBOX),
- nil
- }
- -- Реализация far.Menu (или Menu.Show) через далог.
- -- Отлиия от указанных:
- -- 1. Объединение функций:
- -- 1.1. Даёт возможность в отличие от far.Menu установит "Начальный фильтр", а от Menu.Show - BreakKeys
- -- 1.2. Items может быть и строкой (как в Menu.Show), так и таблицей (как в far.Menu);
- -- 2. Расширение функционала:
- -- 2.1. BreakKeys может быть регэкспом. (обрамлён слэшами. стандартный - не LUA - регэксп)
- -- 2.2. Фильтр также может быть регэкспом.
- -- 2.3. Фильтрация может быть регистрозависимой
- -- 2.4. Если BreakKeys - таблица, то в каждом элеиенте можно указать Action=function(hDlg, key, DlgItems
- -- DlgItems можно использовать для передачи BreakKey в вызывающую процедуру
- -- (присвоить DlgItems.BreakKey=<то, что надо>. Например, {BreakKey=_key; RealResult='qqq'})
- -- 2.5. Начальный фильтр может быть строкой (конечные слэши включают режим регэкспа), функцией
- -- (на входе - строка: на выходе - boolean) или таблицей - списком строк.
- -- 3. Нереализовано:
- -- 3.1. Указание символа для checked-строк
- far_Menu = function(Properties, Items, BreakKeys)
- if type(Items) == 'string' then
- local s=Items
- Items = {}
- for w in s:gmatch('[^\n]+') do
- Items[#Items+1]={text=w}
- end
- end
- if not Properties then Properties={} end
- local FiltRegExp, FiltCaseSens, FiltFromBeg, Title, Bottom, AutoNum, AutoAccel, FilterStr=
- Properties.RegExp, Properties.CaseSensitive, Properties.FromBegin, Properties.Title, Properties.Bottom,
- Properties.AutoNum, Properties.AutoAccel, Properties.FilterStr
- AutoAccel = false
- local DlgItems, ListNo, FiltTextId={}
- local w, h=math.max( (Title or ''):len(), (Bottom or ''):len() ), 0
- local lbItems = {}
- for _,v in ipairs(Items) do
- w = math.max(w, v.text:len()+2)
- h = h + 1
- lbItems[h] = {Text=v.text; Flags={}, Hidden = v.Hidden; Source=v.text}
- local _MenuFlagsToDialogFlags = {
- 'checked', 'separator', 'disable', 'grayed', 'hidden', 'selected'
- }
- for i,vv in ipairs(_MenuFlagsToDialogFlags) do
- if v[vv] then
- lbItems[h].Flags['LIF_'..vv:upper()] = type(v[vv]) == 'boolean' and 1 or v[vv]
- end
- end
- if v.Hidden then
- lbItems[h].Flags.LIF_HIDDEN=1
- end
- end -- for _,v in ipairs(Items) do
- -- Автонумерация
- if AutoNum then
- local iNum, Fmt=0,'%'..tostring(#lbItems):len()..'d - %s'
- for _,v in ipairs(lbItems) do
- if not (v.Flags.LIF_HIDDEN or v.Flags.LIF_SEPARATOR or v.Flags.LIF_DISABLE) then
- iNum = iNum + 1
- v.Text = Fmt:format(iNum, v.Source)
- end
- end
- w = w + tostring(#lbItems):len() + 3
- end
- -- Автопроставление акселераторов
- if AutoAccel then
- local aAccel, cAccel=''
- for _,v in ipairs(lbItems) do
- local sItem=v.Text
- for i=1,sItem:len() do
- cAccel = sItem:sub(i,i)
- if cAccel:match('[%w_]') and
- not aAccel:find(cAccel,1,true) then
- v.Text = v.Text:sub(1,i-1)..'&'..v.Text:sub(i)
- aAccel = aAccel .. cAccel
- break
- end
- end
- end
- end
- local sFilter
- local function _ApplyFilter(hDlg, Id)
- local _hidden, sItem
- if type(sFilter) == 'string' and not FiltCaseSens then sFilter = sFilter:upper() end
- for i,v in ipairs(lbItems) do
- sItem = v.Source
- if not FiltCaseSens then sItem = sItem:upper() end
- if not v.Hidden -- Перманентное сокрытие. Фильтру неподвластно
- and not v.Flags.LIF_SEPARATOR then
- if sFilter == '' then
- _hidden = false
- else
- if type(FilterStr) == 'string' then
- if FiltRegExp then
- _hidden = (regex.match(sItem, (FiltFromBeg and '^' or '') .. sFilter) == nil)
- else
- _hidden = FiltFromBeg and
- (mf.index(sItem, sFilter, FiltCaseSens and 1 or 0) ~= 0) or
- (mf.index(sItem, sFilter, FiltCaseSens and 1 or 0) < 0)
- end
- elseif type(FilterStr) == 'function' then
- _hidden = not FilterStr(v)
- elseif type(FilterStr) == 'table' then
- _hidden = true
- for _,vv in ipairs(FilterStr) do
- sFilter = vv
- if not FiltCaseSens then sFilter=sFilter:upper() end
- if sFilter:match('^/.*/$') then
- _hidden = not regex.match( sItem, sFilter:match('^/(.*)/$') )
- else
- _hidden = FiltFromBeg and
- (mf.index(sItem, sFilter, FiltCaseSens and 1 or 0) == 0) or
- (mf.index(sItem, sFilter, FiltCaseSens and 1 or 0) >= 0)
- end
- if not _hidden then break end
- end
- end
- end
- _hidden = _hidden and 1 or nil
- if lbItems[i].Flags.LIF_HIDDEN ~= _hidden then
- lbItems[i].Flags.LIF_HIDDEN = _hidden
- if hDlg then
- hDlg:send(far.Flags.DM_LISTUPDATE, Id, {Index=i, Text= lbItems[i].Text, Flags=lbItems[i].Flags})
- end
- end
- end -- if not v.Hidden
- end -- for i,v in ipairs(lbItems)
- -- Скрытие разделителей, после которых нет видимых пунктов
- local iSep
- iSep, _hidden = 0, true
- for i,v in ipairs(lbItems) do
- if v.Flags.LIF_SEPARATOR then
- if iSep > 0 then
- lbItems[iSep].Flags.LIF_HIDDEN = _hidden and 1 or nil
- if hDlg then
- hDlg:send(far.Flags.DM_LISTUPDATE, Id, {Index=iSep, Text= lbItems[iSep].Text, Flags=lbItems[iSep].Flags})
- end
- end
- iSep, _hidden = i, true
- elseif not v.Flags.LIF_HIDDEN then
- _hidden = false
- end
- end
- if iSep > 0 then
- lbItems[iSep].Flags.LIF_HIDDEN = _hidden and 1 or nil
- if hDlg then
- hDlg:send(far.Flags.DM_LISTUPDATE, Id, {Index=iSep, Text= lbItems[iSep].Text, Flags=lbItems[iSep].Flags})
- end
- end
- if hDlg then
- if sFilter == '' then
- hDlg:send("DM_SETTEXT", FiltTextId, "")
- else
- hDlg:send("DM_SETTEXT", FiltTextId, "["..sFilter..']')
- end
- end
- end -- _ApplyFilter
- -- Применение начального фильтра
- sFilter = FilterStr
- _ApplyFilter()
- w = w + 2
- h = math.min(h + 3, math.floor(Properties.MaxHeight or APanel.Height * 0.75) )-- Ограничиваю высоту 3/4 от высоты панели
- DlgItems[#DlgItems+1] = {"DI_DOUBLEBOX", 0,0,w,h,0,0,0,0, Title or 'Эмулятор Menu'}
- DlgItems[#DlgItems+1] = {"DI_TEXT",1,1,w-1,0,0,0,0,{DIF_CENTERTEXT=1}, sFilter=='' and '' or '['..sFilter..']'}; FiltTextId=#DlgItems
- DlgItems[#DlgItems+1] = {"DI_LISTBOX", 0,2,w,h,lbItems,0,0,{DIF_LISTWRAPMODE=1;}, ''}; ListNo=#DlgItems
- --DlgItems[#DlgItems][9].DIF_LISTNOAMPERSAND=1
- local X1,Y1 = Properties.X or BAX_Round((Far.Width - w) / 2),Properties.Y or math.floor( (APanel.Height - h) / 2)
- local function _DlgProc(hDlg, Msg, ItemId, Param2)
- if Msg == F.DN_CTLCOLORDLGLIST then
- return MenuPalette
- elseif Msg == far.Flags.DN_CTLCOLORDLGITEM then
- if DlgItems[ItemId][1] == 'DI_DOUBLEBOX' or
- DlgItems[ItemId][1] == 'DI_TEXT' then
- return DialogPalette
- end
- elseif Msg == far.Flags.DN_CTLCOLORDIALOG then
- return far.AdvControl(F.ACTL_GETCOLOR,far.Colors.COL_MENUTEXT)
- elseif (Msg == F.DN_LISTHOTKEY) then
- mf.postmacro(Keys, "Enter")
- elseif Msg == F.DN_CONTROLINPUT and
- Param2.EventType == F.KEY_EVENT then
- local _key=far.InputRecordToName(Param2)
- if BreakKeys then
- local _BreakKeys, _BreakKey
- if type(BreakKeys) == 'string' then
- _BreakKeys = {{BreakKey=BreakKeys}}
- else
- _BreakKeys = BreakKeys
- end
- for _,v in ipairs(_BreakKeys) do
- -- Строка; возможно, регэксп
- _BreakKey = v.BreakKey
- if _BreakKey:match('^/.*/') then
- -- Регэксп
- if regex.match(_key, _BreakKey:match('^/(.*)/$')) then
- if type(v.Action) == 'function' then
- v.Action(hDlg, _key, hDlg:send("DM_LISTGETCURPOS", ListNo).SelectPos,
- lbItems[hDlg:send("DM_LISTGETCURPOS", ListNo).SelectPos].Source,
- DlgItems)
- else
- DlgItems.BreakKey = {BreakKey=_key}
- hDlg:send"DM_CLOSE"
- return
- end
- end
- --elseif _BreakKey:match('^f/.*/') then
- -- if _key:match(_BreakKey:match('^f/(.*)/$')) then
- -- DlgItems.BreakKey = {BreakKey=_key}
- -- hDlg:send"DM_CLOSE"
- -- return
- -- end
- else
- -- Список клавиш
- if (' '..BreakKeys..' '):find(_key, 1, true) then
- DlgItems.BreakKey = {BreakKey=_key}
- hDlg:send"DM_CLOSE"
- return
- end
- end -- регэксп/не регэксп
- end -- for _,v in ipairs(_BreakKeys)
- end -- if BreakKeys
- -- Сюда попадаем, если BreakKey не найден
- if regex.match(_key, '^([\\w_]|Space)$') and
- type(sFilter) == 'string' then
- if _key=='Space' then _key = ' ' end
- sFilter = (sFilter or '') .. (FiltCaseSens and _key or _key:upper())
- _ApplyFilter(hDlg, ListNo)
- elseif (_key == 'BS') then --and sFilter:len() > 1 then
- sFilter = sFilter:sub(1,sFilter:len()-1)
- _ApplyFilter(hDlg, ListNo)
- end
- end -- if Msg
- end -- _DlgProc
- local res=
- far.Dialog( '', -- 1 Guid
- X1, -- 2 X1
- Y1, -- 3 Y1
- X1+w, Y1+h, -- 4,5 - X2, Y2
- Properties.HelpTopic,
- DlgItems,
- "FDLG_SMALLDIALOG",
- _DlgProc)
- if res and res > 0 then
- if DlgItems.BreakKey then
- return DlgItems.BreakKey,lbItems.SelectIndex
- else
- return Items[lbItems.SelectIndex],lbItems.SelectIndex
- end
- end
- end
- ------------------------ ShowProgress ------------------------
- class_ProgressBar=class_def()
- function class_ProgressBar:__init()
- self.iTime = 0
- self.cntr = true
- self.CurrPos = 0
- self.cntb = 0
- self.Width = BAX_Round(Far.Width / 2)
- self.minValue = 0
- self.maxValue = 100
- --self.iTimeInterval =
- end
- function class_ProgressBar:Step(iPercent)
- if self.iTimeInterval == nil then
- self.iTimeInterval = (iPercent and 500 or 20)
- -- Чтоб не чаще чем 2 раза в секунду - если градусник и не чаще чем 50 раз в секунду - если бесконечный индикатор
- end
- if (not self.iTime) or (self.iTime == 0) then
- self.iTime = win.GetSystemTimeAsFileTime()
- elseif os.difftime(win.GetSystemTimeAsFileTime(), self.iTime) < self.iTimeInterval then
- return
- end
- local progressbar
- if iPercent == nil then
- -- "Туда-сюда индикатор"
- if self.cntr then
- -- Растёт правый конец
- self.CurrPos = self.CurrPos + 1
- progressbar = ('█'):rep(self.CurrPos) .. ('░'):rep(self.Width-self.CurrPos)
- if self.CurrPos == self.Width then
- -- Достигнут конец
- self.cntr = false -- в следующий раз убираю слева
- end
- else
- -- "убирается" левый конец
- self.cntb = self.cntb + 1
- progressbar = ('░'):rep(self.cntb) .. ('█'):rep(self.Width-self.cntb)
- if self.cntb == self.Width then
- self.cntb, self.CurrPos, self.cntr = 0,0,true
- end
- end
- else
- -- "Градусник"
- self.CurrPos = BAX_Round(iPercent / (self.maxValue-self.minValue) * self.Width )
- progressbar = ('█'):rep(self.CurrPos) .. ('░'):rep(self.Width-self.CurrPos)
- end
- self.iTime = win.GetSystemTimeAsFileTime()
- return progressbar
- end -- class_ProgressBar:Step
- class_ShowMessage = class_def()
- function class_ShowMessage:__init(Title, Strings, color, margins, delayed)
- self.Title = Title
- self.Strings = Strings or {}
- self.color = color or far.AdvControl("ACTL_GETCOLOR",far.Colors.COL_DIALOGTEXT)
- self.margins = margins or 2
- self.delayed = (type(delayed)=='boolean' and delayed or false)
- end
- local pad=mf.strpad
- function class_ShowMessage:Line(idx, bDelay)
- if self.LastPos then
- local w, y0, x = self.LastPos.Width, self.LastPos.Top, self.LastPos.Left
- if (idx and idx <= #self.Strings) and (w and w > 0 ) and (x and x > 0) then
- if self.Strings[idx]:len() <= w then
- far.Text(x+2+self.margins, y0 + (self.Title and 1 or 0) + idx , self.color, pad(self.Strings[idx], w-4, ' ', 2))
- if not bDelay then far.Text() end
- return
- end
- end
- end
- self:Show(nil, bDelay)
- end
- function class_ShowMessage:Show(Strings, bDelay)
- if Strings then
- self.Strings = Strings
- end
- if type(bDelay) ~= 'boolean' then bDelay = self.delayed end
- local h = #self.Strings + 2
- if self.Title then h = h + 1 end
- local w = self.Title and self.Title:len()+2 or 0
- for _,v in ipairs(self.Strings) do
- if v ~= '\1' and v ~= '\2' then
- w = math.max(w,v:len()+4)
- end
- end
- local x=BAX_Round( (Far.Width-w-2*self.margins)/2 )
- local y=BAX_Round( (APanel.Height-h)/2 ) - 1
- local y0=y
- self.LastPos = {Top = y0; Width = w; Left = x}
- w = w + 2*self.margins
- if self.Title then
- far.Text(x,y,self.color,pad(self.Title, w, ' ', 2)); y=y+1
- end
- far.Text(x,y,self.color,' ╔'..('═'):rep(w-4)..'╗ '); y=y+1
- for _,v in ipairs(self.Strings) do
- if v == '\1' then
- far.Text(x,y,self.color, ' ╟'..('─'):rep(w-4)..'╢ ')
- elseif v == '\2' then
- far.Text(x,y,self.color, ' ╠'..('═'):rep(w-4)..'╣ ')
- else
- far.Text(x,y,self.color, ' ║'..pad(v,w-4,' ',2)..'║ ')
- end
- y=y+1
- end
- far.Text(x,y,self.color,' ╚'..('═'):rep(w-4)..'╝ ')
- if not bDelay then far.Text() end
- end -- class_ShowMessage:Show
- --ShowMessage = class_ShowMessage()
- CopyFileEx_Lua = function (inPath, outPath, bForce)
- local lResult=false
- if not mf.fexist(inPath) then
- far.Message(inPath..'\n\1\nИсходный файл не найден','CopyFileEx_Lua',nil,'w')
- return lResult
- end
- if mf.fexist(outPath) then
- if not bForce and far.Message(outPath..'\n\1\nФайл сужествует.\nПереписать?','CopyFileEx_Lua',';YesNo') ~= 1 then
- return lResult
- --else
- -- win.DeleteFile(outPath)
- end
- end
- -- Далее вместо стандартного типа LARGE_INTEGER используется LONGLONG, т.к.
- -- при использовании LARGE_INTEGER (union) возникала ошибка в вызове ffi.cast
- ffi.cdef[[
- typedef DWORD (__stdcall *LPPROGRESS_ROUTINE)(
- LONGLONG TotalFileSize
- ,LONGLONG TotalBytesTransferred
- ,LONGLONG StreamSize
- ,LONGLONG StreamBytesTransferred
- ,DWORD dwStreamNumber
- ,DWORD dwCallbackReason
- ,HANDLE hSourceFile
- ,HANDLE hDestinationFile
- ,LPVOID lpData
- );
- BOOL CopyFileExW(
- LPCWSTR lpExistingFileName,
- LPCWSTR lpNewFileName,
- LPPROGRESS_ROUTINE lpProgressRoutine,
- LPVOID lpData,
- LPBOOL pbCancel,
- DWORD dwCopyFlags
- );
- ]]
- -------------------------------------------------------------------------------------------------
- local pbCancel = ffi.new("BOOL[1]"); pbCancel[0] = 0
- -------------------------------------------------------------------------------------------------
- local nTotFSize
- local tStart, tLast=0,0
- local Strings=
- {'', -- inPath -- 1
- '=>', -- 2
- '', -- outPath -- 3
- '\1', -- 4
- '', -- progressbar -- 5
- '', -- perents -- 6
- '', -- Прошло ... осталось скорость -- 7
- '[Esc] - прервать' -- 8
- };
- local ProgressBar = class_ProgressBar()
- local ShowMessage = class_ShowMessage("Копирование файла...",
- Strings, -- Strings
- nil, -- color
- 2, -- margins
- true) -- delayed
- local cbfc = ffi.cast("LPPROGRESS_ROUTINE", function(
- TotalFileSize
- ,TotalBytesTransferred
- ,StreamSize
- ,StreamBytesTransferred
- ,dwStreamNumber
- ,dwCallbackReason
- ,hSourceFile
- ,hDestinationFile
- ,lpData
- )
- local tNow = Far.UpTime
- if (tLast > 0) and ( (tNow-tLast) < 500 ) then return 0 end
- tLast = tNow
- local nBytesTransferred = tonumber(TotalBytesTransferred)
- local percents = BAX_Round( nBytesTransferred * 100 / nTotFSize )
- local PrBar=ProgressBar:Step(
- --percents
- nBytesTransferred
- )
- if PrBar then
- ShowMessage.Strings[5] = PrBar
- ShowMessage:Line(5, true)
- end
- if ShowMessage.LastPos then
- local s1,s3='Скопировано', SparseIntToStr(nBytesTransferred)..' из '..SparseIntToStr(nTotFSize)..' ('.. percents .. '%)'
- local s2 = (' '):rep(ShowMessage.LastPos.Width - (s1:len()+s3:len()) - 2*ShowMessage.margins)
- ShowMessage.Strings[6] = s1..s2..s3
- ShowMessage:Line(6, true)
- if tStart > 0 then
- local tElapsed = tNow - tStart -- прошло времени (милисекунды)
- s1 = 'Прошло '..os.date('!%H:%M:%S', tElapsed/1000)
- local vel=(nBytesTransferred) / (tElapsed/1000) -- скорость байт/с
- s3 = 'Осталось: '..(os.date('!%H:%M:%S', BAX_Round((nTotFSize - nBytesTransferred) / vel)) or '00:00:00')
- s2 = mf.strpad(BAX_Round( vel / 1024 ) .. ' КБайт/с', ShowMessage.LastPos.Width - (s1:len()+s3:len()) - 2*ShowMessage.margins, ' ',2)
- ShowMessage.Strings[7] = s1 .. s2 .. s3
- ShowMessage:Line(7, true)
- end;
- end
- far.Text()
- if mf.waitkey(1, 0) == 'Esc' then
- local ans = far.Message('Прервать копирование?','Копирование файла',';YesNo', 'w')
- if ans == 1 then
- lResult = false
- return 2 --PROGRESS_STOP - Остановить операцию копирования. Она может быть перезапущена в более позднее время.
- else
- -- Перерисовка ВСЕГО окна
- ShowMessage.LastPos = nil
- ShowMessage:Show(nil, false)
- end
- end
- return 0
- end)
- local inPathW, outPathW = ffi.cast( "LPCWSTR",win.Utf8ToUtf16([[\\?\]]..inPath..'\0') ), ffi.cast( "LPCWSTR",win.Utf8ToUtf16([[\\?\]]..outPath..'\0') )
- nTotFSize = win.GetFileInfo(inPath).FileSize
- ProgressBar.maxValue = nTotFSize
- lResult = true
- Strings[1] = inPath
- Strings[3] = outPath
- --ShowMessage.Title="Копирование файла..."
- --ShowMessage.Strings = Strings
- --ShowMessage.delayed = true
- ShowMessage.Width = nil
- ShowMessage.LastPos = nil
- ShowMessage:Show(Strings)
- tStart = Far.UpTime
- tLast = 0
- ffi.C.CopyFileExW(
- inPathW, -- LPCWSTR lpExistingFileName,
- outPathW, -- LPCWSTR lpNewFileName,
- cbfc, -- LPPROGRESS_ROUTINE lpProgressRoutine
- nil, -- LPVOID lpData,
- pbCancel, -- LPBOOL pbCancel,
- 0x00000002 -- DWORD dwCopyFlags=COPY_FILE_RESTARTABLE -- Написано, что так медленнее. А на самом деле - быстрее
- )
- far.AdvControl("ACTL_REDRAWALL")
- cbfc:free()
- return lResult
- end -- CopyFileEx_Lua
- return { BAX_Round=BAX_Round; -- Округлёж. Многие делают; просто чтоб далеко не ходить
- far_InputBox=far_InputBox; -- Оболочка для far.InputBox с указанием ключевых параметров
- SparseIntToStr=SparseIntToStr; -- Форматирование целого с разделением на группы цифр
- CopyFileEx_Lua=CopyFileEx_Lua; -- Процедуа копирования файла
- MergeSettings=MergeSettings; -- Процедуа слияния настроек (Old - умодчательные; New - прочитанные из файла
- far_Menu=far_Menu; -- far.Menu с предустановленным фильтром
- class_def=class_def; -- Функция-генератор нового класса
- class_ProgressBar=class_ProgressBar; -- Класс рисования прогресс-бара
- class_ShowMessage=class_ShowMessage; -- Класс вывода строк на экран через far.Text(...)
- }
GeSHi © Codebox Plus Extension
- local BaxUtils=require"BaxUtils"
- local CopyFileEx_Lua, far_InputBox=BaxUtils.CopyFileEx_Lua, BaxUtils.far_InputBox
- local inPath=APanel.Path..'\\'..APanel.Current
- local outPath=PPanel.Path..'\\'..APanel.Current
- if inPath:sub(1,1) == '\\' then inPath = inPath:sub(2) end
- if outPath:sub(1,1) == '\\' then outPath = outPath:sub(2) end
- local bCont=true
- while bCont do
- inPath = far_InputBox( { Title="Тест",Prompt="Введите имя исходного файла", HistoryName="Bax.CopyFileEx_Lua.InPath",
- SrcText=inPath, Flags={FIB_EDITPATH=1;FIB_EXPANDENV=1;FIB_BUTTONS=1;} } )
- if not inPath then
- bCont = false
- else
- outPath = far_InputBox( { Title="Тест", Prompt="Введите имя результирующего файла", HistoryName="Bax.CopyFileEx_Lua.OutPath",
- srcText=mf.fsplit(outPath, 3)..mf.fsplit(inPath, 12),
- Flags={FIB_EDITPATH=1;FIB_EXPANDENV=1;FIB_BUTTONS=1;} } )
- --{FIB_NOUSELASTHISTORY=1;FIB_EDITPATH=1;FIB_EXPANDENV=1;FIB_BUTTONS=1;} )
- if not outPath then
- bCont = false
- end
- end
- if bCont then
- bCont = CopyFileEx_Lua(inPath, outPath)
- end
- end
GeSHi © Codebox Plus Extension
По-прежнему остались мелкая"непонятка": у функции CopyFileEx можно указать флаг COPY_FILE_RESTARTABLE (Ход выполнения копирования отслеживается в целевом файле в случае сбоя копирования. Сбойную копию можно перезапустить позже, указав те же значения для lpExistingFileName и lpNewFileName, которые использовались в сбойном вызове). А CallBack-процедура может вернуть значение PROGRESS_STOP (Остановить операцию копирования. Она может быть перезапущена в более позднее время). Но почему-то несмотря на указание этих параметров при прерывании копирования скопированная часть файла стирается. Если кто-то знает, как сделать "дописываемый" файл при поможи процедуры CopyFileEx - поделитесь, пожалуйста, секретом
Кроме этого в скрипте имеется процедура индикатора процесса (как проценты, так и бесконечный). Тоже может кому пригодится...
- CopyFileEx_Lua.lua.7z
- Модуль BaxUtils.lua с примером использования CopyFileEx_Lua
- (7.66 KiB) Downloaded 103 times