lazarus/lcl/interfaces/customdrawn/wincallback.inc
2020-08-18 17:35:21 +00:00

1300 lines
38 KiB
PHP

{%MainUnit CustomDrawnInt.pas}
{
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
}
type
TWinControlAccess = class(TWinControl);
{*************************************************************}
{ callback routines }
{*************************************************************}
function WndClassName(Wnd: HWND): WideString; inline;
var
winClassName: array[0..19] of WideChar;
begin
GetClassName(Wnd, @winClassName, 20);
Result := winClassName;
end;
{------------------------------------------------------------------------------
Function: CallDefaultWindowProc
Params: Window - The window that receives a message
Msg - The message received
WParam - Word parameter
LParam - Long-integer parameter
Returns: 0 if Msg is handled; non-zero long-integer result otherwise
Passes message on to 'default' handler. This can be a control specific window
procedure or the default window procedure.
------------------------------------------------------------------------------}
function CallDefaultWindowProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam;
LParam: Windows.LParam): LResult;
var
PrevWndProc: Windows.WNDPROC;
setComboWindow: boolean;
WindowInfo: TWindowInfo;
begin
{$ifdef MSG_DEBUG}
DebugLn('Trace:CallDefaultWindowProc - Start');
{$endif}
WindowInfo := GetWindowInfo(Window);
PrevWndProc := WindowInfo.DefWndProc;
if (PrevWndProc = nil) or (PrevWndProc = @WindowProc) // <- prevent recursion
then
begin
{$ifdef MSG_DEBUG}
DebugLn('Trace:CallDefaultWindowProc - A');
{$endif}
Result := Windows.DefWindowProcW(Window, Msg, WParam, LParam)
end
else begin
{$ifdef MSG_DEBUG}
DebugLn('Trace:CallDefaultWindowProc - B ' + IntToHex(PtrInt(PrevWndProc), 8));
{$endif}
Result := Windows.CallWindowProc(PrevWndProc, Window, Msg, WParam, LParam);
end;
end;
var
DisabledForms: TList = nil;
function CheckMouseMovement: boolean;
// returns true if mouse did not move between lmousebutton down
var
lCursorPos: TPoint;
moveX, moveY: integer;
begin
Result := true;
{ GetCursorPos(lCursorPos);
moveX := lCursorPos.X - MouseDownPos.X;
moveY := lCursorPos.Y - MouseDownPos.Y;
Result := (-3 <= moveX) and (moveX <= 3) and (-3 <= moveY) and (moveY <= 3);}
end;
{------------------------------------------------------------------------------
Function: WindowProc
Params: Window - The window that receives a message
Msg - The message received
WParam - Word parameter
LParam - Long-integer parameter
Returns: 0 if Msg is handled; non-zero long-integer result otherwise
Handles the messages sent to the specified window, in parameter Window, by
Windows or other applications
------------------------------------------------------------------------------}
function WindowProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam;
LParam: Windows.LParam): LResult; {$if defined(win32) or defined(win64)}stdcall{$else}cdecl{$endif};
Var
LMessage: TLMessage;
PLMsg: PLMessage;
R: TRect;
P: TPoint;
NewLeft, NewTop, NewWidth, NewHeight: integer;
lWinControl, ChildWinControl: TWinControl;
TargetObject: TObject;
WinProcess: Boolean;
NotifyUserInput: Boolean;
WindowPlacement: TWINDOWPLACEMENT;
OverlayWindow: HWND;
TargetWindow: HWND;
WindowInfo: TWindowInfo;
Flags: dword;
WindowColor: Integer;
LMScroll: TLMScroll; // used by WM_HSCROLL
LMKey: TLMKey; // used by WM_KEYDOWN WM_KEYUP
LMChar: TLMChar; // used by WM_CHAR
LMMouse: TLMMouse; // used by WM_LBUTTONDBLCLK
LMContextMenu: TLMContextMenu;
LMMouseMove: TLMMouseMove; // used by WM_MOUSEMOVE
LMMouseEvent: TLMMouseEvent; // used by WM_MOUSEWHEEL
LMMove: TLMMove; // used by WM_MOVE
LMNotify: TLMNotify; // used by WM_NOTIFY
DrawListItemStruct: TDrawListItemStruct; //used by WM_DRAWITEM
CancelEndSession : Boolean;//use by WM_QUERYENDSESSION
OrgCharCode: word; // used in WM_CHAR handling
// Message information
XPos, YPos: Integer;
WParamShiftState: TShiftState;
MouseButton: TMouseButton;
UTF8Char: TUTF8Char;
NMHdr: PNMHdr absolute LParam; // used by WM_NOTIFY
TmpSize: TSize; // used by WM_MEASUREITEM
{$ifdef wince}
Info: SHRGINFO; // used by SHRecognizeGesture in WM_LBUTTONDOWN
{$endif}
// CustomDrawn specific
lEventReceiver: TWinControl;
lEventX: Integer = -1;
lEventY: Integer = -1;
function GetIsNativeControl(AWindow: HWND): Boolean;
begin
Result := False;//WndClassName(AWindow) <> ClsName;
end;
{ Differences with LCL-Win32:
* Here there are zero native controls
* Here nothing needs parent paint
* The double buffering from LCL-Win32 is not used, instead we have a different
architecture where double buffering is always on, it is impossible to turn it
off because all drawings are done to the temporary image first
}
procedure SendPaintMessage(ControlDC: HDC);
var
DC: HDC;
lBitmap, lMask: HBITMAP;
PaintRegion: HRGN;
PS : TPaintStruct;
PaintMsg: TLMPaint;
WindowOrg: Windows.POINT;
WindowWidth, WindowHeight: Integer;
DCIndex: integer;
parLeft, parTop: integer;
needParentPaint: boolean;
BufferWasSaved: Boolean;
lRawImage: TRawImage;
begin
if lWinControl = nil then exit;
{$IFDEF VerboseCDMessages}
DebugLn(Format('[SendPaintMessage]: Control:%s:%s', [lWinControl.Name, lWinControl.ClassName]));
{$ENDIF}
// create a paint message
needParentPaint := False;
LCLIntf.GetWindowSize(HWND(WindowInfo), WindowWidth, WindowHeight);
// Start the double buffering by checking if we need to increase the buffer
if (WindowInfo.BitmapWidth < WindowWidth) or (WindowInfo.BitmapHeight < WindowHeight) then
begin
// first release old objects
if WindowInfo.BitmapDC <> 0 then
begin
Windows.SelectObject(WindowInfo.BitmapDC, WindowInfo.DCBitmapOld);
Windows.DeleteObject(WindowInfo.BitmapDC);
end;
if WindowInfo.Bitmap <> 0 then Windows.DeleteObject(WindowInfo.Bitmap);
// And now create the new ones
DC := Windows.GetDC(0);
WindowInfo.BitmapDC := Windows.CreateCompatibleDC(0);
WindowInfo.BitmapWidth := WindowWidth;
WindowInfo.BitmapHeight := WindowHeight;
WindowInfo.Bitmap := Windows.CreateCompatibleBitmap(DC, WindowWidth, WindowHeight);
WindowInfo.DCBitmapOld := Windows.SelectObject(WindowInfo.BitmapDC, WindowInfo.Bitmap);
Windows.ReleaseDC(0, DC);
// Reset the image and canvas
WindowInfo.Canvas.Free;
WindowInfo.Canvas := nil;
WindowInfo.Image.Free;
WindowInfo.Image := nil;
end;
// Prepare the non-native Canvas if necessary
if (WindowInfo.Image = nil) then
begin
WinProc_RawImage_FromBitmap(lRawImage, WindowInfo.Bitmap, 0);
WindowInfo.Image := TLazIntfImage.Create(WindowWidth, WindowHeight);
WindowInfo.Image.SetRawImage(lRawImage);
end;
if (WindowInfo.Canvas = nil) then WindowInfo.Canvas := TLazCanvas.Create(WindowInfo.Image);
{$ifdef VerboseCDMessages}
DebugLn(Format('[SendPaintMessage] WindowInfo^.Canvas=%s', [dbghex(PtrInt(WindowInfo.Canvas))]));
{$endif}
// main processing
WinProcess := false;
try
if ControlDC = 0 then
begin
// ignore first erase background on themed control, paint will do everything
DC := Windows.BeginPaint(Window, @PS);
end else begin
DC := ControlDC;
PaintRegion := 0;
end;
// Draw the form
RenderForm(WindowInfo.Image, WindowInfo.Canvas, TCustomForm(lWinControl));
// Now convert the rawimage to a HBITMAP and draw it to the screen
WindowInfo.Image.GetRawImage(lRawImage);
WinProc_RawImage_CreateBitmaps(lRawImage, lBitmap, lMask, True);
Windows.SelectObject(WindowInfo.BitmapDC, lBitmap);
Windows.BitBlt(DC, 0, 0, WindowWidth, WindowHeight, WindowInfo.BitmapDC, 0, 0, SRCCOPY);
Windows.SelectObject(WindowInfo.BitmapDC, WindowInfo.Bitmap);
Windows.DeleteObject(lBitmap);
if ControlDC = 0 then
Windows.EndPaint(Window, @PS);
finally
end;
{$ifdef VerboseCDMessages}
DebugLn(':< [SendPaintMessage] Finish');
{$endif}
end;
procedure HandleSetCursor;
var
lControl: TControl;
BoundsOffset: TRect;
ACursor: TCursor;
begin
if (lWinControl <> nil) and not (csDesigning in lWinControl.ComponentState)
and (Lo(LParam) = HTCLIENT) then
begin
Windows.GetCursorPos(@(Windows.POINT(P)));
Windows.ScreenToClient(Window, @(Windows.POINT(P)));
if GetLCLClientBoundsOffset(lWinControl, BoundsOffset) then
begin
Dec(P.X, BoundsOffset.Left);
Dec(P.Y, BoundsOffset.Top);
end;
ACursor := Screen.RealCursor;
if ACursor = crDefault then
begin
// statictext controls do not get WM_SETCURSOR messages...
lControl := lWinControl.ControlAtPos(P, [capfOnlyClientAreas,
capfAllowWinControls, capfHasScrollOffset, capfRecursive]);
if lControl = nil then
lControl := lWinControl;
ACursor := lControl.Cursor;
end;
if ACursor <> crDefault then
begin
Windows.SetCursor(Screen.Cursors[ACursor]);
LMessage.Result := 1;
end;
end;
if LMessage.Result = 0 then
begin
LMessage.Msg := LM_SETCURSOR;
LMessage.WParam := WParam;
LMessage.LParam := LParam;
end;
WinProcess := false;
end;
procedure HandleSysCommand;
var
ParentForm: TCustomForm;
prevFocus: HWND;
begin
// forward keystroke to show window menu, if parent form has no menu
// if wparam contains SC_KEYMENU, lparam contains key pressed
// keymenu+space should always bring up system menu
case (WParam and $FFF0) of
SC_KEYMENU:
if (lWinControl <> nil) and (lParam <> VK_SPACE) then
begin
ParentForm := GetParentForm(lWinControl);
if (ParentForm <> nil) and (ParentForm.Menu = nil)
and (Application <> nil) and (Application.MainForm <> nil)
and (Application.MainForm <> ParentForm)
and Application.MainForm.HandleAllocated then
begin
targetWindow := Application.MainForm.Handle;
if IsWindowEnabled(targetWindow) and IsWindowVisible(targetWindow) then
begin
prevFocus := Windows.GetFocus;
Windows.SetFocus(targetWindow);
PLMsg^.Result := Windows.SendMessageW(targetWindow, WM_SYSCOMMAND, WParam, LParam);
Windows.SetFocus(prevFocus);
WinProcess := false;
end;
end;
end;
//roozbeh : we do not have these in wince!
{ SC_MINIMIZE:
begin
if (Application <> nil) and (lWinControl <> nil)
and (Application.MainForm <> nil)
and (Application.MainForm = lWinControl) then
Window := TWinCEWidgetSet(WidgetSet).AppHandle;//redirection
if (Window = TWinCEWidgetSet(WidgetSet).AppHandle)
and (Application <> nil)
and (Application.MainForm<>nil) then
begin
Windows.SetWindowPos(Window, HWND_TOP,
Application.MainForm.Left, Application.MainForm.Top,
Application.MainForm.Width, 0, SWP_NOACTIVATE);
if Application.MainForm.HandleAllocated then
Windows.ShowWindow(Application.MainForm.Handle,SW_HIDE);
Application.IntfAppMinimize;
end;
end;}
{SC_RESTORE:
begin
if (Window = TWinCEWidgetSet(WidgetSet).AppHandle)
and (Application <> nil)
and (Application.MainForm<>nil)
and Application.MainForm.HandleAllocated then
begin
PLMsg^.Result := Windows.DefWindowProc(Window, WM_SYSCOMMAND, WParam, LParam);
Windows.ShowWindow(Application.MainForm.Handle,SW_SHOW);
if Windows.IsWindowEnabled(Application.MainForm.Handle)
then Windows.SetActiveWindow(Application.MainForm.Handle);
WinProcess := false;
Application.IntfAppRestore;
end;
end;}
end;
end;
begin
//DebugLn('Trace:WindowProc - Start');
LMessage.Result := 0;
LMessage.Msg := LM_NULL;
PLMsg := @LMessage;
WinProcess := True;
NotifyUserInput := False;
{$ifdef VerboseCDMessages}
DebugLn(Format('WindowProc Window= %x FAppHandle=%x MSG=%s',
[Window, CDWidgetset.FAppHandle, WM_To_String(Msg)]));
{$endif}
//DebugLn('Trace:WindowProc - Getting Object with Callback Procedure');
WindowInfo := GetWindowInfo(Window);
lWinControl := WindowInfo.LCLForm;
{$ifdef VerboseCDMessages}
DebugLn('WindowProc lWinControl: ',DbgSName(lWinControl));
{$endif}
if (IgnoreNextCharWindow <> 0) and ((Msg = WM_CHAR) or (Msg = WM_SYSCHAR)) then
begin
if IgnoreNextCharWindow = Window then
begin
IgnoreNextCharWindow := 0;
Result := 1;
exit;
end;
IgnoreNextCharWindow := 0;
end;
{$ifdef MSG_DEBUG}
DebugLn('Trace:WindowProc - Case Msg of');
{$endif}
case Msg Of
WM_NULL:
begin
CheckSynchronize;
{TCDWidgetset(Widgetset).CheckPipeEvents;}
end;
WM_ACTIVATE:
begin
LMessage.Msg := LM_ACTIVATE;
end;
WM_CAPTURECHANGED:
begin
LMessage.Msg := LM_CAPTURECHANGED;
end;
WM_CHAR:
begin
UTF8Char := UTF8Encode(widestring(WideChar(WParam)));
CallbackKeyChar(WindowInfo, Word(Char(WideChar(WParam))), UTF8Char);
Result := 1;
Exit;
end;
WM_CLOSE:
begin
if (Window = TCDWidgetSet(WidgetSet).AppHandle) and
(Application.MainForm <> nil) then
begin
Windows.SendMessage(Application.MainForm.Handle, WM_CLOSE, 0, 0);
end else begin
LMessage.Msg := LM_CLOSEQUERY;
end;
// default is to destroy window, inhibit
WinProcess := false;
end;
{
WM_COMMAND
Parameters:
wNotifyCode = HIWORD(wParam);
wID = LOWORD(wParam);
hwndCtl = (HWND) lParam;
If wID is IDOK, then we have received a close message from the
"OK" button in the title of dialog windows.
}
WM_COMMAND:
begin
{ Handles the "OK" button in the title bar of dialogs }
if Lo(wParam) = IDOK then
begin
if (lWinControl is TCustomForm) and (fsModal in TCustomForm(lWinControl).FormState) then
TCustomForm(lWinControl).ModalResult := mrOK
else
SendMessage(Window, WM_CLOSE, 0, 0);
end;
(* else
begin
{ Handles other reasons for WM_COMMAND }
if Hi(WParam) < 2 then //1 for accelerator 0 for menu
begin
{$ifdef VerboseWinCEMenu}
DebugLn('[wincecallback] Hi(WParam) < 2');
{$endif}
TargetObject := GetMenuItemObject();
end
else // menuitem or shortcut
begin
TargetObject := nil;
end;
if TargetObject is TMenuItem then
begin
{$ifdef VerboseWinCEMenu}
DebugLn('[wincecallback] Sending menuitem Click');
{$endif}
LMessage.Msg := LM_ACTIVATE;
TargetObject.Dispatch(LMessage);
lWinControl := nil;
end
else
begin
lWinControl := GetWindowInfo(LParam)^.WinControl;
// buddy controls use 'awincontrol' to designate associated wincontrol
if lWinControl = nil then
lWinControl := GetWindowInfo(LParam)^.AWinControl;
end;
// no specific message found? try send a general msg
if (LMessage.Msg = LM_NULL) and (lWinControl <> nil) then
lWinControl.Perform(CN_COMMAND, WParam, LParam);
end;*)
end;
{
* Besides the fact that LCL does not respond to LM_CREATE, this code is
probably never reached anyway, as the callback is not set until after
window creation
WM_CREATE:
begin
//DebugLn('Trace:WindowProc - Got WM_CREATE');
LMessage.Msg := LM_CREATE;
end;
}
WM_CLEAR:
begin
LMessage.Msg := LM_CLEAR;
end;
WM_COPY:
begin
LMessage.Msg := LM_COPY;
end;
WM_CUT:
begin
LMessage.Msg := LM_CUT;
end;
WM_DESTROY:
begin
LMessage.Msg := LM_DESTROY;
end;
WM_DESTROYCLIPBOARD:
begin
if assigned(OnClipBoardRequest) then begin
// {$IFDEF VerboseWin32Clipbrd}
// debugln('WM_DESTROYCLIPBOARD');
// {$ENDIF}
OnClipBoardRequest(0, nil);
OnClipBoardRequest := nil;
LMessage.Result := 0;
end;
end;
WM_ENABLE:
begin
if WParam <> 0 then
LMessage.Msg := LM_SETEDITABLE;
if Window = TCDWidgetSet(WidgetSet).FAppHandle then
if WParam = 0 then
DisabledForms := Screen.DisableForms(nil, DisabledForms)
else
Screen.EnableForms(DisabledForms);
end;
WM_ENTERIDLE: Application.Idle(False);
WM_ERASEBKGND:
begin
{eraseBkgndCommand := TEraseBkgndCommand(EraseBkgndStack and EraseBkgndStackMask);
if eraseBkgndCommand = ecDoubleBufferNoRemove then
begin
end
else
if eraseBkgndCommand <> ecDiscardNoRemove then
EraseBkgndStack := EraseBkgndStack shr EraseBkgndStackShift;
if eraseBkgndCommand in [ecDiscard, ecDiscardNoRemove] then
begin
Result := 0;
exit;
end;
if not False or (eraseBkgndCommand = ecDoubleBufferNoRemove) then
begin
LMessage.Msg := LM_ERASEBKGND;
LMessage.WParam := WParam;
LMessage.LParam := LParam;
end else
begin
SendPaintMessage(HDC(WParam));
LMessage.Result := 1;
end;}
WinProcess := False;
end;
{ WM_EXITMENULOOP:
// is it a popup menu
if longbool(WPARAM) and assigned(WindowInfo^.PopupMenu) then
WindowInfo^.PopupMenu.Close;
WM_GETDLGCODE:
begin
LMessage.Result := DLGC_WANTALLKEYS;
WinProcess := false;
end;}
{
* TODO: make it work... icon does not show up yet, so better disable it
WM_GETICON:
begin
if WindowInfo^.WinControl is TCustomForm then
begin
LMessage.Result := TCustomForm(WindowInfo^.WinControl).GetIconHandle;
WinProcess := false;
end;
end;
}
WM_KEYDOWN:
begin
CallbackKeyDown(WindowInfo, Word(WParam));
Result := 1;
Exit;
end;
WM_KEYUP:
begin
CallbackKeyUp(WindowInfo, Word(WParam));
Result := 1;
Exit;
end;
WM_KILLFOCUS:
begin
{$ifdef DEBUG_CARET}
DebugLn('WM_KILLFOCUS received for window ', IntToHex(Window, 8));
{$endif}
LMessage.Msg := LM_KILLFOCUS;
LMessage.WParam := WParam;
end;
//TODO:LM_KILLCHAR,LM_KILLWORD,LM_KILLLINE
WM_LBUTTONDBLCLK:
begin
NotifyUserInput := True;
PLMsg:=@LMMouse;
with LMMouse Do
begin
Msg := LM_LBUTTONDBLCLK;
XPos := SmallInt(Lo(LParam));
YPos := SmallInt(Hi(LParam));
Keys := WParam;
lEventX := XPos;
lEventY := YPos;
end;
end;
WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN:
begin
{$ifdef wince}
// Gesture recognition process to enable popup menus.
if (lWinControl.PopupMenu <> nil) and (Msg = WM_LBUTTONDOWN) then
begin
Info.cbSize := SizeOf(SHRGINFO);
Info.dwFlags := SHRG_RETURNCMD;
Info.hwndClient := Window;
Info.ptDown.x := Lo(LParam);
Info.ptDown.y := Hi(LParam);
SHRecognizeGesture(Info);
end;
{$endif}
XPos := SmallInt(Lo(LParam));
YPos := SmallInt(Hi(LParam));
if GetLCLClientBoundsOffset(WindowInfo, R) then
begin
Dec(XPos, R.Left);
Dec(YPos, R.Top);
end;
WParamShiftState := KeysToShiftState(WParam); //MsgKeyDataToShiftState
case Msg of
WM_LBUTTONDOWN: MouseButton := mbLeft;
WM_MBUTTONDOWN: MouseButton := mbMiddle;
WM_RBUTTONDOWN: MouseButton := mbRight;
end;
CallbackMouseDown(WindowInfo, XPos, YPos, MouseButton, WParamShiftState);
Result := 1;
// focus window
if (Windows.GetFocus <> Window) and
((lWinControl = nil) or (lWinControl.CanFocus)) then
Windows.SetFocus(Window);
Exit;
end;
WM_LBUTTONUP, WM_MBUTTONUP, WM_RBUTTONUP:
begin
XPos := SmallInt(Lo(LParam));
YPos := SmallInt(Hi(LParam));
case Msg of
WM_LBUTTONUP: MouseButton := mbLeft;
WM_MBUTTONUP: MouseButton := mbMiddle;
WM_RBUTTONUP: MouseButton := mbRight;
end;
WParamShiftState := KeysToShiftState(WParam); //MsgKeyDataToShiftState
CallbackMouseUp(WindowInfo, XPos, YPos, MouseButton, WParamShiftState);
Result := 1;
Exit;
end;
WM_MBUTTONDBLCLK:
begin
NotifyUserInput := True;
PLMsg:=@LMMouse;
with LMMouse Do
begin
Msg := LM_MBUTTONDBLCLK;
XPos := SmallInt(Lo(LParam));
YPos := SmallInt(Hi(LParam));
Keys := WParam;
lEventX := XPos;
lEventY := YPos;
end;
end;
WM_MOUSEHOVER:
begin
NotifyUserInput := True;
LMessage.Msg := LM_ENTER;
end;
WM_MOUSELEAVE:
begin
NotifyUserInput := True;
LMessage.Msg := LM_LEAVE;
end;
WM_MOUSEMOVE:
begin
XPos := SmallInt(Lo(LParam));
YPos := SmallInt(Hi(LParam));
if GetLCLClientBoundsOffset(WindowInfo, R) then
begin
Dec(XPos, R.Left);
Dec(YPos, R.Top);
end;
WParamShiftState := KeysToShiftState(WParam);
CallbackMouseMove(WindowInfo, XPos, YPos, WParamShiftState);
Result := 1;
Exit;
end;
WM_MOUSEWHEEL:
begin
NotifyUserInput := True;
PLMsg:=@LMMouseEvent;
with LMMouseEvent Do
begin
X := SmallInt(Lo(LParam));
Y := SmallInt(Hi(LParam));
// check if mouse cursor within this window, otherwise send message to window the mouse is hovering over
P.X := X;
P.Y := Y;
lEventX := X;
lEventY := Y;
TargetWindow := TCDWidgetSet(WidgetSet).WindowFromPoint(P);
if TargetWindow = HWND(nil) then
exit;
// the mousewheel message is for us
// windows handles combobox's mousewheel messages
if (lWinControl=nil) or (lWinControl.FCompStyle <> csComboBox) then
begin
Msg := LM_MOUSEWHEEL;
Button := Lo(WParam);
WheelDelta := SmallInt(Hi(WParam));
State := KeysToShiftState(Button);
UserData := Pointer(GetWindowLong(Window, GWL_USERDATA));
WinProcess := false;
end;
end;
end;
{$IFDEF EnableWMDropFiles}
WM_DROPFILES:
begin
LMessage.Msg := LM_DROPFILES;
LMessage.WParam := WParam;
LMessage.LParam := LParam;
end;
{$ENDIF}
//TODO:LM_MOVEPAGE,LM_MOVETOROW,LM_MOVETOCOLUMN
WM_NCACTIVATE:
begin
// do not allow main form to be deactivated
if (Application <> nil) and (Application.MainForm <> nil) and
Application.MainForm.HandleAllocated and (Window = Application.MainForm.Handle) and
(WParam = 0) then
begin
WParam := 1;
end;
end;
WM_NCHITTEST:
begin
if (lWinControl <> nil) then begin
if (lWinControl.FCompStyle = csHintWindow) then
begin
LMessage.Result := HTTRANSPARENT;
WinProcess := false;
end;
end;
end;
WM_NCLBUTTONDOWN:
begin
NotifyUserInput := True;
//DebugLn('Trace:WindowProc - Got WM_NCLBUTTONDOWN');
end;
WM_PAINT:
begin
SendPaintMessage(HDC(WParam));
// SendPaintMessage sets winprocess to false
end;
WM_PRINTCLIENT:
begin
if ((LParam and PRF_CLIENT) = PRF_CLIENT) and (lWinControl <> nil) then
SendPaintMessage(HDC(WParam));
end;
WM_PASTE:
begin
LMessage.Msg := LM_PASTE;
end;
WM_RBUTTONDBLCLK:
begin
NotifyUserInput := True;
PLMsg:=@LMMouse;
with LMMouse Do
begin
Msg := LM_RBUTTONDBLCLK;
XPos := SmallInt(Lo(LParam));
YPos := SmallInt(Hi(LParam));
Keys := WParam;
end;
end;
WM_CONTEXTMENU:
begin
{$ifndef WinCE}
WinProcess := false;
NotifyUserInput := True;
PLMsg := @LMContextMenu;
with LMContextMenu do
begin
Msg := LM_CONTEXTMENU;
XPos := GET_X_LPARAM(LParam);
YPos := GET_Y_LPARAM(LParam);
hWnd := Window;
Result := 0;
end;
{$endif}
end;
WM_SETCURSOR:
begin
HandleSetCursor;
end;
WM_SETFOCUS:
begin
{$ifdef DEBUG_CARET}
DebugLn('WM_SETFOCUS received for window ', IntToHex(Window, 8));
{$endif}
// handle feature mouse-click, setfocus, mouse-click -> double-click
if (Window <> MouseDownWindow) and (MouseDownFocusStatus <> mfNone) then
begin
MouseDownFocusStatus := mfFocusChanged;
MouseDownFocusWindow := Window;
end;
LMessage.Msg := LM_SETFOCUS;
if (lWinControl <> nil) and (lWinControl.FCompStyle = csEdit) then
Windows.SendMessage(Window, EM_SETSEL, 0, -1);
end;
WM_SHOWWINDOW:
begin
//DebugLn('Trace:WindowProc - Got WM_SHOWWINDOW');
with TLMShowWindow(LMessage) Do
begin
Msg := LM_SHOWWINDOW;
Show := WParam <> 0;
Status := LParam;
end;
if assigned(lWinControl) and ((WParam<>0) or not lWinControl.Visible)
and ((WParam=0) or lWinControl.Visible)
and (Application<>nil) and (lWinControl=Application.MainForm) then
begin
if WParam=0 then
Flags := SW_HIDE
else
Flags := SW_SHOWNOACTIVATE;
Windows.ShowWindow(TCDWidgetSet(WidgetSet).FAppHandle, Flags);
end;
end;
WM_SYSCHAR:
begin
PLMsg:=@LMChar;
with LMChar Do
begin
Msg := CN_SYSCHAR;
KeyData := LParam;
CharCode := Word(WParam);
Result := 0;
//DebugLn(Format('WM_CHAR KeyData= %d CharCode= %d ',[KeyData,CharCode]));
end;
WinProcess := false;
end;
WM_SYSCOMMAND:
begin
HandleSysCommand;
end;
WM_SYSKEYDOWN:
begin
NotifyUserInput := True;
PLMsg:=@LMKey;
with LMKey Do
begin
Msg := CN_SYSKEYDOWN;
KeyData := LParam;
CharCode := Word(WParam);
Result := 0;
end;
WinProcess := false;
end;
WM_SYSKEYUP:
begin
NotifyUserInput := True;
PLMsg:=@LMKey;
with LMKey Do
begin
Msg := CN_SYSKEYUP;
KeyData := LParam;
CharCode := Word(WParam);
Result := 0;
end;
WinProcess := false;
end;
WM_TIMER:
begin
LMessage.Msg := LM_TIMER;
LMessage.WParam := WParam;
LMessage.LParam := LParam;
end;
WM_WINDOWPOSCHANGED:
begin
with TLMWindowPosMsg(LMessage) Do
begin
Msg := LM_WINDOWPOSCHANGED;
Unused := WParam;
WindowPos := PWindowPos(LParam);
end;
// cross-interface compatible: complete invalidate on resize
if (PWindowPos(LParam)^.flags and SWP_NOSIZE) = 0 then
Windows.InvalidateRect(Window, nil, true);
end;
{ WM_LCL_SOCK_ASYNC:
begin
if (Window = TWinCEWidgetSet(WidgetSet).AppHandle) and
Assigned(TWinCEWidgetSet(WidgetSet).FOnAsyncSocketMsg) then
exit(TWinCEWidgetSet(WidgetSet).FOnAsyncSocketMsg(WParam, LParam))
end;}
{$ifdef wince}
WM_HOTKEY:
begin
// Implements back-key sending to edits, instead of hiding the form
// See http://bugs.freepascal.org/view.php?id=16699
{if HIWORD(lParam) = VK_ESCAPE then
begin
SHSendBackToFocusWindow(Msg, wParam, lParam);
Exit;
end;}
end;
{$endif}
else
// pass along user defined messages
if Msg >= WM_USER then
begin
LMessage.Msg := Msg;
LMessage.WParam := WParam;
LMessage.LParam := LParam;
WinProcess := false;
end;
end;
{$ifdef MSG_DEBUG}
DebugLn('Trace:WindowProc - End Case Msg of');
{$endif}
if WinProcess Then
begin
{$ifdef MSG_DEBUG}
DebugLn('Trace:WindowProc - if WinProcess Then');
{$endif}
PLMsg^.Result := CallDefaultWindowProc(Window, Msg, WParam, LParam);
WinProcess := false;
{$ifdef MSG_DEBUG}
DebugLn('Trace:WindowProc - End if WinProcess Then');
{$endif}
end;
case Msg of
WM_MOVE:
begin
{$ifndef WinCE}
PLMsg:=@LMMove;
with LMMove Do
begin
Msg := LM_MOVE;
// MoveType := WParam; WParam is not defined!
MoveType := Move_SourceIsInterface;
if GetWindowLong(Window, GWL_STYLE) and WS_CHILD = 0 then
begin
WindowPlacement.length := SizeOf(WindowPlacement);
if IsIconic(Window) and GetWindowPlacement(Window, @WindowPlacement) then
begin
with WindowPlacement.rcNormalPosition do
begin
XPos := Left;
YPos := Top;
end;
end
else
if Windows.GetWindowRect(Window, @R) then
begin
XPos := R.Left;
YPos := R.Top;
end
else
Msg := LM_NULL;
end else
begin
if LCLIntf.GetWindowRelativePosition(HWND(WindowInfo), NewLeft, NewTop) then
begin
XPos := NewLeft;
YPos := NewTop;
end
else
Msg := LM_NULL;
end;
if lWinControl <> nil then
begin
{$IFDEF VerboseSizeMsg}
DebugLn('Win32CallBack WM_MOVE ', dbgsName(lWinControl),
' NewPos=',dbgs(XPos),',',dbgs(YPos));
{$ENDIF}
if (lWinControl.Left = XPos) and (lWinControl.Top = YPos) then
Exit;
end;
end;
{$endif}
end;
WM_SIZE:
begin
with TLMSize(LMessage) do
begin
Msg := LM_SIZE;
SizeType := WParam or Size_SourceIsInterface;
if Window = CDWidgetSet.AppHandle then
begin
if Assigned(Application.MainForm) and Application.MainForm.HandleAllocated then
begin
lWinControl := Application.MainForm;
Window := Application.MainFormHandle;
end;
end;
LCLIntf.GetWindowSize(HWND(WindowInfo), NewWidth, NewHeight);
Width := NewWidth;
Height := NewHeight;
if Assigned(lWinControl) then
begin
{$IFDEF VerboseSizeMsg}
GetClientRect(Window,R);
DebugLn('Win32Callback: WM_SIZE '+ dbgsName(lWinControl)+
' NewSize=', dbgs(Width)+','+dbgs(Height)+
' HasVScroll='+dbgs((GetWindowLong(Window, GWL_STYLE) and WS_VSCROLL) <> 0)+
' HasHScroll='+dbgs((GetWindowLong(Window, GWL_STYLE) and WS_HSCROLL) <> 0)+
' OldClientSize='+dbgs(lWinControl.CachedClientWidth)+','+dbgs(lWinControl.CachedClientHeight)+
' NewClientSize='+dbgs(R.Right)+','+dbgs(R.Bottom));
{$ENDIF}
if (lWinControl.Width <> Width) or
(lWinControl.Height <> Height) or
lWinControl.ClientRectNeedsInterfaceUpdate then
lWinControl.DoAdjustClientRectChange
else
// If we get form size message then we probably changed it state
// (minimized/maximized -> normal). Form adjust its clientrect in the
// second WM_SIZE but WM_MOVE also updates clientrect without adjustment
// thus we need to call DoAdjustClientRectChange. It is safe since this
// methods checks whether it need to adjust something really.
if (lWinControl is TCustomForm) and (lWinControl.Parent = nil) and
(WParam = Size_Restored) then
lWinControl.DoAdjustClientRectChange(False);
end;
end;
end;
{WM_ENDSESSION:
begin
if (Application<>nil) and (TWinCEWidgetSet(WidgetSet).AppHandle=Window) and
(WParam>0) and (LParam=0) then
begin
LMessage.Msg := LM_NULL; // no need to go through delivermessage
Application.IntfEndSession();
LMessage.Result := 0;
end;
end;}
{WM_QUERYENDSESSION:
begin
if (Application<>nil) and (TWinCEWidgetSet(WidgetSet).AppHandle=Window) and
(LParam=0) then
begin
LMessage.Msg := LM_NULL; // no need to go through delivermessage
CancelEndSession := LMessage.Result=0;
Application.IntfQueryEndSession(CancelEndSession);
if CancelEndSession
then LMessage.Result := 0
else LMessage.Result := 1;
end;
end;}
end;
// convert from wince client to lcl client pos.
//
// hack to prevent GetLCLClientBoundsOffset from changing mouse client
// coordinates for TScrollingWinControls, this is required in
// IsControlMouseMsg and ControlAtPos where unscrolled client coordinates
// are expected.
if (PLMsg = @LMMouseMove) and not (lWinControl is TScrollingWinControl) then
begin
if GetLCLClientBoundsOffset(WindowInfo, R) then
begin
Dec(LMMouseMove.XPos, R.Left);
Dec(LMMouseMove.YPos, R.Top);
end;
end else
if (PLMsg = @LMMouse) and not (lWinControl is TScrollingWinControl) then
begin
if GetLCLClientBoundsOffset(WindowInfo, R) then
begin
Dec(LMMouse.XPos, R.Left);
Dec(LMMouse.YPos, R.Top);
end;
end;
// application processing
if NotifyUserInput then
NotifyApplicationUserInput(lWinControl, PLMsg^.Msg);
if (lWinControl <> nil) and (PLMsg^.Msg <> LM_NULL) then
begin
if (lEventX < 0) or (lEventY < 0) then
begin
DeliverMessage(lWinControl, PLMsg^);
end
else
begin
lEventReceiver := FindControlWhichReceivedEvent(
TCustomForm(lWinControl),
GetCDWinControlList(TCustomForm(lWinControl)),
lEventX, lEventY);
DeliverMessage(lEventReceiver, PLMsg^);
end;
end;
// respond to result of LCL handling the message
case PLMsg^.Msg of
LM_ERASEBKGND, LM_SETCURSOR, LM_RBUTTONUP:
begin
if PLMsg^.Result = 0 then
WinProcess := true;
end;
CN_CHAR, CN_SYSCHAR:
begin
// if key not yet processed, let windows process it
WinProcess := LMChar.Result = 0;
if (LMChar.Result=1) or (OrgCharCode<>LMChar.CharCode) then
WParam := Word(WideChar(Char(LMChar.CharCode)));
// WParam := LMChar.CharCode;
end;
CN_KEYDOWN, CN_KEYUP, CN_SYSKEYDOWN, CN_SYSKEYUP:
begin
// if key not yet processed, let windows process it
WinProcess := LMKey.Result = 0;
WParam := LMKey.CharCode;
end;
else
case Msg of
WM_LBUTTONDOWN, WM_LBUTTONUP:
begin
if MouseDownFocusStatus = mfFocusSense then
MouseDownFocusStatus := mfNone;
end;
WM_NCDESTROY:
begin
WindowInfo.Free;
WindowInfo := nil;
end;
end;
end;
if WinProcess then
begin
PLMsg^.Result := CallDefaultWindowProc(Window, Msg, WParam, LParam);
case Msg of
WM_CHAR, WM_KEYDOWN, WM_KEYUP,
WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP:
begin
PLMsg^.Result := 0;
case Msg of
WM_CHAR:
begin
// if want chars, then handled already
PLMsg^.Result := CallDefaultWindowProc(Window, WM_GETDLGCODE, WParam, 0) and DLGC_WANTCHARS;
LMChar.CharCode := Word(WParam);
LMChar.Msg := LM_CHAR;
end;
WM_SYSCHAR:
begin
LMChar.CharCode := Word(WParam);
LMChar.Msg := LM_SYSCHAR;
end;
WM_KEYDOWN:
begin
LMKey.CharCode := Word(WParam);
LMKey.Msg := LM_KEYDOWN;
end;
WM_KEYUP:
begin
LMKey.CharCode := Word(WParam);
LMKey.Msg := LM_KEYUP;
end;
WM_SYSKEYDOWN:
begin
LMKey.CharCode := Word(WParam);
LMKey.Msg := LM_SYSKEYDOWN;
end;
WM_SYSKEYUP:
begin
LMKey.CharCode := Word(WParam);
LMKey.Msg := LM_SYSKEYUP;
end;
end;
// we cannot tell for sure windows didn't want the key
// for WM_CHAR check WM_GETDLGCODE/DLGC_WANTCHARS
// winapi too inconsistent about return value
if PLMsg^.Result = 0 then
DeliverMessage(lWinControl, PLMsg^);
{ // handle the hardware back key (= VK_ESCAPE) for edit controls
// It should work as the backspace
if (PLMsg^.Result = 0) and (Msg = WM_KEYDOWN) and (WParam = VK_ESCAPE)
{and (GetKeyState(VK_CONTROL) >= 0) and (GetKeyState(VK_MENU) >= 0)} then
begin
if (WndClassName(Window) = EditClsName) then
begin
Windows.SendMessage(Window, WM_KEYDOWN, VK_BACK, -1);
end;
end;}
end;
end;
end;
// ignore WM_(SYS)CHAR message if LCL handled WM_(SYS)KEYDOWN
if ((Msg = WM_KEYDOWN) or (Msg = WM_SYSKEYDOWN)) then
begin
if (PLMsg^.Result <> 0) then
begin
IgnoreNextCharWindow := Window;
end else begin
// stop ignoring if KEYUP has come by (not all keys generate CHAR)
// assume WM_CHAR is always preceded by WM_KEYDOWN
IgnoreNextCharWindow := 0;
end;
end;
{ LMMouseEvent and LMInsertText have no Result field }
if PLMsg = @LMScroll then Result := LMScroll.Result
else if PLMsg = @LMKey then Result := LMKey.Result
else if PLMsg = @LMChar then Result := LMChar.Result
else if PLMsg = @LMMouse then Result := LMMouse.Result
else if PLMsg = @LMMouseMove then Result := LMMouseMove.Result
else if PLMsg = @LMMove then Result := LMMove.Result
else if PLMsg = @LMNotify then Result := LMNotify.Result
else if PLMsg = @LMMouseEvent then Result := 1
else Result := PLMsg^.Result;
//DebugLn('Trace:WindowProc - Exit');
end;
{------------------------------------------------------------------------------
Procedure: TimerCallBackProc
Params: window_hnd - handle of window for timer message, not set in this implementation
msg - WM_TIMER message
idEvent - timer identifier
dwTime - current system time
Calls the timerfunction in the Timer Object in the LCL
------------------------------------------------------------------------------}
procedure TimerCallBackProc(window_hwnd : hwnd; msg :UINT; idEvent: UINT_PTR; dwTime: DWORD);
{$if defined(win32) or defined(win64)}stdcall{$else}cdecl{$endif};
Var
TimerInfo: PWinCETimerInfo;
n: Integer;
begin
n := FTimerData.Count;
while (n>0) do begin
dec(n);
TimerInfo := FTimerData[n];
if TimerInfo^.TimerID=idEvent then begin
TimerInfo^.TimerFunc;
break;
end;
end;
end;