Пример создания скрипта для PowerPro

Скрипт HighLightCurrentTab выделяет цветом текущую вкладку (закладку, таб, ушко) на файловой панели Total Commander. Цвет зависит от установленной "темы" Windows. По умолчанию (как у меня) вкладки имеют серый цвет, а их выделение - синий. Пусть у нас открыты три вкладки - корни дисков C:, D:, E:



После запуска скрипта с параметром "1" текущая (в нашем случае вторая) вкладка закрашивается синим цветом:



Для возвращения вкладки к первозданному виду следует запустить скрипт с параметром "0".

Далее с вкладкой можно производить обычные операции: запирание, дублирование (выделение не "наследуется"), переименование и прочее.

Излишне упоминать, что раскрасить можно произвольное число вкладок:



Зачем это нужно? Я приведу лишь три примера, когда это действие может оказаться полезным. Если я работаю одновременно над двумя-тремя разными проектами, у меня в каждой панели оказывается по 10-15 вкладок, а то и больше. Что имеется в виду под "проектом"? Ну, скажем, я готовлю доклад на конференции, да еще разбираю цифровые фотографии из последнего похода, да еще делаю халтуру по переводу, да еще переписываюсь с любимой девушкой, да еще... Мало найдется счастливцев, кто одновременно занимается всего одним делом. И если каждый "проект" требует хотя бы 6-7 вкладок, то общее их количество оказывается слишком большим. Разобраться, какая из вкладок для какого проекта нужна, получается все хуже и хуже, и сама работа отнимает меньше времени, чем поиск нужной директории.

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

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

Второй пример. Пусть открыты две вкладки - одна указывает на директорию d:\Graphics\Viewers\Irfan\plugins\, вторая - на d:\Utils\PowerPro\plugins\. Называются обе, естественно, одинаково - plugins, а это неудобно. Можно, конечно, дать им собственные имена, щелкнув правой кнопкой, выбрав "Rename/Lock tab" и введя имя. Если одноименных вкладок три или пять, придется так и поступить (или, опять же, обратиться к TabTools - он сделает все сам). Если же одноименных вкладок всего две, куда быстрее одним щелчком закрасить одну из них и знать, что синяя - это которая Irfan.

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

Итак, приступаем к разбору скрипта, для начала приведя его код:

local hwnd = win.handle("c=TTOTAL_CMD")
local Panel = ifelse(tc.active(hwnd) == "left", 2, 3)
local TMP = childwin.handle(hwnd, "TMyPanel", Panel)
local TMTC = childwin.handle(TMP, "TMyTabControl")
local CurTab = win.sendmessage(TMTC, 4875, 0, 0)
win.sendmessage(TMTC, 4915, CurTab, arg(1))


Первой строкой я присваиваю переменнной hwnd дескриптор окна Total Commander. Следует заметить, что так начинаются почти все скрипты, нацеленные на работу с конкретным окном. Для этого мне нужно, во-первых, знать, что класс окна Total Commander имеет вид TTOTAL_CMD. Как это узнать? Способов много. Есть, например, утилита Sign Of Misery
, есть другие подобные программы, но я пользуюсь скриптом ShowWinInfo собственного сочинения. Запускаем скрипт, за отведенные 5 секунд наводим курсор на строку заголовка и узнаем много информации, которая нам пригодится в дальнейшем. Эту информацию я далее буду называть "информация об окне". В данном случае нас интересует именно класс окна. Он, разумеется, всегда один и тот же, а вот дескриптор (на программерском жаргоне хэндл) окна при каждом запуске программы - разный. Обращение к окну по дескриптору предпочтительнее (а чаще всего это единственная возможность), поскольку окон с одним и тем же классом и/или заголовком в системе может существовать сколько угодно, а дескриптор - уникальная характеристика каждого окна.

Чтобы узнать дескриптор по классу, понадобится плагин win программы PowerPro. Искать и скачивать этот плагин нужды нет - он включен в стандартную поставку PowerPro и находится в поддиректории plugins.

У плагина win множество полезных функций, но нас интересует функция handle, возвращающая handle - дескриптор окна. Можно было задать в качестве параметра не класс, а заголовок окна, но класс надежней.

Спецификатор доступа local переменной hwnd означает, что переменная будет объявлена локальной, то есть она будет доступна только в течение выполнения скрипта. После его завершения значение переменной потеряется, а память, которую она занимала - освободится. Нам нет никакой нужды "помнить" дескриптор окна Total Commander. Можно было бы, конечно, объявить эту переменную как static или global, тогда мы сэкономили бы несколько миллисекунд при следующем обращении к скрипту, но потеряли бы несколько байт памяти. Специфика скрипта состоит в том, что нет никакой гарантии, что он будет вызван многократно, так что экономия времени лишь гипотетическая, зато затраты памяти - фактические. Конечно, потеря нескольких байт при современных объемах в гигабайты мизерна, но здесь важен принцип. Один скрипт "откусывает" несколько лишних байт, другой... Как ни велика память, но все же не безгранична.

