2017-07-27 10:30:53 +02:00

951 lines
27 KiB
C++

// This file is part of Notepad++ project
// Copyright (C)2003 Don HO <don.h@free.fr>
//
// 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 <algorithm>
#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<int32_t>(i); //already in the list
}
_keyCombos.push_back(combo);
++_size;
return static_cast<int32_t>(_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<MacroShortcut> & 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<UserCommand> & 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<PluginCmdShortcut> & 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<HMENU>(::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<WPARAM>(&_keyCombo), reinterpret_cast<LPARAM>(&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<LPARAM>(namedKeyArray[i].name));
if (_keyCombo._key == namedKeyArray[i].id)
iFound = static_cast<int32_t>(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<int32_t>(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<int32_t>(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<int32_t>(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<LPARAM>(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<int> incrFindAccIds;
incrFindAccIds.push_back(IDM_SEARCH_FINDNEXT);
incrFindAccIds.push_back(IDM_SEARCH_FINDPREV);
incrFindAccIds.push_back(IDM_SEARCH_FINDINCREMENT);
NppParameters *pNppParam = NppParameters::getInstance();
vector<CommandShortcut> & shortcuts = pNppParam->getUserShortcuts();
vector<MacroShortcut> & macros = pNppParam->getMacroList();
vector<UserCommand> & userCommands = pNppParam->getUserCommandList();
vector<PluginCmdShortcut> & 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<ACCEL> 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<WORD>(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<int32_t>(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<CommandShortcut> commands = pNppParam->getUserShortcuts();
for(size_t i = 0; i < commands.size(); ++i) {
updateMenuItemByCommand(commands[i]);
}
vector<MacroShortcut> mcommands = pNppParam->getMacroList();
for(size_t i = 0; i < mcommands.size(); ++i) {
updateMenuItemByCommand(mcommands[i]);
}
vector<UserCommand> ucommands = pNppParam->getUserCommandList();
for(size_t i = 0; i < ucommands.size(); ++i) {
updateMenuItemByCommand(ucommands[i]);
}
vector<PluginCmdShortcut> 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<char *>(_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<UINT>(pEditView->execute(SCI_GETCODEPAGE)), 0, _sParameter.c_str(), -1, ansiBuffer, 3, NULL, NULL);
auto lParam = reinterpret_cast<LPARAM>(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<LPARAM>(&scnN));
}
}
}
void ScintillaAccelerator::init(vector<HWND> * 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<ScintillaKeyMap> & 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<int32_t>(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<int>(::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_GETCURSEL, 0, 0));
if (index == LB_ERR)
return;
setKeyComboByIndex(static_cast<int>(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<LPARAM>(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<LPARAM>(namedKeyArray[i].name));
}
for(size_t i = 0; i < _size; ++i)
{
::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(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<int32_t>(wParam), BM_GETCHECK, 0, 0);
//applyToCurrentIndex();
validateDialog();
return TRUE;
case IDC_ALT_CHECK :
_keyCombo._isAlt = BST_CHECKED == ::SendDlgItemMessage(_hSelf, static_cast<int32_t>(wParam), BM_GETCHECK, 0, 0);
//applyToCurrentIndex();
validateDialog();
return TRUE;
case IDC_SHIFT_CHECK :
_keyCombo._isShift = BST_CHECKED == ::SendDlgItemMessage(_hSelf, static_cast<int32_t>(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<int32_t>(oldsize))
{
::SendDlgItemMessage(_hSelf, IDC_LIST_KEYS, LB_INSERTSTRING, static_cast<WPARAM>(-1), reinterpret_cast<LPARAM>(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<size_t>(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;
}