Расширение Lister'а

Здесь мне придется отказаться от ставшей традиционной схемы представления скрипта: табличка-код-комментарии. Хотя бы потому, что скриптов здесь будет сразу три. Кроме того, для работы скриптов (по крайней мере, некоторых их функций) необходима настройка конфигурации PowerPro. Так что мне показалось целесообразным разбить изложение на несколько небольших главок. Часть из них будут описывать темы, хорошо знакомые некоторым из читателей. Поэтому придется попросить у них прощения.


1. Пролог: пакетный просмотр

Лично мне частенько приходится пользоваться функцией Lister'а, позволяющей просматривать произвольное количество файлов подряд. Для тех, кто не в курсе: выделив несколько файлов и нажав определенную комбинацию клавиш (по умолчанию Shift+F3), можно просмотреть их по очереди, перемещаясь вперед по списку клавишей "n" (next), а назад - "p" (previous).

Если количество файлов исчисляется единицами и даже десятками, проблем не возникает. Но вот при большом количестве файлов работа с ними требует определенных операций, для выполнения которых Total Commander оказывается бессильным.

Допустим, просматривая один из сотни выделенных файлов, мы приходим к выводу, что данный файл нам не нужен и требует удаления. Вот что для этого надо сделать:

Казалось бы - что сложного? Но здесь множество мелких (и не очень мелких) "если", которые способны превратить работу в пытку. Вот далеко не полный их список:

Вероятно, каждый, кому приходится пользоваться пакетным просмотром, продолжит этот грустный список.

Предлагаемые скрипты и изменения в конфигурации PowerPro если и не решают все описанные проблемы, то, по крайней мере, уменьшают их количество и упрощают решение остальных.


2. Конфигурация PowerPro: клавиши и класс окна

Линейка операционных систем носит название Windows не только потому, что в ней существуют такие окна, как Total Commander и Word. Понятие "окно" имеет гораздо более широкий смысл, дать которому исчерпывающую формулировку очень сложно. Окно имеет множество характеристик, из которых для нас в данном случае важен класс (тип, разновидность). Например, файловая панель Total Commander представляет собой окно класса TMyListBox, а встроенный просмотрщик Lister создает окно класса TLister. Именно этот класс нас и интересует.

Среди многочисленных возможностей PowerPro есть и такая, как назначение определенным клавишам определенных действий в определенных окнах. Я уже косвенно затрагивал этот вопрос при описании скрипта F2 - клавиша F2 вызывает локальное меню если, во-первых, класс активного окна TTOTAL_CMD (класс окна Total Commander), а во-вторых, файл _local.bar существует в текущей директории или ее родителе. Например, окно комментария к файлу имеет класс TMemo, и клавиша F2 в нем сработает как положено - сохранит комментарий, вне зависимости от наличия файла _local.bar.

Аналогичный прием я использовал при расширении возможностей Lister'а. Если задать класс TLister при описании окна, то клавиши будут переданы, только если активное окно принадлежит к этому классу, то есть, если клавиша нажата при просмотре файла через Lister.

Для изменения конфигурации PowerPro:

Возникнет окно, подобное показанному на скриншоте.

  1. В списке "Key/Mouse" выбрать insert;
  2. В строке "Blank..." написать c=TLister;
  3. В списке "Enter command..." выбрать *Script;
  4. В списке "Script action" выбрать RunFile;
  5. В строке "Enter file name..." написать
    D:\Utils\PowerPro\scripts\XLister=ins
    

    Здесь D:\Utils\PowerPro\ - путь, куда установлена программа PowerPro, XLister - имя скрипта XLister (см. ниже). Обратите внимание на отсутствие расширения!

    Что же касается строки ins, то это - значение, передаваемое скрипту. Наличие знака = между именем скрипта и значением обязательно!

Логика заполнения полей проста:

  1. При нажатии клавиши insert,
  2. если класс (c) активного окна равен (=) классу Lister'а (TLister), то
  3. выполнить "скрипт" (*Script),
  4. а именно, скрипт, записанный в файле (RunFile),
  5. расположенный в директории D:\Utils\PowerPro\scripts\ и носящий имя XLister, и передать ему значение (=), равное ins.

Аналогично следует переопределить другие клавиши согласно таблице:

 Поле "Key/Mouse"   Передаваемое значение 
delete del
insert ins
g Green
y Yellow
r Red
k copy
o open
t name
z comment