Но это только полбеды. Представим себе, что между первым и вторым исполнением скрипта мы по каким-либо причинам перезапустили Total Commander. Тогда его дескриптор после второго запуска будет совсем другим, но статическая или глобальная переменная hwnd будет продолжать хранить прежнее значение, которое потеряет смысл. В худшем случае это может привести к очень неприятным последствиям (если осободившийся дескриптор окажется занятым каким-нибудь новым окном), в лучшем - скрипт просто не произведет желаемого действия. Таким образом, разумно все переменные объявлять локальными, если нет серьезных аргументов против. Из кода скрипта нетрудно видеть, что все остальные переменные также локальны, хотя и по иным соображениям. Поэтому к вопросу о спецификаторах доступа я больше возвращаться не буду.

Теперь требуется определить, какая из панелей - левая или правая - активна в текущий момент, иначе мы не будем знать, какую вкладку закрашивать. В файле конфигурации можно найти указание на активные вкладки обеих панелей, но в какой из них находится курсор, неизвестно. На помощь нам придет плагин tc, который написал Gregory специально для работы с Total Commander. В стандартную поставку плагин tc, разумеется, не включен, поэтому будем качать его отсюда
.

Если передать функции active плагина tc дескриптор окна Total Commander, то функция вернет "left", если активна левая панель и "right" - в противном случае. Однако нам не нужно знать, какая именно панель активна, поскольку скрипт должен одинаково работать в любой панели. То, что нам нужно знать - дескриптор этой панели. Его мы узнаем чуть позже, а пока "переведем" слова "left" и "right" на язык цифр.

Во второй строке скрипта мы создаем переменную Panel, которая и принимает определенное числовое значение. Если высказывание tc.active(hwnd) == "left" истинно (активна левая панель), то переменной Panel согласно синтаксису оператора ifelse присваивается его второй операнд, если ложно (правая панель) - третий.

Теперь настало время найти дескриптор самой файловой панели (ведь это тоже окно). Для этого надо знать: дескриптор родительского окна, класс дочернего окна, индекс (если их несколько). Дескриптор окна Total Commander получен в первой строке и хранится в переменной hwnd. Класс дочернего окна можно выяснить указанным способом - из "информации об окне" - он равен TMyPanel. Кроме того, из того же источника становится ясно, что индексы левой и правой панелей равны 2 и 3 соответственно. Значит, переменная Panel хранит индекс дочернего окна.

Итак, у нас есть все, кроме плагина childwin
, созданного для работы с дочерними окнами, ведь окно TMyPanel - дочернее по отношению к Total Commander. Поэтому в результате отработки третьей строки в переменную TMP будет записан дескриптор активной файловой панели.

Четвертая строка по сути аналогична третьей - мы продолжаем движение вглубь иерархии окон. Теперь переменная TMTC хранит дескриптор вкладок (Tab Control). Настало время узнать, какая из вкладок - текущая. Трюк с дескрипторами не пройдет - мы спустились в самый низ иерархии. Каждая отдельная вкладка собственного дескриптора не имеет - только набор в целом. Так как же выяснить номер текущей вкладки?

Можно, конечно, запросить данные из файла конфигурации, но для начала его надо обновить командой cm_ConfigSaveSettings, а это занимает достаточно заметное время (порядка секунды). Намного проще и существенно быстрее послать сообщение вкладкам, а для этого нам понадобится MSDN - Microsoft Development Network. Программеры хорошо знают, что это, а остальным я сообщу, что MSDN - сборник сведений, чрезвычайно полезных, а зачастую просто необходимых при написании любых программ, даже таких простых, как скрипты. Успокою противников Microsoft вообще и Билла Гейтса в частности - искомую информацию можно почерпнуть и из иных источников.

Для начала нам понадобится узнать побольше о сообщении TCM_GETCURSEL, где TCM расшифровывается как Tab Control Message. Вот что сказано об этом в MSDN:

TCM_GETCURSEL Message

Syntax

