9.8 - Создание доступных для редактирования пользователем клавиш - ускорителей

Этот пример показывает, как создать диалоговое окно, которое позволяет пользователю изменять клавишу - ускоритель, связанную с пунктом меню. Диалоговое окно состоит из комбинированного блока содержащего пункты меню, комбинированного блока, содержащего названия клавиш и окошек для флажка "галочка" для выбора клавиш CTRL, ALT и SHIFT. Иллюстрация ниже показывает это диалоговое окно.

19_1.gif (14805 b)

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

EdAccelBox DIALOG 5, 17, 193, 114
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
CAPTION "Edit Accelerators"
BEGIN
	COMBOBOX		IDD_MENUITEMS, 10, 22, 52, 53,
					CBS_SIMPLE | CBS_SORT | WS_VSCROLL |
					WS_TABSTOP
	CONTROL		 "Control", IDD_CNTRL, "Button",
						BS_AUTOCHECKBOX | WS_TABSTOP,
						76, 35, 40, 10
	CONTROL		 "Alt", IDD_ALT, "Button",
						BS_AUTOCHECKBOX | WS_TABSTOP,
						76, 48, 40, 10
	CONTROL		 "Shift", IDD_SHIFT, "Button",
						BS_AUTOCHECKBOX | WS_TABSTOP,
						76, 61, 40, 10
	COMBOBOX		IDD_KEYSTROKES, 124, 22, 58, 58,
					CBS_SIMPLE | CBS_SORT | WS_VSCROLL |
					WS_TABSTOP
	PUSHBUTTON	  "Ok", IDOK, 43, 92, 40, 14
	PUSHBUTTON	  "Cancel", IDCANCEL, 103, 92, 40, 14
	LTEXT		   "Select Item:", 101, 10, 12, 43, 8
	LTEXT		   "Select Keystroke:", 102, 123, 12,
					 60, 8
END

Диалоговое окно использует массив определяемых программой структур VKEY, каждая из которых содержит текстовую строку о нажатии клавиши и текстовую строку о клавише - ускорителе. Когда диалоговое окно создается, оно анализирует массив и добавляет текстовую строку о каждом нажатии клавиши в комбинированный блок Выбор Нажатия клавиши (Select Keystroke). Когда пользователь щелкает по кнопке Ok, блок диалога ищет текстовую строку о выбранном нажатии клавиши и извлекает данные соответствующие текстовой строке о клавише - ускорителе. Диалоговое окно приобщает текстовую строку о клавише - ускорителе к тексту пункта меню, который выбрал пользователь. Следующий пример показывает массив структур VKEY:

/* Настройка поиска VKey */

#define MAXKEYS 26

typedef struct _VKEYS {
	char *pKeyName;
	char *pKeyString;
} VKEYS;

VKEYS vkeys[MAXKEYS] = {
	"BkSp", "Back Space",
	"PgUp", "Page Up",
	"PgDn", "Page Down",
	"End", "End",
	"Home", "Home",
	"Lft", "Left",
	"Up", "Up",
	"Rgt", "Right",
	"Dn", "Down",
	"Ins", "Insert",
	"Del", "Delete",
	"Mult", "Multiply",
	"Add", "Add",
	"Sub", "Subtract",
	"DecPt", "Decimal Point",
	"Div", "Divide",
	"F2", "F2",
	"F3", "F3",
	"F5", "F5",
	"F6", "F6",
	"F7", "F7",
	"F8", "F8",
	"F9", "F9",
	"F11", "F11",
	"F12", "F12"
};

Процедура инициализации диалогового окна заполняет комбинированные блоки Элемент Выбора (Select Item) и Выбор Нажатия клавиши (Select Keystroke). После того, как пользователь выберет пункт меню и связанную клавишу - ускоритель, блок диалога проверяет элементы управления в диалоговом окне, чтобы получить пользовательский выбор, модифицирует текст пункта меню, а затем создает новую таблицу клавиш-ускорителей , которая содержит определенную пользователем новую клавишу - ускоритель. Следующий пример показывает эту процедуру диалогового окна.

/* Глобальные переменные */

HWND hwndMain;	  /* дескриптор главного окна */
HANDLE hinstAcc;	/* дескриптор экземпляра приложения */
HACCEL haccel;	  /* дескриптор таблицы клавиши-ускорителя */

.
.
.

/* Процедура диалогового окна */

