четверг, 13 августа 2009 г.

Как управлять IContextMenu, часть 9 - добавление своих команд

Это перевод How to host an IContextMenu, part 9 - Adding custom commands. Автор: Реймонд Чен.

Чтобы изменить меню, вам не всегда нужно реализовывать своё расширение меню.

Параметры indexMenu, idCmdFirst и idCmdLast в методе IContextMenu.QueryContextMenu позволяют вам, серверу, контролировать где в контекстном меню IContextMenu будет вставлять свои команды. Чтобы продемонстрировать это, давайте добавим две свои собственные команды в наше контекстное меню, со скучными именами "Top" и "Bottom".

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

const 
SCRATCH_QCM_FIRST = 1;
SCRATCH_QCM_LAST = $6FFF;
IDM_TOP = $7000;
IDM_BOTTOM = $7001;

Мы зарезервировали $1000 команд для себя, позволяя IContextMenu играть только с командами от 1 до $6FFF (мы также могли откусить кусок от начала, увеличивая SCRATCH_QCM_FIRST вместо уменьшения SCRATCH_QCM_LAST).
Вернитесь назад к программе из части 6 и сделайте такие изменения:

procedure TForm1.FormContextPopup(Sender: TObject; MousePos: TPoint;
var Handled: Boolean);
var
pcm: IContextMenu;
Menu: HMENU;
Info: TCMInvokeCommandInfoEx;
Pt: TPoint;
iCmd: Integer;
begin
Handled := True;

Pt := MousePos;
if (Pt.X = -1) and (Pt.Y = -1) then
begin
Pt.X := 0;
Pt.Y := 0;
end;
Windows.ClientToScreen(Handle, Pt);

if SUCCEEDED(GetUIObjectOfFile(Handle, 'C:\Windows\clock.avi', IID_IContextMenu, pcm)) then
try
Menu := CreatePopupMenu;
if Menu <> 0 then
try
if InsertMenu(Menu, 0, MF_BYPOSITION, IDM_TOP, 'Top') and
InsertMenu(Menu, 1, MF_BYPOSITION, IDM_BOTTOM, 'Bottom') and
SUCCEEDED(pcm.QueryContextMenu(Menu, 1, SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST, CMF_NORMAL)) then
begin
g_pcm := Pcm;
Pcm.QueryInterface(IID_IContextMenu2, g_pcm2);
Pcm.QueryInterface(IID_IContextMenu3, g_pcm3);
try
iCmd := Integer(TrackPopupMenuEx(Menu, TPM_RETURNCMD, Pt.X, Pt.y, Handle, nil));
finally
g_pcm3 := nil;
g_pcm2 := nil;
g_pcm := nil;
end;
if iCmd = IDM_TOP then
MessageBox(Handle, 'Top', 'Custom', MB_OK)
else
if iCmd = IDM_BOTTOM then
MessageBox(Handle, 'Bottom', 'Custom', MB_OK)
else
if iCmd > 0 then
begin
FillChar(Info, SizeOf(Info), 0);
Info.cbSize := SizeOf(info);
Info.fMask := CMIC_MASK_UNICODE or CMIC_MASK_PTINVOKE;
if GetKeyState(VK_CONTROL) < 0 then
Info.fMask := Info.fMask or CMIC_MASK_CONTROL_DOWN;
if GetKeyState(VK_SHIFT) < 0 then
Info.fMask := Info.fMask or CMIC_MASK_SHIFT_DOWN;
Info.hwnd := Handle;
Info.lpVerb := MAKEINTRESOURCEA(iCmd - SCRATCH_QCM_FIRST);
Info.lpVerbW := MAKEINTRESOURCEW(iCmd - SCRATCH_QCM_FIRST);
Info.nShow := SW_SHOWNORMAL;
Info.ptInvoke := Pt;
SetLastError(pcm.InvokeCommand(PCMInvokeCommandInfo(@Info)^));
if GetLastError <> 0 then
RaiseLastOSError;
end;

end;
finally
DestroyMenu(menu);
end;
finally
pcm := nil;
end;
end;

Прежде, чем вызывать IContextMenu.QueryContextMenu, мы добавляем наши собственные команды в меню (поскольку мы используем идентификаторы меню не из диапазона, что мы передаём в IContextMenu.QueryContextMenu, то у нас не возникают конфликты), и потом вызываем IContextMenu.QueryContextMenu, передавая новый уменьшенный диапазон, также указывая позицию для вставки равную 1 вместо 0.

Когда мы передаём меню в IContextMenu.QueryContextMenu, оно выглядит так:

Top
Bottom

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

Top

... новые пункты меню ...
 
Bottom

После показа этого расширенного контекстного меню, мы проверяем, какую команду выбрал пользователь: либо одну из наших (мы обрабатываем её сразу) или же одну из вставленных команд обработчика меню (мы передаём вызов самому обработчику).

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

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

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