Остальные поля должны быть теми же, что и в рассмотренном примере. Поэтому удобнее будет воспользоваться кнопкой "Clone" и менять значение этих двух полей.

Позже я обнаружил, что если Lister используется не только для просмотра, но и для редактирования файлов (таковы возможности некоторых Lister-плагинов), использование букв создает неудобства. Имеет смысл отметить один или несколько чекбоксов (например, Ctrl - вверху на скриншоте) и вместо "g" нажимать "Ctrl+G" и т. д.

Теперь осталось только дважды нажать "ОК", тем самым сохранив конфигурацию.


3. Скрипт XLister

Название XLister
Имя файла (*.txt) XLister.txt
Описание Обрабатывает клавиши, нажатые при активном окне Lister'а
Автор Вахмурка
Параметры -
Вызываемые плагины win, childwin, file, clip
Иконка -
Версия 2.1
Дата обновления 16.06.2014

local Hand = win.handle("c=TLister")
local Text = childwin.gettext(Hand)
local From = index(Text, "[")
local To = index(Text, "]")
local Name = select(Text, From + 1, To - 1)
local i, State

if(not validpath(Name))
	quit

if(x9 == "del") do
	file.delete(Name)
elseif(x9 == "ins") do
	for(i = 0; i < Counter; i++)
		if(index(Name, v[i])) do
			State = win.sendmessage(TMLB, 0x0185, 0, i)
			if(State == -1) do
				messagebox("ok error", "Sending message failed", "XLister Script")
				quit
			else
				break
			endif
		endif
	endfor
elseif(x9 == "Green" || x9 == "Yellow" || x9 == "Red") do
	Text = scriptfolder ++ "\FileLists\" ++ x9 ++ ".tfl"
	State = file.open(Text, "a")
	if(State <= 0) do
		messagebox("ok error", "File not opened", "FILE plugin error")
		quit
	endif

	file.writeline(State, Name)
	file.close(State)
elseif(x9 == "copy") do
	clip.set(Name)
elseif(x9 == "name") do
	messagebox("ok", file.nametype(Name), "XLister script")
elseif(x9 == "open") do
	file.doverb(Name, "open")
elseif(x9 == "comment") do
	local Path = file.folder(Name) ++ "\"
	State = file.nametype(Name)
;Sub_Descript_ion - имя скрипта Sub_Descript_ion;
	From = runfile.Subs\Sub_Descript_ion(Path, State, "", "g")
	if(From == "ERROR: no comment file") do
		Text = input("Input comment for file " ++ Name)
	else
		Text = inputdefault(From, "Current comment: " ++ From)
		if(not Text)
			Text = From
					
	endif
	runfile.Subs\Sub_Descript_ion(Path, State, Text, "s")
endif

Комментарии:

Внимание! Скрипт не работает в Total Commander 7.5 pb1, pb2 и pb3!

Данный скрипт предназначен именно для "перехвата" клавиш, нажатых во время просмотра файла в Lister'е, и последующей обработки. Поэтому скрипт XLister не следует исполнять как обычный скрипт, присваивать ему кнопку или горячую клавишу! Скрипт выполняется автоматически при одновременном соблюдении двух условий: 1) текущее окно имеет класс TLister; 2) нажата одна из клавиш, указанных в конфигурации PowerPro. Непосредственное исполнение скрипта не только не имеет смысла, но может и привести к непредсказуемым последствиям!

Путь к скрипту должен соответствовать указанному в конфигурации PowerPro (в примере выше D:\Utils\PowerPro\scripts\).

Обратите внимание на комменарий в коде скрипта. Если программа PowerPro установлена в директорию d:\Utils\PowerPro (как у меня), то поддиректорию d:\Utils\PowerPro\scripts\FileLists\ нужно создать вручную.


3. Скрипт ABLV

Название Advanced Batch Lister View
Имя файла (*.txt) ABLV.txt
Описание Создает список выделенных файлов и запускает их пакетный просмотр
Автор Вахмурка
Параметр ("%L")
Вызываемые плагины win, childwin, file, tc, vec, ini
Иконка
Версия 3.0
Дата обновления 16.06.2014
args List
local hwnd = win.handle("c=TTOTAL_CMD")
local Panel = ifelse(tc.active(hwnd) == "left", 8, 7)
;;Если wincmd.ini находится не в директории Windows, укажите его полный путь: 
local Config = env("WINDIR") ++ "\wincmd.ini"
local Str, i, Hand, Tabs, j, Ind, Temp, Buttons
global TMLB = childwin.handle(hwnd, "TMyListBox", Panel)
global Counter = win.sendmessage(TMLB, 0x0190, 0, 0)
local Total = win.sendmessage(TMLB, 0x018B, 0, 0)

