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


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

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

Демонстрационный пример

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

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

Демонстрационный пример

MainMenu MENU 
{ 
    POPUP "&Character" 
    { 
        MENUITEM    "&Regular\tF5",         IDM_REGULAR 
        MENUITEM    "&Bold\tCtrl+B",        IDM_BOLD 
        MENUITEM    "&Italic\tCtrl+I",      IDM_ITALIC 
        MENUITEM    "&Underline\tCtrl+U",   IDM_ULINE 
    }
} 
 
FontAccel ACCELERATORS 
{ 
    VK_F5,  IDM_REGULAR,    VIRTKEY 
    "B",    IDM_BOLD,       CONTROL, VIRTKEY 
    "I",    IDM_ITALIC,     CONTROL, VIRTKEY 
    "U",    IDM_ULINE,      CONTROL, VIRTKEY 
}
 

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

#define IDM_REGULAR    1100
#define IDM_BOLD       1200
#define IDM_ITALIC     1300
#define IDM_ULINE      1400

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

Демонстрационный пример

// Поддержка поиска VKey 
 
#define MAXKEYS 25 
 
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;      // дескриптор главного окна 
HACCEL haccel;      // дескриптор таблицы ускорителей
 
// Процедура диалогового окна 
 
BOOL CALLBACK EdAccelProc(HWND hwndDlg, UINT uMsg,
                          WPARAM wParam, LPARAM lParam) 
{ 
    int nCurSel;            // индекс списка пунктов 
    UINT idItem;            // идентификатор пункта меню 
    UINT uItemPos;          // позиция пункта меню 
    UINT i, j = 0;          // счетчики цикла 
    static UINT cItems;     // количество пунктов в меню 
    char szTemp[32];        // буфер временных данных 
    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 и подсчитаем
       // число пунктов, которые в нем есть. В этом примере, 
       // меню имеет позицию 0. Вы должны изменить это значение,
       // если добавляйте дополнительное меню. 
            hmenu = GetSubMenu(GetMenu(hwndMain), 0); 
            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; 
}

 

Назад в оглавление
На главную страницу

Hosted by uCoz