lResult = SendMessage(      // returns LRESULT in lResult
   (HWND) hWndControl,
      // handle to destination control
   (UINT) TCM_GETCURSEL,   // message ID
   (WPARAM) wParam,      // = 0; not used, must be zero
   (LPARAM) lParam      // = 0; not used, must be zero );

Parameters

wParam   Must be zero.
lParam    Must be zero.

Return Value

Returns the index of the selected tab if successful, or -1 if no tab is selected.


Ну что же, давайте разбираться. Прежде всего, нам надо послать сообщение (SendMessage). Эта функция входит в уже упомянутый плагин win, так что с отправкой сообщений все в порядке. Далее, параметры функции: дескриптор hWndControl нами получен в четвертой строке и обозначен TMTC. Найти идентификатор сообщения будет чуть сложнее - для этого понадобится файл commctrl.h, входящий в состав MSDN. Открываем этот файл и ищем строку TCM_GETCURSEL:

#define TCM_GETCURSEL (TCM_FIRST + 11)

Это - так называемая метакоманда, которая дает указание компилятору заменять в программе строку TCM_GETCURSEL на строку TCM_FIRST + 11. Но пока что от этого не легче: чему равно TCM_FIRST? Ищем в том же commctrl.h:

#define TCM_FIRST 0x1300   // Tab control messages

Вот это другое дело! Значит, второй аргумент команды SendMessage будет равен 0x1300 + 11 = 4875. Третий и четвертый параметры, как указано выше, равны нулю. Вот мы и сформировали пятую строку скрипта. Результатом ее отработки будет присвоение переменной CurTab номера текущей вкладки. Строго говоря, следовало бы проверить, что переменная CurTab не равна -1 - это значит, что произошла какая-то ошибка. Да, "правила хорошего тона" программирования этого требуют, но мы в интересах простоты и краткости их нарушаем.

Дело за малым - послать текущей вкладке сообщение о выделении (закраске), то есть сообщение TCM_HIGHLIGHTITEM. Вновь обращаемся к MSDN:

TCM_HIGHLIGHTITEM Message

Syntax

lResult = SendMessage(      // returns LRESULT in lResult
(HWND) hWndControl,      // handle to destination control
(UINT) TCM_HIGHLIGHTITEM,   // message ID
(WPARAM) wParam,      // = (WPARAM) (INT) idItem
(LPARAM) lParam      // = (LPARAM) MAKELONG (fHighlight, 0)
);

Parameters

idItem       - Zero-based index of a tab control item.
fHighlight    - Value specifying the highlight state to be set.
      If this value is TRUE, the tab is highlighted; if FALSE,
      the tab is set to its default state.


Первые два аргумента разбираем так же, как раньше. Третий параметр - индекс вкладки - хранится в переменной CurTab. И, наконец, четвертый параметр может иметь значение "1" (TRUE) - установить выделение, и "0" (FALSE) - снять выделение. Поскольку нам может понадобиться и то, и другое, имеет смысл сделать эту цифру параметром скрипта, причем единственным. Как известно, в скрипт PowerPro можно передать до 9 параметров, обозначаемых arg(1), arg(2), и т. д. Так что разумно создать на панели Total Commander две кнопки, вызывающих скрипт HighlightCurrentTab, но одна - с параметром "1", другая - "0". Тогда мы сможем, переключая вкладки и нажимая то одну, то другую кнопку, раскрашивать вкладки по своему усмотрению.

Итак, простенький скрипт, состоящий из шести строк, уложился в очень простой линейный алгоритм также из шести пунктов-задач:

1.Определить дескриптор окна Total Commander;
2.Определить индекс текущей панели в окне Total Commander;
3.Определить дескриптор текущей панели в окне Total Commander;
4.Определить дескриптор вкладок на текущей панели Total Commander;
5.Определить номер текущей вкладки на текущей панели Total Commander;
6.Закрасить текущую вкладку на текущей панели Total Commander.

Те, кто делает свои первые шаги на тернистом пути скриптинга, могут, взяв алгоритм за основу, поизгаляться над вкладками, внеся в скрипт новые возможности: закрасить все вкладки; закрасить текущую вкладку на противоположной панели; закрасить через одну (две, три); сменить статус на противоположный; и так далее...

Корректно ли вмешиваться в работу программы на более низком, операционном, уровне? Строго говоря, конечно же, нет. В частности, эксперименты с сообщением TCM_DELETEITEM закончились полным фиаско, равно как и с некоторыми другими сообщениями семейства TCM. Утешает то, что раскраска вкладок относится к категории "косметических" и на функциональность программы повлиять не должна. Сомневающихся спешу успокоить - никаких сбоев и побочных эффектов рассматриваемый скрипт не давал, так что пользоваться им можно вполне смело. Конечно, ситуация может измениться в следующей версии Total Commander, но вероятность этого невелика.

На прощание остается лишь выразить удивление и сожаление тем фактом, что Гислер не включил эту простую, полезную и изящную функцию в Total Commander. И порадоваться, что PowerPro помог нам восполнить этот пробел.

ВАХМУРКА
Оригинал статьи: http://pprotcmd.narod.ru/Articles.html