if(not Counter) do
	messagebox("ok error", "At least two files must be selected", "ABLV Script")
	quit
endif

if(Counter != Total) do
	Panel = ifelse(Panel == 8, 12, 19)
	Tabs = childwin.handle(hwnd, Panel)
	i = win.sendmessage(Tabs, 4868, 0, 0)
	win.postmessage(hwnd, 0x400+51, 501, 0)
	wait.for(1500, activewindow("c=TFindFile"))
	*keys {del}
	Hand = win.handle("c=TFindFile")
	Str = win.childhandlelist(Hand, "c=TButton")
	static v = vec.createfromwords(Str)

	if(v <= 0) do
		messagebox("ok error", "Failure creating vector", "VEC plugin Error #2")
		quit
	endif

	Buttons = v.length
	Str = .ABLV@TL(0)
	for(j = 1; j < Buttons; j++)
		if(win.parent(v[j]) != Hand)
			continue

		Temp = .ABLV@TL(j)
		if(Temp < Str) do
			Str = Temp
			Ind = j
		endif

	endfor
	Str = v[Ind]
	win.sendmessage(Str, 0x00F5, 0, 0)

	for(not win.gettext(win.childhandlelist(Hand, "c=TMyPanel")))
	endfor

	Str = .ABLV@BL(0)
	Ind = 0

	for(j = 1; j < Buttons; j++)
		Temp = .ABLV@BL(j)
		if(Temp > Str) do
			Str = Temp
			Ind = j
		endif

	endfor
	Str = v[Ind]
	win.sendmessage(Str, 0x00F5, 0, 0)
	wait.for(1000, i != win.sendmessage(Tabs, 4868, 0, 0))
	win.sendmessage(hwnd, 0x400+51, 523, 0)
endif


Panel = file.readall(List)
v = vec.createfromlines(Panel)

Str = ini.get(Config, "Configuration", "Viewertype")

if(Str == "1") do
	keys {F3}
else
	keys +{F3}
endif
quit
;;************************************** 
Function TL(i)
quit(float.divide(win.top(v[i]), win.right(v[i])))
;;************************************** 
Function BL(i)
quit(float.mul(win.top(v[i]), win.right(v[i])))

Комментарии:

Внимание! Скрипт не работает в Total Commander 7.5 pb1 и pb2!

Версия данного скрипта для Total Commander 7.0 и старше.

ABLV расшифровывается как "Advanced Batch Lister View" - "Продвинутый пакетный просмотр в Lister'е". Перед запуском скрипта необходимо выделить файлы, которые требуется просмотреть в пакетном режиме. Если ничего не выделено, будет выдано сообщение об ошибке.

Выделять файлы можно как в обычной файловой панели, так и в панели listbox, если после поиска нажата кнопка "Feed to listbox". Если выделено много файлов, придется подождать появления окна Lister'а. Если же файлов мало, это происходит практически мгновенно.

Если выделены директории, то все файлы в них будут включены в список.

Итак, какие же новые возможности пакетного просмотра предоставляет этот скрипт?

Удаление текущего файла. Если при просмотре файла мы приходим к выводу, что данный файл должен быть удален с диска, нужно просто нажать клавишу Del. Если вы нажали клавишу по ошибке, можно сохранить файл через меню Lister'а. Если попытаться вернуться к удаленному файлу (например, нажав "n" и "p", или нажав F2), то будет выдано совершенно логичное сообщение об ошибке, как если бы вы сначала перешли в файловую панель, стерли файл, а затем попытались перечитать файл с диска. Таким образом, разница состоит в том, что экономится время на переключение из окна в окно. Кроме того, вы можете не задумываться о том, где файл находится, и не нужно долго искать его ни в списке, ни в дебрях директорий.

Копирование имени. Нажав клавишу "k", помещаем полное имя текущего файла в буфер.

Открытие текущего файла. Обычно с каждым расширением ассоциируется определенное приложение, запускаемое и загружающее файл при двойном щелчке по нему. Изменить эти настройки можно, например, командой cm_Associate (File\Associate with...). Если при просмотре файла в Lister'е мы решаем, что файл надо отредактировать, необходимо просто нажать клавишу "о" - откроется ассоциированное приложение с текущим файлом.

