// This file is part of Notepad++ project // Copyright (C)2003 Don HO // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // Note that the GPL places important restrictions on "derived works", yet // it does not provide a detailed definition of that term. To avoid // misunderstandings, we consider an application to constitute a // "derivative work" for the purpose of this license if it does any of the // following: // 1. Integrates source code from Notepad++. // 2. Integrates/includes/aggregates Notepad++ into a proprietary executable // installer, such as those produced by InstallShield. // 3. Links to a library or executes a program that does any of the above. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include "shortcut.h" #include "Parameters.h" #include "ScintillaEditView.h" #include "resource.h" #include "Notepad_plus_Window.h" #include "keys.h" using namespace std; const int KEY_STR_LEN = 16; struct KeyIDNAME { const TCHAR * name; UCHAR id; }; KeyIDNAME namedKeyArray[] = { {TEXT("None"), VK_NULL}, {TEXT("Backspace"), VK_BACK}, {TEXT("Tab"), VK_TAB}, {TEXT("Enter"), VK_RETURN}, {TEXT("Esc"), VK_ESCAPE}, {TEXT("Spacebar"), VK_SPACE}, {TEXT("Page up"), VK_PRIOR}, {TEXT("Page down"), VK_NEXT}, {TEXT("End"), VK_END}, {TEXT("Home"), VK_HOME}, {TEXT("Left"), VK_LEFT}, {TEXT("Up"), VK_UP}, {TEXT("Right"), VK_RIGHT}, {TEXT("Down"), VK_DOWN}, {TEXT("INS"), VK_INSERT}, {TEXT("DEL"), VK_DELETE}, {TEXT("0"), VK_0}, {TEXT("1"), VK_1}, {TEXT("2"), VK_2}, {TEXT("3"), VK_3}, {TEXT("4"), VK_4}, {TEXT("5"), VK_5}, {TEXT("6"), VK_6}, {TEXT("7"), VK_7}, {TEXT("8"), VK_8}, {TEXT("9"), VK_9}, {TEXT("A"), VK_A}, {TEXT("B"), VK_B}, {TEXT("C"), VK_C}, {TEXT("D"), VK_D}, {TEXT("E"), VK_E}, {TEXT("F"), VK_F}, {TEXT("G"), VK_G}, {TEXT("H"), VK_H}, {TEXT("I"), VK_I}, {TEXT("J"), VK_J}, {TEXT("K"), VK_K}, {TEXT("L"), VK_L}, {TEXT("M"), VK_M}, {TEXT("N"), VK_N}, {TEXT("O"), VK_O}, {TEXT("P"), VK_P}, {TEXT("Q"), VK_Q}, {TEXT("R"), VK_R}, {TEXT("S"), VK_S}, {TEXT("T"), VK_T}, {TEXT("U"), VK_U}, {TEXT("V"), VK_V}, {TEXT("W"), VK_W}, {TEXT("X"), VK_X}, {TEXT("Y"), VK_Y}, {TEXT("Z"), VK_Z}, {TEXT("Numpad 0"), VK_NUMPAD0}, {TEXT("Numpad 1"), VK_NUMPAD1}, {TEXT("Numpad 2"), VK_NUMPAD2}, {TEXT("Numpad 3"), VK_NUMPAD3}, {TEXT("Numpad 4"), VK_NUMPAD4}, {TEXT("Numpad 5"), VK_NUMPAD5}, {TEXT("Numpad 6"), VK_NUMPAD6}, {TEXT("Numpad 7"), VK_NUMPAD7}, {TEXT("Numpad 8"), VK_NUMPAD8}, {TEXT("Numpad 9"), VK_NUMPAD9}, {TEXT("Num *"), VK_MULTIPLY}, {TEXT("Num +"), VK_ADD}, //{TEXT("Num Enter"), VK_SEPARATOR}, //this one doesnt seem to work {TEXT("Num -"), VK_SUBTRACT}, {TEXT("Num ."), VK_DECIMAL}, {TEXT("Num /"), VK_DIVIDE}, {TEXT("F1"), VK_F1}, {TEXT("F2"), VK_F2}, {TEXT("F3"), VK_F3}, {TEXT("F4"), VK_F4}, {TEXT("F5"), VK_F5}, {TEXT("F6"), VK_F6}, {TEXT("F7"), VK_F7}, {TEXT("F8"), VK_F8}, {TEXT("F9"), VK_F9}, {TEXT("F10"), VK_F10}, {TEXT("F11"), VK_F11}, {TEXT("F12"), VK_F12}, {TEXT("~"), VK_OEM_3}, {TEXT("-"), VK_OEM_MINUS}, {TEXT("="), VK_OEM_PLUS}, {TEXT("["), VK_OEM_4}, {TEXT("]"), VK_OEM_6}, {TEXT(";"), VK_OEM_1}, {TEXT("'"), VK_OEM_7}, {TEXT("\\"), VK_OEM_5}, {TEXT(","), VK_OEM_COMMA}, {TEXT("."), VK_OEM_PERIOD}, {TEXT("/"), VK_OEM_2}, {TEXT("<>"), VK_OEM_102}, }; #define nbKeys sizeof(namedKeyArray)/sizeof(KeyIDNAME) generic_string Shortcut::toString() const { generic_string sc = TEXT(""); if (!isEnabled()) return sc; if (_keyCombo._isCtrl) sc += TEXT("Ctrl+"); if (_keyCombo._isAlt) sc += TEXT("Alt+"); if (_keyCombo._isShift) sc += TEXT("Shift+"); generic_string keyString; getKeyStrFromVal(_keyCombo._key, keyString); sc += keyString; return sc; } void Shortcut::setName(const TCHAR * name) { lstrcpyn(_menuName, name, nameLenMax); lstrcpyn(_name, name, nameLenMax); int i = 0, j = 0; while(name[j] != 0 && i < nameLenMax) { if (name[j] != '&') { _name[i] = name[j]; ++i; } else { //check if this ampersand is being escaped if (name[j+1] == '&') { //escaped ampersand _name[i] = name[j]; ++i; ++j; //skip escaped ampersand } } ++j; } _name[i] = 0; } generic_string ScintillaKeyMap::toString() const { return toString(0); } generic_string ScintillaKeyMap::toString(size_t index) const { generic_string sc = TEXT(""); if (!isEnabled()) return sc; KeyCombo kc = _keyCombos[index]; if (kc._isCtrl) sc += TEXT("Ctrl+"); if (kc._isAlt) sc += TEXT("Alt+"); if (kc._isShift) sc += TEXT("Shift+"); generic_string keyString; getKeyStrFromVal(kc._key, keyString); sc += keyString; return sc; } KeyCombo ScintillaKeyMap::getKeyComboByIndex(size_t index) const { return _keyCombos[index]; } void ScintillaKeyMap::setKeyComboByIndex(int index, KeyCombo combo) { if(combo._key == 0 && (_size > 1)) { //remove the item if possible _keyCombos.erase(_keyCombos.begin() + index); } _keyCombos[index] = combo; } void ScintillaKeyMap::removeKeyComboByIndex(size_t index) { if (_size > 1 && index < _size) { _keyCombos.erase(_keyCombos.begin() + index); _size--; } } int ScintillaKeyMap::addKeyCombo(KeyCombo combo) { //returns index where key is added, or -1 when invalid if (combo._key == 0) //do not allow to add disabled keycombos return -1; if (!isEnabled()) { //disabled, override current combo with new enabled one _keyCombos[0] = combo; return 0; } for(size_t i = 0; i < _size; ++i) { //if already in the list do not add it KeyCombo & kc = _keyCombos[i]; if (combo._key == kc._key && combo._isCtrl == kc._isCtrl && combo._isAlt == kc._isAlt && combo._isShift == kc._isShift) return static_cast(i); //already in the list } _keyCombos.push_back(combo); ++_size; return static_cast(_size - 1); } bool ScintillaKeyMap::isEnabled() const { return (_keyCombos[0]._key != 0); } size_t ScintillaKeyMap::getSize() const { return _size; } void getKeyStrFromVal(UCHAR keyVal, generic_string & str) { str = TEXT(""); bool found = false; int i; for (i = 0; i < nbKeys; ++i) { if (keyVal == namedKeyArray[i].id) { found = true; break; } } if (found) str = namedKeyArray[i].name; else str = TEXT("Unlisted"); } void getNameStrFromCmd(DWORD cmd, generic_string & str) { if ((cmd >= ID_MACRO) && (cmd < ID_MACRO_LIMIT)) { vector & theMacros = (NppParameters::getInstance())->getMacroList(); int i = cmd - ID_MACRO; str = theMacros[i].getName(); } else if ((cmd >= ID_USER_CMD) && (cmd < ID_USER_CMD_LIMIT)) { vector & userCommands = (NppParameters::getInstance())->getUserCommandList(); int i = cmd - ID_USER_CMD; str = userCommands[i].getName(); } else if ((cmd >= ID_PLUGINS_CMD) && (cmd < ID_PLUGINS_CMD_LIMIT)) { vector & pluginCmds = (NppParameters::getInstance())->getPluginCommandList(); size_t i = 0; for (size_t j = 0, len = pluginCmds.size(); j < len ; ++j) { if (pluginCmds[j].getID() == cmd) { i = j; break; } } str = pluginCmds[i].getName(); } else { HWND hNotepad_plus = ::FindWindow(Notepad_plus_Window::getClassName(), NULL); const int commandSize = 64; TCHAR cmdName[commandSize]; HMENU m = reinterpret_cast(::SendMessage(hNotepad_plus, NPPM_INTERNAL_GETMENU, 0, 0)); int nbChar = ::GetMenuString(m, cmd, cmdName, commandSize, MF_BYCOMMAND); if (!nbChar) return; bool fin = false; int j = 0; size_t len = lstrlen(cmdName); for (size_t i = 0 ; i < len; ++i) { switch(cmdName[i]) { case '\t': cmdName[j] = '\0'; fin = true; break; case '&': break; default : cmdName[j++] = cmdName[i]; } if (fin) break; } cmdName[j] = '\0'; str = cmdName; } return; } void Shortcut::updateConflictState(const bool endSession) const { if (endSession) { // Clean up message for detached dialogs: save Macros/RunCommands ::SendMessage(_hParent, NPPM_INTERNAL_FINDKEYCONFLICTS, 0, 0); return; } // Check for conflicts bool isConflict = false; ::SendMessage(_hParent, NPPM_INTERNAL_FINDKEYCONFLICTS, reinterpret_cast(&_keyCombo), reinterpret_cast(&isConflict)); ::ShowWindow(::GetDlgItem(_hSelf, IDC_CONFLICT_STATIC), isConflict ? SW_SHOW : SW_HIDE); } INT_PTR CALLBACK Shortcut::run_dlgProc(UINT Message, WPARAM wParam, LPARAM) { switch (Message) { case WM_INITDIALOG : { ::SetDlgItemText(_hSelf, IDC_NAME_EDIT, getMenuName()); //display the menu name, with ampersands if (!_canModifyName) ::SendDlgItemMessage(_hSelf, IDC_NAME_EDIT, EM_SETREADONLY, TRUE, 0); auto textlen = ::SendDlgItemMessage(_hSelf, IDC_NAME_EDIT, WM_GETTEXTLENGTH, 0, 0); ::SendDlgItemMessage(_hSelf, IDC_CTRL_CHECK, BM_SETCHECK, _keyCombo._isCtrl?BST_CHECKED:BST_UNCHECKED, 0); ::SendDlgItemMessage(_hSelf, IDC_ALT_CHECK, BM_SETCHECK, _keyCombo._isAlt?BST_CHECKED:BST_UNCHECKED, 0); ::SendDlgItemMessage(_hSelf, IDC_SHIFT_CHECK, BM_SETCHECK, _keyCombo._isShift?BST_CHECKED:BST_UNCHECKED, 0); ::EnableWindow(::GetDlgItem(_hSelf, IDOK), isValid() && (textlen > 0 || !_canModifyName)); int iFound = -1; for (size_t i = 0 ; i < nbKeys ; ++i) { ::SendDlgItemMessage(_hSelf, IDC_KEY_COMBO, CB_ADDSTRING, 0, reinterpret_cast(namedKeyArray[i].name)); if (_keyCombo._key == namedKeyArray[i].id) iFound = static_cast(i); } if (iFound != -1) ::SendDlgItemMessage(_hSelf, IDC_KEY_COMBO, CB_SETCURSEL, iFound, 0); ::ShowWindow(::GetDlgItem(_hSelf, IDC_WARNING_STATIC), isEnabled()?SW_HIDE:SW_SHOW); updateConflictState(); goToCenter(); return TRUE; } case WM_COMMAND : { auto textlen = ::SendDlgItemMessage(_hSelf, IDC_NAME_EDIT, WM_GETTEXTLENGTH, 0, 0); switch (wParam) { case IDC_CTRL_CHECK : _keyCombo._isCtrl = BST_CHECKED == ::SendDlgItemMessage(_hSelf, static_cast(wParam), BM_GETCHECK, 0, 0); ::EnableWindow(::GetDlgItem(_hSelf, IDOK), isValid() && (textlen > 0 || !_canModifyName)); updateConflictState(); return TRUE; case IDC_ALT_CHECK : _keyCombo._isAlt = BST_CHECKED == ::SendDlgItemMessage(_hSelf, static_cast(wParam), BM_GETCHECK, 0, 0); ::EnableWindow(::GetDlgItem(_hSelf, IDOK), isValid() && (textlen > 0 || !_canModifyName)); updateConflictState(); return TRUE; case IDC_SHIFT_CHECK : _keyCombo._isShift = BST_CHECKED == ::SendDlgItemMessage(_hSelf, static_cast(wParam), BM_GETCHECK, 0, 0); updateConflictState(); return TRUE; case IDOK : if (!isEnabled()) { _keyCombo._isCtrl = _keyCombo._isAlt = _keyCombo._isShift = false; } if (_canModifyName) { TCHAR editName[nameLenMax]; ::SendDlgItemMessage(_hSelf, IDC_NAME_EDIT, WM_GETTEXT, nameLenMax, reinterpret_cast(editName)); setName(editName); } ::EndDialog(_hSelf, 0); updateConflictState(true); return TRUE; case IDCANCEL : ::EndDialog(_hSelf, -1); updateConflictState(true); return TRUE; default: if (HIWORD(wParam) == EN_CHANGE) { if (LOWORD(wParam) == IDC_NAME_EDIT) { ::EnableWindow(::GetDlgItem(_hSelf, IDOK), isValid() && (textlen > 0 || !_canModifyName)); return TRUE; } } else if (HIWORD(wParam) == CBN_SELCHANGE) { if (LOWORD(wParam) == IDC_KEY_COMBO) { auto i = ::SendDlgItemMessage(_hSelf, LOWORD(wParam), CB_GETCURSEL, 0, 0); _keyCombo._key = namedKeyArray[i].id; ::EnableWindow(::GetDlgItem(_hSelf, IDOK), isValid() && (textlen > 0 || !_canModifyName)); ::ShowWindow(::GetDlgItem(_hSelf, IDC_WARNING_STATIC), isEnabled()?SW_HIDE:SW_SHOW); updateConflictState(); return TRUE; } } return FALSE; } } default : return FALSE; } } // return true if one of CommandShortcuts is deleted. Otherwise false. void Accelerator::updateShortcuts() { vector incrFindAccIds; incrFindAccIds.push_back(IDM_SEARCH_FINDNEXT); incrFindAccIds.push_back(IDM_SEARCH_FINDPREV); incrFindAccIds.push_back(IDM_SEARCH_FINDINCREMENT); NppParameters *pNppParam = NppParameters::getInstance(); vector & shortcuts = pNppParam->getUserShortcuts(); vector & macros = pNppParam->getMacroList(); vector & userCommands = pNppParam->getUserCommandList(); vector & pluginCommands = pNppParam->getPluginCommandList(); size_t nbMenu = shortcuts.size(); size_t nbMacro = macros.size(); size_t nbUserCmd = userCommands.size(); size_t nbPluginCmd = pluginCommands.size(); if (_pAccelArray) delete [] _pAccelArray; _pAccelArray = new ACCEL[nbMenu+nbMacro+nbUserCmd+nbPluginCmd]; vector incrFindAcc; ACCEL *pSearchFindAccel = nullptr; int offset = 0; size_t i = 0; //no validation performed, it might be that invalid shortcuts are being used by default. Allows user to 'hack', might be a good thing for(i = 0; i < nbMenu; ++i) { if (shortcuts[i].isEnabled()) { _pAccelArray[offset].cmd = static_cast(shortcuts[i].getID()); _pAccelArray[offset].fVirt = shortcuts[i].getAcceleratorModifiers(); _pAccelArray[offset].key = shortcuts[i].getKeyCombo()._key; // Special extra handling for shortcuts shared by Incremental Find dialog if (std::find(incrFindAccIds.begin(), incrFindAccIds.end(), shortcuts[i].getID()) != incrFindAccIds.end()) incrFindAcc.push_back(_pAccelArray[offset]); if (shortcuts[i].getID() == IDM_SEARCH_FIND) pSearchFindAccel = &_pAccelArray[offset]; ++offset; } } for(i = 0; i < nbMacro; ++i) { if (macros[i].isEnabled()) { _pAccelArray[offset].cmd = (WORD)(macros[i].getID()); _pAccelArray[offset].fVirt = macros[i].getAcceleratorModifiers(); _pAccelArray[offset].key = macros[i].getKeyCombo()._key; ++offset; } } for(i = 0; i < nbUserCmd; ++i) { if (userCommands[i].isEnabled()) { _pAccelArray[offset].cmd = (WORD)(userCommands[i].getID()); _pAccelArray[offset].fVirt = userCommands[i].getAcceleratorModifiers(); _pAccelArray[offset].key = userCommands[i].getKeyCombo()._key; ++offset; } } for(i = 0; i < nbPluginCmd; ++i) { if (pluginCommands[i].isEnabled()) { _pAccelArray[offset].cmd = (WORD)(pluginCommands[i].getID()); _pAccelArray[offset].fVirt = pluginCommands[i].getAcceleratorModifiers(); _pAccelArray[offset].key = pluginCommands[i].getKeyCombo()._key; ++offset; } } _nbAccelItems = offset; updateFullMenu(); //update the table if (_hAccTable) ::DestroyAcceleratorTable(_hAccTable); _hAccTable = ::CreateAcceleratorTable(_pAccelArray, _nbAccelItems); if (_hIncFindAccTab) ::DestroyAcceleratorTable(_hIncFindAccTab); size_t nb = incrFindAcc.size(); ACCEL *tmpIncrFindAccelArray = new ACCEL[nb]; for (i = 0; i < nb; ++i) { tmpIncrFindAccelArray[i] = incrFindAcc[i]; } _hIncFindAccTab = ::CreateAcceleratorTable(tmpIncrFindAccelArray, static_cast(nb)); delete [] tmpIncrFindAccelArray; if (_hIncFindAccTab) ::DestroyAcceleratorTable(_hIncFindAccTab); if (_hFindAccTab) ::DestroyAcceleratorTable(_hFindAccTab); if (pSearchFindAccel != nullptr) { ACCEL *tmpFindAccelArray = new ACCEL[1]; tmpFindAccelArray[0] = *pSearchFindAccel; _hFindAccTab = ::CreateAcceleratorTable(tmpFindAccelArray, 1); delete[] tmpFindAccelArray; } return; } void Accelerator::updateFullMenu() { NppParameters * pNppParam = NppParameters::getInstance(); vector commands = pNppParam->getUserShortcuts(); for(size_t i = 0; i < commands.size(); ++i) { updateMenuItemByCommand(commands[i]); } vector mcommands = pNppParam->getMacroList(); for(size_t i = 0; i < mcommands.size(); ++i) { updateMenuItemByCommand(mcommands[i]); } vector ucommands = pNppParam->getUserCommandList(); for(size_t i = 0; i < ucommands.size(); ++i) { updateMenuItemByCommand(ucommands[i]); } vector pcommands = pNppParam->getPluginCommandList(); for(size_t i = 0; i < pcommands.size(); ++i) { updateMenuItemByCommand(pcommands[i]); } ::DrawMenuBar(_hMenuParent); } void Accelerator::updateMenuItemByCommand(CommandShortcut csc) { int cmdID = csc.getID(); // Ensure that the menu item checks set prior to this update remain in affect. UINT cmdFlags = GetMenuState(_hAccelMenu, cmdID, MF_BYCOMMAND ); cmdFlags = MF_BYCOMMAND | ((cmdFlags&MF_CHECKED) ? MF_CHECKED : MF_UNCHECKED); ::ModifyMenu(_hAccelMenu, cmdID, cmdFlags, cmdID, csc.toMenuItemString().c_str()); } recordedMacroStep::recordedMacroStep(int iMessage, uptr_t wParam, uptr_t lParam, int codepage) : _message(iMessage), _wParameter(wParam), _lParameter(lParam), _macroType(mtUseLParameter) { if (_lParameter) { switch (_message) { case SCI_SETTEXT : case SCI_REPLACESEL : case SCI_REPLACETARGET : case SCI_REPLACETARGETRE : case SCI_SEARCHINTARGET : case SCI_ADDTEXT : case SCI_ADDSTYLEDTEXT : case SCI_INSERTTEXT : case SCI_APPENDTEXT : case SCI_SETWORDCHARS : case SCI_SETWHITESPACECHARS : case SCI_SETSTYLINGEX : case SCI_TEXTWIDTH : case SCI_STYLESETFONT : case SCI_SEARCHNEXT : case SCI_SEARCHPREV : case IDFINDWHAT: case IDREPLACEWITH: case IDD_FINDINFILES_DIR_COMBO: case IDD_FINDINFILES_FILTERS_COMBO: { char *ch = reinterpret_cast(_lParameter); TCHAR tch[2]; ::MultiByteToWideChar(codepage, 0, ch, -1, tch, 2); _sParameter = *tch; _macroType = mtUseSParameter; _lParameter = 0; } break; default : // for all other messages, use _lParameter "as is" break; } } } void recordedMacroStep::PlayBack(Window* pNotepad, ScintillaEditView *pEditView) { if (_macroType == mtMenuCommand) ::SendMessage(pNotepad->getHSelf(), WM_COMMAND, _wParameter, 0); else { if (_macroType == mtUseSParameter) { char ansiBuffer[3]; ::WideCharToMultiByte(static_cast(pEditView->execute(SCI_GETCODEPAGE)), 0, _sParameter.c_str(), -1, ansiBuffer, 3, NULL, NULL); auto lParam = reinterpret_cast(ansiBuffer); pEditView->execute(_message, _wParameter, lParam); } else { pEditView->execute(_message, _wParameter, _lParameter); } if ( (_message == SCI_SETTEXT) || (_message == SCI_REPLACESEL) || (_message == SCI_ADDTEXT) || (_message == SCI_ADDSTYLEDTEXT) || (_message == SCI_INSERTTEXT) || (_message == SCI_APPENDTEXT) ) { SCNotification scnN; scnN.nmhdr.code = SCN_CHARADDED; scnN.nmhdr.hwndFrom = pEditView->getHSelf(); scnN.nmhdr.idFrom = 0; if(_sParameter.empty()) scnN.ch = 0; else scnN.ch = _sParameter.at(0); ::SendMessage(pNotepad->getHSelf(), WM_NOTIFY, 0, reinterpret_cast(&scnN)); } } } void ScintillaAccelerator::init(vector * vScintillas, HMENU hMenu, HWND menuParent) { _hAccelMenu = hMenu; _hMenuParent = menuParent; size_t nbScintilla = vScintillas->size(); for (size_t i = 0; i < nbScintilla; ++i) { _vScintillas.push_back(vScintillas->at(i)); } } void ScintillaAccelerator::updateKeys() { NppParameters *pNppParam = NppParameters::getInstance(); vector & map = pNppParam->getScintillaKeyList(); size_t mapSize = map.size(); size_t index; size_t nb = nbScintillas(); for (size_t i = 0; i < nb; ++i) { ::SendMessage(_vScintillas[i], SCI_CLEARALLCMDKEYS, 0, 0); for(int32_t j = static_cast(mapSize) - 1; j >= 0; j--) //reverse order, top of the list has highest priority { ScintillaKeyMap skm = map[j]; if (skm.isEnabled()) { //no validating, scintilla accepts more keys size_t size = skm.getSize(); for(index = 0; index < size; ++index) ::SendMessage(_vScintillas[i], SCI_ASSIGNCMDKEY, skm.toKeyDef(index), skm.getScintillaKeyID()); } if (skm.getMenuCmdID() != 0) { updateMenuItemByID(skm, skm.getMenuCmdID()); } if (j == 0) //j is unsigned, so default method doesnt work break; } } } void ScintillaAccelerator::updateMenuItemByID(ScintillaKeyMap skm, int id) { const int commandSize = 64; TCHAR cmdName[commandSize]; ::GetMenuString(_hAccelMenu, id, cmdName, commandSize, MF_BYCOMMAND); int i = 0; while(cmdName[i] != 0) { if (cmdName[i] == '\t') { cmdName[i] = 0; break; } ++i; } generic_string menuItem = cmdName; if (skm.isEnabled()) { menuItem += TEXT("\t"); //menuItem += TEXT("Sc:"); //sc: scintilla shortcut menuItem += skm.toString(); } ::ModifyMenu(_hAccelMenu, id, MF_BYCOMMAND, id, menuItem.c_str()); ::DrawMenuBar(_hMenuParent); } //This procedure uses _keyCombo as a temp. variable to store current settings which can then later be applied (by pressing OK) void ScintillaKeyMap::applyToCurrentIndex() { int index = static_cast(::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_GETCURSEL, 0, 0)); if (index == LB_ERR) return; setKeyComboByIndex(static_cast(index), _keyCombo); updateListItem(index); ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_SETCURSEL, index, 0); } void ScintillaKeyMap::validateDialog() { bool valid = isValid(); //current combo valid? bool isDisabling = _keyCombo._key == 0; //true if this keycombo were to disable the shortcut bool isDisabled = !isEnabled(); //true if this shortcut already is bool isDuplicate = false; //true if already in the list for (size_t i = 0; i < _size; ++i) { if (_keyCombo._key == _keyCombos[i]._key && _keyCombo._isCtrl == _keyCombos[i]._isCtrl && _keyCombo._isAlt == _keyCombos[i]._isAlt && _keyCombo._isShift == _keyCombos[i]._isShift) { isDuplicate = true; break; } } ::EnableWindow(::GetDlgItem(_hSelf, IDC_BUTTON_ADD), valid && !isDisabling && !isDuplicate); ::EnableWindow(::GetDlgItem(_hSelf, IDC_BUTTON_APPLY), valid && (!isDisabling || _size == 1) && !isDuplicate); ::EnableWindow(::GetDlgItem(_hSelf, IDC_BUTTON_RMVE), (_size > 1)?TRUE:FALSE); ::ShowWindow(::GetDlgItem(_hSelf, IDC_WARNING_STATIC), isDisabled?SW_SHOW:SW_HIDE); updateConflictState(); } void ScintillaKeyMap::showCurrentSettings() { auto keyIndex = ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_GETCURSEL, 0, 0); _keyCombo = _keyCombos[keyIndex]; ::SendDlgItemMessage(_hSelf, IDC_CTRL_CHECK, BM_SETCHECK, _keyCombo._isCtrl?BST_CHECKED:BST_UNCHECKED, 0); ::SendDlgItemMessage(_hSelf, IDC_ALT_CHECK, BM_SETCHECK, _keyCombo._isAlt?BST_CHECKED:BST_UNCHECKED, 0); ::SendDlgItemMessage(_hSelf, IDC_SHIFT_CHECK, BM_SETCHECK, _keyCombo._isShift?BST_CHECKED:BST_UNCHECKED, 0); for (size_t i = 0 ; i < nbKeys ; ++i) { if (_keyCombo._key == namedKeyArray[i].id) { ::SendDlgItemMessage(_hSelf, IDC_KEY_COMBO, CB_SETCURSEL, i, 0); break; } } } void ScintillaKeyMap::updateListItem(int index) { ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_INSERTSTRING, index, reinterpret_cast(toString(index).c_str())); ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_DELETESTRING, index+1, 0); } INT_PTR CALLBACK ScintillaKeyMap::run_dlgProc(UINT Message, WPARAM wParam, LPARAM) { switch (Message) { case WM_INITDIALOG : { ::SetDlgItemText(_hSelf, IDC_NAME_EDIT, _name); _keyCombo = _keyCombos[0]; for (size_t i = 0 ; i < nbKeys ; ++i) { ::SendDlgItemMessage(_hSelf, IDC_KEY_COMBO, CB_ADDSTRING, 0, reinterpret_cast(namedKeyArray[i].name)); } for(size_t i = 0; i < _size; ++i) { ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_ADDSTRING, 0, reinterpret_cast(toString(i).c_str())); } ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_SETCURSEL, 0, 0); showCurrentSettings(); validateDialog(); goToCenter(); return TRUE; } case WM_COMMAND : { switch (wParam) { case IDC_CTRL_CHECK : _keyCombo._isCtrl = BST_CHECKED == ::SendDlgItemMessage(_hSelf, static_cast(wParam), BM_GETCHECK, 0, 0); //applyToCurrentIndex(); validateDialog(); return TRUE; case IDC_ALT_CHECK : _keyCombo._isAlt = BST_CHECKED == ::SendDlgItemMessage(_hSelf, static_cast(wParam), BM_GETCHECK, 0, 0); //applyToCurrentIndex(); validateDialog(); return TRUE; case IDC_SHIFT_CHECK : _keyCombo._isShift = BST_CHECKED == ::SendDlgItemMessage(_hSelf, static_cast(wParam), BM_GETCHECK, 0, 0); //applyToCurrentIndex(); validateDialog(); return TRUE; case IDOK : //Cleanup _keyCombo._key = 0; _keyCombo._isCtrl = _keyCombo._isAlt = _keyCombo._isShift = false; ::EndDialog(_hSelf, 0); return TRUE; case IDCANCEL : ::EndDialog(_hSelf, -1); return TRUE; case IDC_BUTTON_ADD: { size_t oldsize = _size; int res = addKeyCombo(_keyCombo); if (res > -1) { if (res == static_cast(oldsize)) { ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_INSERTSTRING, static_cast(-1), reinterpret_cast(toString(res).c_str())); } else { //update current generic_string, can happen if it was disabled updateListItem(res); } ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_SETCURSEL, res, 0); } showCurrentSettings(); validateDialog(); return TRUE; } case IDC_BUTTON_RMVE: { if (_size == 1) //cannot delete last shortcut return TRUE; auto i = ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_GETCURSEL, 0, 0); removeKeyComboByIndex(i); ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_DELETESTRING, i, 0); if (static_cast(i) == _size) i = _size - 1; ::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_SETCURSEL, i, 0); showCurrentSettings(); validateDialog(); return TRUE; } case IDC_BUTTON_APPLY: { applyToCurrentIndex(); validateDialog(); return TRUE; } default: if (HIWORD(wParam) == CBN_SELCHANGE || HIWORD(wParam) == LBN_SELCHANGE) { switch(LOWORD(wParam)) { case IDC_KEY_COMBO: { auto i = ::SendDlgItemMessage(_hSelf, IDC_KEY_COMBO, CB_GETCURSEL, 0, 0); _keyCombo._key = namedKeyArray[i].id; //applyToCurrentIndex(); validateDialog(); return TRUE; } case IDC_LIST_KEYS: { showCurrentSettings(); validateDialog(); return TRUE; } } } return FALSE; } } default : return FALSE; } //return FALSE; }