How to disable copy/paste in TEdit - clipboard

I would like to prevent copy, cut and paste in my TEdit. How can I do this?
I tried setting the Key=NULL on KeyDown event when CTRL+V was pressed on the control, but it didn't work.

You'll need to prevent the WM_CUT, WM_COPY, and WM_PASTE messages from being sent to your TEdit. This answer describes how do to this using just the Windows API. For the VCL, it may be sufficient to subclass TEdit and change its DefWndProc property or override its WndProc method.

Assign this to TEdit.OnKeyPress :
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if (Key=#22) or (Key=#3) then Key:=#0; // 22 = [Ctrl+V] / 3 = [Ctrl+C]
end;

I know this is an old question but I'll add what I have found. The original poster almost had the solution. It works fine if you ignore cut/copy/paste in the key press event instead of the key down event. ie (c++ builder)
void __fastcall Form::OnKeyPress(TObject *Sender, System::WideChar &Key)
{
if( Key==0x03/*ctrl-c*/ || Key==0x16/*ctrl-v*/ || Key==0x018/*ctrl-x*/ )
Key = 0; //ignore key press
}

You can use some global programs that grab shortcuts and block C-V C-C C-X when TEdit window is active

Uses Clipbrd;
procedure TForm1.Edit1Enter(Sender: TObject);
begin
Clipboard.AsText := '';
end;

An old question, but the same bad answers are still floating around.
unit LockEdit;
// Version of TEdit with a property CBLocked that prevents copying, pasting,
// and cutting when the property is set.
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, StdCtrls, Windows;
type
TLockEdit = class(TEdit)
protected
procedure WndProc(var msg: TMessage); override;
private
FLocked: boolean;
public
property CBLocked: boolean read FLocked write FLocked default false;
end;
implementation
procedure TLockEdit.WndProc(Var msg: TMessage);
begin
if ((msg.msg = WM_PASTE) or (msg.msg = WM_COPY) or (msg.msg = WM_CUT))
and CBLocked
then msg.msg:=WM_NULL;
inherited;
end;
end.

Related

Changing a TMemo font size # runtime

How does one change a FMX.TMemo font size # runtime? In the following app the Spinbox1.Change method does not change the Memo.Font.Size. I have tried the BeginUpdate() and EndUpdate() methods of the TMemo. I have also tried Memo1.Repaint() and nothing seems to work. I have looked at every property, function and procedure for TMemo, but, I can't find what I need. Later versions of Delphi have TTextSettings for TMemo, but, XE5 does not. I also tried a class helper for TMemo to add a TTextSettings property, but, to no avail.
unit Unit13;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
FMX.Edit, FMX.Layouts, FMX.Memo;
type
TForm13 = class(TForm)
Memo1: TMemo;
ToolBar1: TToolBar;
SpinBox1: TSpinBox;
procedure FormCreate( Sender : TObject );
procedure SpinBox1Change(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form13: TForm13;
implementation
{$R *.fmx}
procedure TForm13.FormCreate( Sender : TObject );
begin
Memo1.Font.Size := SpinBox1.Value;
end;
procedure TForm13.SpinBox1Change(Sender: TObject);
begin
Memo1.Font.Size := SpinBox1.Value;
end;
end.
working with FMX is certainly not like the VCL.
The TMemo as many other controls has a property StyledSettings which has a few subfields, Family, Size, Style, FontColor and Other.
All except Other are True by default, so the control would automatically follow any used style setting. Set StyledSettings.Size = False in the Object Inspector and the TMemo will follow your own font size settings.
As I don't have any older XE-version than XE7, the above is tested with XE7. If XE5 doesn't have the corresponding settings, I will remove this answer as it doesn't answer your question.
Alternatively, in code you can write:
with Memo1 do
StyledSettings := StyledSettings - [TStyledSetting.ssSize];
This should work in XE5.

how do I intercept was pressed the F1 key on my main window?

How to do that when I'm in the main window of my system by pressing F1 will display the help muetre in pdf format. how do I intercept was pressed the F1 key on my main window?
I use Delphi XE2
Thanks for the help!
Use the Application.OnHelpCommand event, which you can either assign in code:
interface
type
TForm1 = class(TForm)
// IDE generated code
private
procedure AppOnHelp(Command: Word; Data: Integer;
var CallHelp: Boolean);
end;
implementation
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnHelp := AppOnHelp;
end;
Or assign by using a TApplicationEvents component and creating a handler for the OnHelp event in the Object Inspector's Events tab.
You can set CallHelp to false to prevent the normal help processing, and launch your own help file via ShellExecute.
procedure TForm1.AppOnHelp(Command: Word; Data: Integer;
var CallHelp: Boolean);
begin
CallHelp := False;
// Launch your own help here
end;

Any method to cast a Handle?

Good day,
I have different handles from different non VLC Objects like (SysListView32, ToolbarWin32) and I am wondering if there is any method to cast these handles (HWND).
For example, I got the start button(which is in the left bottom of desktop) handle. Then I found the class name "Button".
I would like to cast him and retrieve from him the caption property "start". For example:
type
TButtonStartMenuFictiveClass = class(TButton)
public
Text: string;
end;
if classname = 'button' then
begin
ShowMessage((objecthandle as TButtonStartMenuFictiveClass).Text);
end;
I am looking to hook all of the objects and to display the text of them. Like the narrator from Windows.
In some cases, you can instantiate a VCL object and assign the external HWND to its WindowHandle property, eg:
var
S: String;
with TButton.Create(nil) do
try
WindowHandle := TheButtonWnd;
try
S := Caption;
finally
WindowHandle := 0; // important
end;
finally
Free;
end;
As I explained in your previous question, you need to use the Windows API to gain access to the properties of a foreign window-control. You can't simply cast a window-handle to an object. They're not pointers to Delphi objects.
The example I linked you too not only shows the classname of the control you're hovering over, but also the caption (text) of the control. This function will also do the trick:
function GetWndText(const Handle: Hwnd): string;
var
Len: Integer;
begin
Len := GetWindowTextLength(Handle) + 1;
SetLength(Result, Len);
GetWindowText(Handle, PChar(Result), Len);
end;

Delphi XE and Trapping Arrow Key with OnKeyDown

I want my form to handle the arrow keys, and I can do it -- as long as there is no button on the form. Why is this?
Key messages are processed by the controls themselves who receives these messages, that's why when you're on a button the form is not receiving the message. So normally you would have to subclass these controls, but the VCL is kind enough to ask the parenting form what to do if the form is interested:
type
TForm1 = class(TForm)
..
private
procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY;
..
procedure TForm1.DialogKey(var Msg: TWMKey);
begin
if not (Msg.CharCode in [VK_DOWN, VK_UP, VK_RIGHT, VK_LEFT]) then
inherited;
end;
François editing: to answer the OP original question, you need to call onKeyDown somehow so that his event code would work (feel free to edit; was too long for a comment).
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Private declarations }
procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.DialogKey(var Msg: TWMKey);
begin
case Msg.CharCode of
VK_DOWN, VK_UP, VK_RIGHT, VK_LEFT:
if Assigned(onKeyDown) then
onKeyDown(Self, Msg.CharCode, KeyDataToShiftState(Msg.KeyData));
else
inherited
end;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
case Key of
VK_DOWN: Top := Top + 5;
VK_UP: Top := Top - 5;
VK_LEFT: Left := Left - 5;
VK_RIGHT: Left := Left + 5;
end;
end;
Arrow keys are used to navigate between buttons on a form. This is standard Windows behaviour. Although you can disable this standard behaviour you should think twice before going against the platform standard. Arrow keys are meant for navigation.
If you want to get the full low down on how a key press finds its way through the message loop I recommend reading A Key's Odyssey. If you want to intercept the key press before it becomes a navigation key, you need to do so in IsKeyMsg or earlier. For example, Sertac's answer gives one such possibility.
Only the object that has the focus can receive a keyboard event.
To let the form have access to the arrow keys event,
declare a MsgHandler in the public part of the form.
In the form create constructor, assign the Application.OnMessage to this MsgHandler.
The code below intercepts the arrow keys only if they are coming from a TButton descendant. More controls can be added as needed.
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnMessage := Self.MsgHandler;
end;
procedure TForm1.MsgHandler(var Msg: TMsg; var Handled: Boolean);
var
ActiveControl: TWinControl;
key : word;
begin
if (Msg.message = WM_KEYDOWN) then
begin
ActiveControl := Screen.ActiveControl;
// if the active control inherits from TButton, intercept the key.
// add other controls as fit your needs
if not ActiveControl.InheritsFrom(TButton)
then Exit;
key := Msg.wParam;
Handled := true;
case Key of // intercept the wanted keys
VK_DOWN : ; // doStuff
VK_UP : ; // doStuff
VK_LEFT : ; // doStuff
VK_RIGHT : ; // doStuff
else Handled := false;
end;
end;
end;
Because they are preempted to deal with setting the focus on the next available WinControl.
(I'm pretty sure that if you put an Edit instead of a Button you see the same thing).
If you want to handle them yourself, you can provide the Application with an OnMessage event that will filter those before they are processed and handle them yourself there.
var
KBHook: HHook; {this intercepts keyboard input}
implementation
{$R *.dfm}
function KeyboardHookProc(Code: Integer; WordParam: Word; LongParam: LongInt): LongInt; stdcall;
begin
case WordParam of
vk_Space: ShowMessage ('space') ;
vk_Right:ShowMessage ('rgt') ;
vk_Left:ShowMessage ('lft') ;
vk_Up: ShowMessage ('up') ;
vk_Down: ShowMessage ('down') ;
end; {case}
end;
procedure TForm4.FormCreate(Sender: TObject);
begin
KBHook:=SetWindowsHookEx(WH_KEYBOARD,#KeyboardHookProc,HInstance,GetCurrentThreadId());
end;
This code will work even when a control is focused (buttons , listboxes), so be careful some controls may loose their keyboard events (Read David haffernans answer) .
keyboard events with Focused controls
eg: If you are having textbox in your app and want to recive text(if focused) also , then
add an applicationevent1
procedure TForm4.ApplicationEvents1Message(var Msg: tagMSG;var Handled: Boolean);
begin
if Msg.message = WM_KEYFIRST then
KBHook:=SetWindowsHookEx(WH_KEYBOARD,#KeyboardHookProc,HInstance,GetCurrentThreadId());
end;
add the following code at the bottom of the function KeyboardHookProc
UnhookWindowsHookEx(KBHook);
and remove
KBHook:=SetWindowsHookEx(WH_KEYBOARD,#KeyboardHookProc, HInstance,
GetCurrentThreadId());
from oncreate event.

Delphi: Is system menu opened?

I Delphi, I need a function which determinates if the system menu (resp. window menu, the menu that appears when the icon is clicked) is opened. The reason is that I am writing a anti-keylogger functionality which sends garbage to the current active editcontrol (this also prevents keylogger which read WinAPI messages to read the content). But if system-menu is opened, the editcontrol STILL has the focus, so the garbage will invoke shortcuts.
If I use message WM_INITMENUPOPUP in my TForm1, I can determinate when the system menu opens, but I wish that I do not have to change the TForm, since I want to write a non visual component, which does not need any modifications at the TForm-derivate-class itself.
//I do not want that solution since I have to modify TForm1 for that!
procedure TForm1.WMInitMenuPopup(var Message: TWMInitMenuPopup);
begin
if message.MenuPopup=getsystemmenu(Handle, False) then
begin
SystemMenuIsOpened := true;
end;
end;
TApplicaton.HookMainWindow() does not send the WM_INITMENUPOPUP to my hook function.
function TForm1.MessageHook(var Msg: TMessage): Boolean;
begin
Result := False;
if (Msg.Msg = WM_INITMENUPOPUP) then
begin
// Msg.Msg IS NEVER WM_INITMENUPOPUP!
if LongBool(msg.LParamHi) then
begin
SystemMenuIsOpened := true;
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.HookMainWindow(MessageHook);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Application.UnhookMainWindow(MessageHook);
end;
Even after very long research I did not found any information about how to query if the system-menu is opened or not. I do not find any way to determinate the opening+closing of that menu.
Has someone a solution for me please?
Regards
Daniel Marschall
Application.HookMainWindow doesn't do what you seem to think. It hooks the hidden application window, not the main form. To intercept WM_INITMENUPOPUP on a specific form, all you need to do is write a handler for it, as you have seen.
To do this generically for any owner form of a component, you could assign WindowProc property of the form to place the hook:
unit FormHook;
interface
uses
Windows, Classes, SysUtils, Messages, Controls, Forms;
type
TFormMessageEvent = procedure(var Message: TMessage; var Handled: Boolean) of object;
TFormHook = class(TComponent)
private
FForm: TCustomForm;
FFormWindowProc: TWndMethod;
FOnFormMessage: TFormMessageEvent;
protected
procedure FormWindowProc(var Message: TMessage); virtual;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnFormMessage: TFormMessageEvent read FOnFormMessage write FOnFormMessage;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Test', [TFormHook]);
end;
procedure TFormHook.FormWindowProc(var Message: TMessage);
var
Handled: Boolean;
begin
if Assigned(FFormWindowProc) then
begin
Handled := False;
if Assigned(FOnFormMessage) then
FOnFormMessage(Message, Handled);
if not Handled then
FFormWindowProc(Message);
end;
end;
constructor TFormHook.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FFormWindowProc := nil;
FForm := nil;
while Assigned(AOwner) do
begin
if AOwner is TCustomForm then
begin
FForm := TCustomForm(AOwner);
FFormWindowProc := FForm.WindowProc;
FForm.WindowProc := FormWindowProc;
Break;
end;
AOwner := AOwner.Owner;
end;
end;
destructor TFormHook.Destroy;
begin
if Assigned(FForm) and Assigned(FFormWindowProc) then
begin
FForm.WindowProc := FFormWindowProc;
FFormWindowProc := nil;
FForm := nil;
end;
inherited Destroy;
end;
end.
You could then use this component on a form:
procedure TForm1.FormHook1FormMessage(var Message: TMessage; var Handled: Boolean);
begin
case Message.Msg of
WM_INITMENUPOPUP:
...
end;
end;
The problem might be that if the form has any other components which do the same thing then you need to make sure that unhooking happens in reverse order (last hooked, first unhooked). The above example hooks in the constructor and unhooks in the destructor; this seems to work even with multiple instances on the same form.
If you don't want any modifications to TForm-derivate-class, why don't try pure Windows API way to implement your current solution, that is, use SetWindowLongPtr() to intercept the WM_INITMENUPOPUP message. Delphi VCL style to intercept messages is just a wrapper of this Windows API function actually.
For that purpose, use SetWindowLongPtr() to set a new address for the window procedure and to get the original address of the window procedure, both at one blow. Remember to store the original address in a LONG_PTR variable. In 32-bit Delphi, LONG_PTR was Longint; supposing 64-bit Delphi will have been released in the future, LONG_PTR should be Int64; you can use $IFDEF directive to distinguish them as follows:
Type
{$IFDEF WIN32}
PtrInt = Longint;
{$ELSE}
PtrInt = Int64;
{$ENDIF}
LONG_PTR = PtrInt;
The value for nIndex parameter to be used for this purpose is GWLP_WNDPROC. Also, pass the new address for the window procedure to dwNewLong parameter, e.g. LONG_PTR(NewWndProc). The NewWndProc is a WindowProc Callback Function that processes messages, it is where your put your intercept criteria and override the default handling of the message you are going to intercept. The callback function can be any name, but the parameters must follow the WindowProc convention.
Note that you must call CallWindowProc() to pass any messages not processed by the new window procedure to the original window procedure.
Finally, you should call SetWindowLongPtr() again somewhere in your code to set the address of modified/new window procedure handler back to the original address. The original address has been saved before as mentioned above.
There was a Delphi code example here. It used SetWindowLong(), but now Microsoft recommends to use SetWindowLongPtr() instead to make it compatible with both 32-bit and 64-bit versions of Windows.
SetWindowLongPtr() didn't exist in Windows.pas of Delphi prior to Delphi 2009. If you use an older version of Delphi, you must declare it by yourself, or use JwaWinUser unit of JEDI API Library.
Not tried this myself, but give this a shot:
Use GetMenuItemRect to get the rect for item 0 of the menu returned by GetSystemMenu.
I (assume!) GetMenuItemRect should return 0 if the system menu is not open (because system could not know the rect of the menu item unless it is open?) If the result is non-zero, check if the coords returned are possible for the given screen resolution.
If you have the time, you could look into AutoHotKey's source code to see how to monitor when system menu is open/closed.

Resources