in ext/scintilla/win32/ScintillaWin.cxx [1247:1790]
sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
try {
//Platform::DebugPrintf("S M:%x WP:%x L:%x\n", iMessage, wParam, lParam);
iMessage = SciMessageFromEM(iMessage);
switch (iMessage) {
case WM_CREATE:
ctrlID = ::GetDlgCtrlID(static_cast<HWND>(wMain.GetID()));
// Get Intellimouse scroll line parameters
GetIntelliMouseParameters();
::RegisterDragDrop(MainHWND(), reinterpret_cast<IDropTarget *>(&dt));
break;
case WM_COMMAND:
Command(LOWORD(wParam));
break;
case WM_PAINT:
return WndPaint();
case WM_PRINTCLIENT: {
HDC hdc = reinterpret_cast<HDC>(wParam);
if (!IsCompatibleDC(hdc)) {
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
}
FullPaintDC(hdc);
}
break;
case WM_VSCROLL:
ScrollMessage(wParam);
break;
case WM_HSCROLL:
HorizontalScrollMessage(wParam);
break;
case WM_SIZE: {
#if defined(USE_D2D)
if (paintState == notPainting) {
DropRenderTarget();
} else {
renderTargetValid = false;
}
#endif
//Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LOWORD(lParam), HIWORD(lParam));
ChangeSize();
}
break;
case WM_MOUSEWHEEL:
if (!mouseWheelCaptures) {
// if the mouse wheel is not captured, test if the mouse
// pointer is over the editor window and if not, don't
// handle the message but pass it on.
RECT rc;
GetWindowRect(MainHWND(), &rc);
POINT pt;
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
if (!PtInRect(&rc, pt))
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
}
// if autocomplete list active then send mousewheel message to it
if (ac.Active()) {
HWND hWnd = static_cast<HWND>(ac.lb->GetID());
::SendMessage(hWnd, iMessage, wParam, lParam);
break;
}
// Don't handle datazoom.
// (A good idea for datazoom would be to "fold" or "unfold" details.
// i.e. if datazoomed out only class structures are visible, when datazooming in the control
// structures appear, then eventually the individual statements...)
if (wParam & MK_SHIFT) {
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
}
// Either SCROLL or ZOOM. We handle the wheel steppings calculation
wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
if (std::abs(wheelDelta) >= WHEEL_DELTA && linesPerScroll > 0) {
Sci::Line linesToScroll = linesPerScroll;
if (linesPerScroll == WHEEL_PAGESCROLL)
linesToScroll = LinesOnScreen() - 1;
if (linesToScroll == 0) {
linesToScroll = 1;
}
linesToScroll *= (wheelDelta / WHEEL_DELTA);
if (wheelDelta >= 0)
wheelDelta = wheelDelta % WHEEL_DELTA;
else
wheelDelta = - (-wheelDelta % WHEEL_DELTA);
if (wParam & MK_CONTROL) {
// Zoom! We play with the font sizes in the styles.
// Number of steps/line is ignored, we just care if sizing up or down
if (linesToScroll < 0) {
KeyCommand(SCI_ZOOMIN);
} else {
KeyCommand(SCI_ZOOMOUT);
}
} else {
// Scroll
ScrollTo(topLine + linesToScroll);
}
}
return 0;
case WM_TIMER:
if (wParam == idleTimerID && idler.state) {
SendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);
} else {
TickFor(static_cast<TickReason>(wParam - fineTimerStart));
}
break;
case SC_WIN_IDLE:
// wParam=dwTickCountInitial, or 0 to initialize. lParam=bSkipUserInputTest
if (idler.state) {
if (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, 0, 0, 0, QS_INPUT|QS_HOTKEY))) {
if (Idle()) {
// User input was given priority above, but all events do get a turn. Other
// messages, notifications, etc. will get interleaved with the idle messages.
// However, some things like WM_PAINT are a lower priority, and will not fire
// when there's a message posted. So, several times a second, we stop and let
// the low priority events have a turn (after which the timer will fire again).
// Suppress a warning from Code Analysis that the GetTickCount function
// wraps after 49 days. The WM_TIMER will kick off another SC_WIN_IDLE
// after the wrap.
#ifdef _MSC_VER
#pragma warning(suppress: 28159)
#endif
const DWORD dwCurrent = GetTickCount();
const DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;
const DWORD maxWorkTime = 50;
if (dwCurrent >= dwStart && dwCurrent > maxWorkTime && dwCurrent - maxWorkTime < dwStart)
PostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);
} else {
SetIdle(false);
}
}
}
break;
case SC_WORK_IDLE:
IdleWork();
break;
case WM_GETMINMAXINFO:
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
case WM_LBUTTONDOWN: {
// For IME, set the composition string as the result string.
IMContext imc(MainHWND());
::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
//
//Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
// KeyboardIsKeyDown(VK_SHIFT),
// KeyboardIsKeyDown(VK_CONTROL),
// KeyboardIsKeyDown(VK_MENU));
::SetFocus(MainHWND());
ButtonDownWithModifiers(PointFromLParam(lParam), ::GetMessageTime(),
MouseModifiers(wParam));
}
break;
case WM_MOUSEMOVE: {
const Point pt = PointFromLParam(lParam);
// Windows might send WM_MOUSEMOVE even though the mouse has not been moved:
// http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
if (ptMouseLast != pt) {
SetTrackMouseLeaveEvent(true);
ButtonMoveWithModifiers(pt, ::GetMessageTime(), MouseModifiers(wParam));
}
}
break;
case WM_MOUSELEAVE:
SetTrackMouseLeaveEvent(false);
MouseLeave();
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
case WM_LBUTTONUP:
ButtonUpWithModifiers(PointFromLParam(lParam),
::GetMessageTime(), MouseModifiers(wParam));
break;
case WM_RBUTTONDOWN: {
::SetFocus(MainHWND());
const Point pt = PointFromLParam(lParam);
if (!PointInSelection(pt)) {
CancelModes();
SetEmptySelection(PositionFromLocation(PointFromLParam(lParam)));
}
RightButtonDownWithModifiers(pt, ::GetMessageTime(), MouseModifiers(wParam));
}
break;
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT) {
if (inDragDrop == ddDragging) {
DisplayCursor(Window::cursorUp);
} else {
// Display regular (drag) cursor over selection
POINT pt;
if (0 != ::GetCursorPos(&pt)) {
::ScreenToClient(MainHWND(), &pt);
if (PointInSelMargin(PointFromPOINT(pt))) {
DisplayCursor(GetMarginCursor(PointFromPOINT(pt)));
} else if (PointInSelection(PointFromPOINT(pt)) && !SelectionEmpty()) {
DisplayCursor(Window::cursorArrow);
} else if (PointIsHotspot(PointFromPOINT(pt))) {
DisplayCursor(Window::cursorHand);
} else {
DisplayCursor(Window::cursorText);
}
}
}
return TRUE;
} else {
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
}
case WM_CHAR:
if (((wParam >= 128) || !iscntrl(static_cast<int>(wParam))) || !lastKeyDownConsumed) {
wchar_t wcs[3] = {static_cast<wchar_t>(wParam), 0};
unsigned int wclen = 1;
if (IS_HIGH_SURROGATE(wcs[0])) {
// If this is a high surrogate character, we need a second one
lastHighSurrogateChar = wcs[0];
return 0;
} else if (IS_LOW_SURROGATE(wcs[0])) {
wcs[1] = wcs[0];
wcs[0] = lastHighSurrogateChar;
lastHighSurrogateChar = 0;
wclen = 2;
}
AddWString(std::wstring_view(wcs, wclen));
}
return 0;
case WM_UNICHAR:
if (wParam == UNICODE_NOCHAR) {
return TRUE;
} else if (lastKeyDownConsumed) {
return 1;
} else {
wchar_t wcs[3] = {0};
const unsigned int wclen = UTF16FromUTF32Character(static_cast<unsigned int>(wParam), wcs);
AddWString(std::wstring_view(wcs, wclen));
return FALSE;
}
case WM_SYSKEYDOWN:
case WM_KEYDOWN: {
//Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL));
lastKeyDownConsumed = false;
const int ret = KeyDownWithModifiers(KeyTranslate(static_cast<int>(wParam)),
ModifierFlags(KeyboardIsKeyDown(VK_SHIFT),
KeyboardIsKeyDown(VK_CONTROL),
KeyboardIsKeyDown(VK_MENU)),
&lastKeyDownConsumed);
if (!ret && !lastKeyDownConsumed) {
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
}
break;
}
case WM_IME_KEYDOWN: {
if (wParam == VK_HANJA) {
ToggleHanja();
}
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
}
case WM_IME_REQUEST: {
if (wParam == IMR_RECONVERTSTRING) {
return ImeOnReconvert(lParam);
}
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
}
case WM_KEYUP:
//Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam);
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
case WM_SETTINGCHANGE:
//Platform::DebugPrintf("Setting Changed\n");
InvalidateStyleData();
// Get Intellimouse scroll line parameters
GetIntelliMouseParameters();
break;
case WM_GETDLGCODE:
return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
case WM_KILLFOCUS: {
HWND wOther = reinterpret_cast<HWND>(wParam);
HWND wThis = MainHWND();
const HWND wCT = static_cast<HWND>(ct.wCallTip.GetID());
if (!wParam ||
!(::IsChild(wThis, wOther) || (wOther == wCT))) {
SetFocusState(false);
DestroySystemCaret();
}
// Explicitly complete any IME composition
IMContext imc(MainHWND());
if (imc.hIMC) {
::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
}
}
break;
case WM_SETFOCUS:
SetFocusState(true);
DestroySystemCaret();
CreateSystemCaret();
break;
case WM_SYSCOLORCHANGE:
//Platform::DebugPrintf("Setting Changed\n");
InvalidateStyleData();
break;
case WM_IME_STARTCOMPOSITION: // dbcs
if (KoreanIME() || imeInteraction == imeInline) {
return 0;
} else {
ImeStartComposition();
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
}
case WM_IME_ENDCOMPOSITION: // dbcs
ImeEndComposition();
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
case WM_IME_COMPOSITION:
if (KoreanIME() || imeInteraction == imeInline) {
return HandleCompositionInline(wParam, lParam);
} else {
return HandleCompositionWindowed(wParam, lParam);
}
case WM_CONTEXTMENU: {
Point pt = PointFromLParam(lParam);
POINT rpt = {static_cast<int>(pt.x), static_cast<int>(pt.y)};
::ScreenToClient(MainHWND(), &rpt);
const Point ptClient = PointFromPOINT(rpt);
if (ShouldDisplayPopup(ptClient)) {
if ((pt.x == -1) && (pt.y == -1)) {
// Caused by keyboard so display menu near caret
pt = PointMainCaret();
POINT spt = POINTFromPoint(pt);
::ClientToScreen(MainHWND(), &spt);
pt = PointFromPOINT(spt);
}
ContextMenu(pt);
return 0;
}
}
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
case WM_INPUTLANGCHANGE:
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
case WM_INPUTLANGCHANGEREQUEST:
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
case WM_ERASEBKGND:
return 1; // Avoid any background erasure as whole window painted.
case WM_CAPTURECHANGED:
capturedMouse = false;
return 0;
case WM_IME_SETCONTEXT:
if (KoreanIME() || imeInteraction == imeInline) {
if (wParam) {
LPARAM NoImeWin = lParam;
NoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);
return ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);
}
}
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
// These are not handled in Scintilla and its faster to dispatch them here.
// Also moves time out to here so profile doesn't count lots of empty message calls.
case WM_MOVE:
case WM_MOUSEACTIVATE:
case WM_NCHITTEST:
case WM_NCCALCSIZE:
case WM_NCPAINT:
case WM_NCMOUSEMOVE:
case WM_NCLBUTTONDOWN:
case WM_IME_NOTIFY:
case WM_SYSCOMMAND:
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED:
return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);
case WM_GETTEXTLENGTH:
return GetTextLength();
case WM_GETTEXT:
return GetText(wParam, lParam);
case EM_LINEFROMCHAR:
if (static_cast<int>(wParam) < 0) {
wParam = SelectionStart().Position();
}
return pdoc->LineFromPosition(static_cast<int>(wParam));
case EM_EXLINEFROMCHAR:
return pdoc->LineFromPosition(static_cast<int>(lParam));
case EM_GETSEL:
if (wParam) {
*reinterpret_cast<int *>(wParam) = static_cast<int>(SelectionStart().Position());
}
if (lParam) {
*reinterpret_cast<int *>(lParam) = static_cast<int>(SelectionEnd().Position());
}
return MAKELRESULT(SelectionStart().Position(), SelectionEnd().Position());
case EM_EXGETSEL: {
if (lParam == 0) {
return 0;
}
Sci_CharacterRange *pCR = reinterpret_cast<Sci_CharacterRange *>(lParam);
pCR->cpMin = static_cast<Sci_PositionCR>(SelectionStart().Position());
pCR->cpMax = static_cast<Sci_PositionCR>(SelectionEnd().Position());
}
break;
case EM_SETSEL: {
Sci::Position nStart = static_cast<Sci::Position>(wParam);
Sci::Position nEnd = lParam;
if (nStart == 0 && nEnd == -1) {
nEnd = pdoc->Length();
}
if (nStart == -1) {
nStart = nEnd; // Remove selection
}
SetSelection(nEnd, nStart);
EnsureCaretVisible();
}
break;
case EM_EXSETSEL: {
if (lParam == 0) {
return 0;
}
const Sci_CharacterRange *pCR = reinterpret_cast<const Sci_CharacterRange *>(lParam);
sel.selType = Selection::selStream;
if (pCR->cpMin == 0 && pCR->cpMax == -1) {
SetSelection(pCR->cpMin, pdoc->Length());
} else {
SetSelection(pCR->cpMin, pCR->cpMax);
}
EnsureCaretVisible();
return pdoc->LineFromPosition(SelectionStart().Position());
}
case SCI_GETDIRECTFUNCTION:
return reinterpret_cast<sptr_t>(DirectFunction);
case SCI_GETDIRECTPOINTER:
return reinterpret_cast<sptr_t>(this);
case SCI_GRABFOCUS:
::SetFocus(MainHWND());
break;
#ifdef INCLUDE_DEPRECATED_FEATURES
case SCI_SETKEYSUNICODE:
break;
case SCI_GETKEYSUNICODE:
return true;
#endif
case SCI_SETTECHNOLOGY:
if ((wParam == SC_TECHNOLOGY_DEFAULT) ||
(wParam == SC_TECHNOLOGY_DIRECTWRITERETAIN) ||
(wParam == SC_TECHNOLOGY_DIRECTWRITEDC) ||
(wParam == SC_TECHNOLOGY_DIRECTWRITE)) {
const int technologyNew = static_cast<int>(wParam);
if (technology != technologyNew) {
if (technologyNew > SC_TECHNOLOGY_DEFAULT) {
#if defined(USE_D2D)
if (!LoadD2D())
// Failed to load Direct2D or DirectWrite so no effect
return 0;
#else
return 0;
#endif
} else {
bidirectional = EditModel::Bidirectional::bidiDisabled;
}
#if defined(USE_D2D)
DropRenderTarget();
#endif
technology = technologyNew;
// Invalidate all cached information including layout.
DropGraphics(true);
InvalidateStyleRedraw();
}
}
break;
case SCI_SETBIDIRECTIONAL:
if (technology == SC_TECHNOLOGY_DEFAULT) {
bidirectional = EditModel::Bidirectional::bidiDisabled;
} else if (wParam <= SC_BIDIRECTIONAL_R2L) {
bidirectional = static_cast<EditModel::Bidirectional>(wParam);
}
// Invalidate all cached information including layout.
DropGraphics(true);
InvalidateStyleRedraw();
break;
#ifdef SCI_LEXER
case SCI_LOADLEXERLIBRARY:
LexerManager::GetInstance()->Load(ConstCharPtrFromSPtr(lParam));
break;
#endif
case SCI_TARGETASUTF8:
return TargetAsUTF8(CharPtrFromSPtr(lParam));
case SCI_ENCODEDFROMUTF8:
return EncodedFromUTF8(ConstCharPtrFromUPtr(wParam),
CharPtrFromSPtr(lParam));
default:
return ScintillaBase::WndProc(iMessage, wParam, lParam);
}
} catch (std::bad_alloc &) {
errorStatus = SC_STATUS_BADALLOC;
} catch (...) {
errorStatus = SC_STATUS_FAILURE;
}
return 0;
}