Имя файла. Слушая множество mp3-файлов, я выделяю их и запускаю скрипт. Если путь к файлу имеет большую длину, он не вмещается в заголовок окна. Нажав "t", можно узнать имя текущего файла.

Комментарий к файлу. Теперь, чтобы отредактировать описание текущего файла в descript.ion, совсем не обязательно переключаться в файловые панели Total Commander! Жмем, как и там, Ctrl+Z (если, конечно, поставили птицу напротив Ctrl), и в окне вводим новый комментарий (или редактируем старый, если он есть).

Внимание! Все вышеописанных функции, в отличие от остальных, работают не только при пакетном, но и при обычном просмотре! Иными словами, просматривая единичный файл, вы с помощью клавиши Del также удаляете его с диска, а с помощью "k" - запоминаете имя.

Внимательный читатель может задать вопрос: "Какой файл будет удален, если открыты несколько окон Lister'а? Ведь все эти окна имеют один и тот же класс!". Простейший эксперимент показал: программа PowerPro достаточно "умна", чтобы удалять именно тот файл, который просматривается в момент нажатия клавиши Del.

Снятие выделения с текущего файла. Понятное дело, что перед тем как запустить пакетный просмотр, нужно сначала выделить файлы, поэтому просматриваемый файл всегда находится в состоянии "выделен". Для того, чтобы снять с него выделение, необходимо, находясь в окне Lister'а, нажать ту же клавишу, что и для изменения состояния выделения в файловой панели, а именно - клавишу Ins.

Преимущества функции очевидны. Если вы хотите, скажем, скопировать некоторые из просматриваемых файлов в новое место, то, не переключаясь в файловую панель, вы просто нажимаете Ins при просмотре необходимого файла. Сколь бы велик ни был список, как бы похоже ни было его имя на какое-то другое имя, где бы файл ни находился - будет снято выделение именно с того, который вы просматриваете в момент нажатия Ins. После того, как просмотр всего списка завершен, можно переключиться в файловую панель и просто нажать клавишу *. Тогда будут выделены те файлы, которые вы отметили при просмотре. Теперь с ними можно делать все что угодно - скопировать, стереть, заархивировать и пр.

Формирование списков групп. Вернемся к примеру, рассмотренному в первой главе. Среди всех просматриваемых файлов необходимо выделить пять групп, с которыми поступить следующим образом:

  1. Скопировать в директорию XXX;
  2. Скопировать в директорию YYY;
  3. Переместить в директорию ZZZ;
  4. Стереть;
  5. Заархивировать.

Напомню, что в "голом" Total Commander пришлось бы просматривать список 5 (!) раз, на каждом файле переключаясь в файловую панель и отмечая данный файл (о трудностях с поиском в списке нужного имени я уж молчу).

Как же сформировать группы с помощью скрипта ABLV? Прежде всего припишем первым трем группам три цвета - красный, желтый и зеленый (цвета светофора). Этим группам соответствуют клавиши "r" (red), "y" (yellow) и "g" (green). Четвертую группу как таковую формировать не будем, стирая файлы по ходу просмотра клавишей Del (см. выше). Наконец, последнюю (для архивирования) группу сформируем, снимая выделение с файлов клавишей Ins. Напомню: все это мы проделываем, не покидая окна Lister'а и перемещаясь от файла к файлу обычным образом - клавишами "n" и "p". Если компьютер вдруг зависнет, то сформированные "цветные" списки останутся нетронутыми - имя помещается в список сразу после нажатия соответствующей клавиши.

Файлы-списки групп имеют имена Green.tfl, Yellow.tfl и Red.tfl, расположены в поддиректории \scripts\FileLists\ директории PowerPro и содержат полные пути файлов, включенных в эти группы. Что делать с этими списками?

Прежде всего, можно выполнить команду cm_LoadSelectionFromFile, задав любой из файлов. Правда, эта команда позволяет выделять файлы только в одной панели. Если же файлы имеют разные пути, предлагается использовать скрипт TreatFileList.


4. Скрипт TreatFileList

Название TreatFileList
Имя файла (*.txt) TreatFileList.txt
Описание Копирует, перемещает или удаляет файлы из списков
Автор Вахмурка
Параметры ("%P", "%T")
Вызываемые плагины file
Иконка
Версия 1.0
Дата обновления 23.01.2006
args Source, Target
local Result, List, What, Dest, Clear
local Cap = "Source: " ++ Source ++ ", Target: " ++ Target ++ "'"