LRESULT CALLBACK EdAccelProc(hwndDlg, uMsg, wParam, lParam)
HWND hwndDlg;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
	int nCurSel;		/* Индекс элемента окна списка */
	UINT idItem;		/* Идентификатор пункта меню */
	UINT uItemPos;		/* Позиция пункта меню */
	UINT i, j = 0;		/* Счетчики цикла */
	static UINT cItems;	/* Номер элемента в меню */
	char szTemp[32];	i>/* Временный буфер */
	char szAccelText[32];	/* Буфер для текста клавиши-ускорителя */
	char szKeyStroke[16];	/* Буфер для текста нажатой клавиши */
	static char szItem[32];	/* Буфер для текста пункта меню */
	HWND hwndCtl;		/* Дескриптор окна управления */
	static HMENU hmenu;	/* Дескриптор меню "Character" */
	PCHAR pch, pch2;		/* Указатели на копируемую строку */
	WORD wVKCode;		/* Код виртуальной клавиши - ускорителя */
	BYTE fAccelFlags;		/* Флажки fVirt для структуры ACCEL */
	LPACCEL lpaccelNew;	/* Адрес новой таблицы клавишей ускорителей */
	HACCEL haccelOld;		/* Адрес старой таблицы клавишей ускорителей */
	int cAccelerators;		/* Число клавиш-ускорителей в таблице */
	static BOOL fItemSelected = FALSE;	/* Флажок выбора элемента */
	static BOOL fKeySelected = FALSE;	/* Флажок выбора клавиши */

	switch (uMsg) {
		case WM_INITDIALOG:

		/* Получим дескриптор пункта меню комбинированного блока */
			hwndCtl = GetDlgItem(hwndDlg, IDD_MENUITEMS);

		/*
		* Строка меню прикладной программы содержит подменю "Character",
		* пункты которого присоединяют клавиши - ускорители к ним.
		* Получим дескриптор подменю "Character" "
		* (его позиция в пределах главного меню и 2),
		* и подсчет числа пунктов, которые в нем есть.
		*/

			hmenu = GetSubMenu(GetMenu(hwndMain), 2);
			cItems = GetMenuItemCount(hmenu);

		/* Получим текст каждого пункта,
		* полоску из '&' и текста  клавиши - ускорителя
		* и добавим текст в пункт меню комбинированный блока
		*

			for (i = 0; i < cItems; i++) {
				if (!(GetMenuString(hmenu, i, szTemp,
					sizeof(szTemp), MF_BYPOSITION)))
					continue;
				for (pch = szTemp, pch2 = szItem;
					*pch != '\0'; )
				{
					if (*pch != '&')
					{
						if (*pch == '\t')
						{
							*pch = '\0';
							*pch2 = '\0';
						} else *pch2++ = *pch++;
					} else pch++;
				}
				SendMessage(hwndCtl, CB_ADDSTRING, 0,
					(LONG) (LPSTR) szItem);
			}

			/* Теперь заполним комбинированный блок нажатия клавиши
			* списком нажатий клавиши, которые будут позволяться для клавиш - ускорителей.
			* Список нажатий клавиши находится в определяемой программой
			* структуре, называемой "vkeys".
			*/

			hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES);
			for (i = 0; i < MAXKEYS; i++)
				SendMessage(hwndCtl, CB_ADDSTRING, 0,
					(LONG) (LPSTR) vkeys[i].pKeyString);

			return TRUE;

		case WM_COMMAND:
			switch (LOWORD(wParam))
			{
				case IDD_MENUITEMS:

					/* Пользователь должен выбрать элемент 
					* из пункта меню - комбинированного блока.
					* Этот флажок проверяется в ходе обработки IDOK,
					* чтобы убедиться, что выбор был сделан.
					*/

					fItemSelected = TRUE;
					return 0;

				case IDD_KEYSTROKES:

					/*
					 * Пользователь должен выбрать элемент из пунктов меню
					 * комбинированного блока. Этот флажок проверяется в ходе
					 * обработки IDOK, чтобы гарантировать, что выбор был сделан.
					 */


					fKeySelected = TRUE;

					return 0;

				case IDOK:

					/*
					 * Если пользователь не выбрал пункт меню
					 * и нажал клавишу, на экране появится напоминание
					 * в окне сообщений.
					 */

			   if (!fItemSelected || !fKeySelected)
			   {
					MessageBox(hwndDlg,
					   "Item or key not selected.", NULL,
						MB_OK);
						return 0;
			   }

					/*
					 * Определим, выбраны ли клавиши CTRL, ALT и SHIFT.
					 * Свяжем соответствующие строки клавиши - ускорителя
					 * с буферной памятью для хранения текста и установим
					 * соответствующие флажки клавиши - ускорителя.
					 */

					szAccelText[0] = '\0';

					hwndCtl = GetDlgItem(hwndDlg, IDD_CNTRL);
					if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == 1)
					{
						lstrcat(szAccelText, "Ctl+");
						fAccelFlags |= FCONTROL;
					}
					hwndCtl = GetDlgItem(hwndDlg, IDD_ALT);
					if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == 1) {
						lstrcat(szAccelText, "Alt+");

						fAccelFlags |= FALT;
					}
					hwndCtl = GetDlgItem(hwndDlg, IDD_SHIFT);
					if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == 1) {
						lstrcat(szAccelText, "Shft+");
						fAccelFlags |= FSHIFT;
					}

					/*
					 * Получим выбранное нажатие клавиши и найдем
					 * текст клавиши-ускорителя, и код виртуальной клавиши
					 * для нажатия клавиши в структуре vkeys.
					 */

					hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES);
					nCurSel = (int) SendMessage(hwndCtl,
						CB_GETCURSEL, 0, 0);
					SendMessage(hwndCtl, CB_GETLBTEXT,
						nCurSel, (LONG) (LPSTR) szKeyStroke);
					for (i = 0; i < MAXKEYS; i++) {
						if(lstrcmp(vkeys[i].pKeyString,
								szKeyStroke) == 0)
						{
							lstrcpy(szKeyStroke,
								vkeys[i].pKeyName);
							break;
						}
					}

					/*
					 * Свяжем текст нажатия клавиши со строкой " Ctl + ", " Alt + " или " Shft + ".
					 *
					 */

					lstrcat(szAccelText, szKeyStroke);


					/*
					 * Установим позицию в меню
					 * выбранного пункта меню. Пункты меню в
					 * меню "Character" имеют позиции 0,2,3 и 4.
					 */

					if (lstrcmp(szItem, "Regular") == 0)
						uItemPos = 0;
					else if (lstrcmp(szItem, "Bold") == 0)
						uItemPos = 2;
					else if (lstrcmp(szItem, "Italic") == 0)
						uItemPos = 3;
					else if (lstrcmp(szItem, "Underline") == 0)
						uItemPos = 4;

					/*
					 * Получим строку символов, которая соответствует выбранному элементу.
					 */

					GetMenuString(hmenu, uItemPos, szItem,
						sizeof(szItem), MF_BYPOSITION);

					/*
					 * Приобщим новый текст клавиши - ускорителя к
					 * тексту пункта меню.
					 */

					for (pch = szItem; *pch != '\t'; pch++);
					++pch;

					for (pch2 = szAccelText; *pch2 != '\0';
							pch2++)
						*pch++ = *pch2;
					*pch = '\0';

					/*
					 * Изменим пункт меню, чтобы отразить новый
					 * текст клавиши-ускорителя.
					 */

					idItem = GetMenuItemID(hmenu, uItemPos);
					ModifyMenu(hmenu, idItem, MF_BYCOMMAND |
						MF_STRING, idItem, szItem);

					/* Сбросим выбранные флажки. */

					fItemSelected = FALSE;
					fKeySelected = FALSE;

					/* Сохраняем текущую таблицу клавиш ускорителей. */

					haccelOld = haccel;

					/*
					 * Сосчитаем число входов в текущей
					 * таблице, распределим буфер для таблицы, а,
					 * затем скопируем таблицу в буфер.
					 */

					cAccelerators = CopyAcceleratorTable(haccelOld, NULL, 0);
					lpaccelNew = (LPACCEL) LocalAlloc(LPTR, cAccelerators * sizeof(ACCEL));

					if (lpaccelNew != NULL) CopyAcceleratorTable(haccel, lpaccelNew,cAccelerators);

					/*
					 * Найдем клавишу - ускоритель, которую пользователь модифицировал
					 * и изменим ее флажки и код виртуальной клавиши
					 * в соответствии с этим.
					 */

					for (i = 0; (lpaccelNew[i].cmd ==
								(WORD) idItem)
							&& (i < (UINT) cAccelerators); i++) {

						lpaccelNew[i].fVirt = fAccelFlags;
						lpaccelNew[i].key = wVKCode;
					}

					/*
					 * Создадим новую таблицу клавиш-ускорителей, а
					 * старую разрушим.
					 */

					DestroyAcceleratorTable(haccelOld);
					haccel = CreateAcceleratorTable(lpaccelNew,cAccelerators);

				   /* Разрушим диалоговое окно. */


					EndDialog(hwndDlg, TRUE);
					return 0;

				case IDCANCEL:
					EndDialog(hwndDlg, TRUE);
					return TRUE;

				default:
					break;
			}
		default:
			break;
	}
	return FALSE;
}

Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Владимир Соковиков.