sptr_t ScintillaWin::WndProc()

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;
}