Result = Inputdialog("~List=List??Green|Yellow|Red, What=Operation with Files from List??Copy|Move|Delete, Dest=Destination Path (for 'Copy' && 'Move' operations)??Source folder|Target folder, Clear=Clear List File after Treatment Finished??", Cap)

if(Result == 0)
	quit

if(List == "") do
	messagebox("ok error", "List file is not specified", "TreatFileList Script")
	quit
endif

if(What == "") do
	messagebox("ok error", "Operation is not specified", "TreatFileList Script")
	quit
endif

if(What != "Delete" && Dest == "") do
	Cap = "Destination folder is not specified"
	messagebox("ok error", Cap, "TreatFileList Script")
	quit
endif

if(What != "Delete") do
	if(Dest == "Source folder" && file.validpath(Source) != "1") do
		Cap = "Source path '" ++ Source ++ "' is invalid"
		messagebox("ok error", Cap, "TreatFileList Script")
		quit
	endif
	if(Dest == "Target folder" && file.validpath(Target) != "1") do
		Cap = "Target path '" ++ Target ++ "' is invalid"
		messagebox("ok error", Cap, "TreatFileList Script")
		quit
	endif
endif

Target = ifelse(Dest == "Source folder", Source, Target)
Source = scriptfolder ++ "\FileLists\" ++ List ++ ".tfl"

if(file.validpath(Source) != "1") do
	Cap = "List File '" ++ Source ++ "' does not exist"
	messagebox("ok error", Cap, "TreatFileList Script")
	quit
endif

local fh = file.open(Source, "r")

if(fh > 0) do
	for(not(file.eof(fh))) 
		Result = file.readstring(fh)
		if(Result != 0) do
			if(file.validpath(Result) != 1) do
				Cap = "File " ++ Result ++ " does not exist"
				if(messagebox("okcancel warning", Cap, "TreatFileList Script") == 0)
					quit

			endif

			if(What == "Copy") do
				file.copy(Result, Target)
			elseif(What == "Move") do
				file.move(Result, Target)
			elseif(What == "Delete") do
				file.delete(Result)
			endif
		endif

	endfor
else
	messagebox("ok error", "File not opened", "FILE plugin error")
	quit
endif

file.close(fh)

if(Clear == "1")
	file.delete(Source)

Комментарии:

Если файлы из списка предполагается копировать или перемещать, перед запуском скрипта нужно открыть директорию-приемник в любой из панелей. Остальное очевидно из скриншота.

Пути обеих панелей приведены в заголовке окна. Если оба пути в заголовок не вмещаются, можно растянуть окно крысой.

При следующем выполнении скрипта ABLV списки будут не удалены, а дополнены новыми именами. Это выгодно, если необходимо сформировать списки файлов, отобранных по различным критериям. Если же сформированный список является окончательным, то разумно установить флаг "Clear List File after Treatment Finished".

Разумеется, полученные списки не обязательно использовать в скрипте TreatFileList. Может быть, нам просто необходимо иметь список файлов, удовлетворяющих определенным критериям, скажем, содержащих какой-то текст. В этом случае важно переименовать или скопировать файл, чтобы он не был затерт или замусорен лишней информацией.


5. Эпилог

Таким образом, в режиме пакетного просмотра произвольного количества файлов, имеющих произвольные пути, можно сформировать три различных списка. Кроме того, в режиме реального времени файлы можно включать в динамическую группу или удалять.

Почему групп именно три и почему им приписаны цвета светофора? Если бы я сформировал двенадцать (по названиям месяцев) или семьдесят девять (по субъектам Российской Федерации) групп, вопрос не потерял бы своей актуальности. А если серьезно, мне лишь однажды потребовалось создать две группы, а третью я добавил на всякий случай.

Предлагаемые скрипты и изменения конфигурации PowerPro никоим образом не конфликтуют с настройками по умолчанию. Если, допустим, вы запустили пакетный просмотр традиционным образом, а потом поняли, что функции скриптов могут оказаться полезными, можно запустить скрипт ABLV для того списка, который уже просматривается. Поэтому имеет смысл вообще отказаться от простого пакетного просмотра, и пользоваться только скриптами. Никаких конфликтов при этом не возникнет, а польза от скриптов может быть чрезвычайно серьезной.


На главную Все о Total Commander PowerPro PowerPro & Total Commander