13

In Delphi 7's TMemo control, an attempt to do the key combo Ctrl + A to select all does not do anything (doesn't select all). So I've made this procedure:

procedure TForm1.Memo1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  C: String;
begin
  if ssCtrl in Shift then begin
    C:= LowerCase(Char(Key));
    if C = 'a' then begin
      Memo1.SelectAll;
    end;
  end;
end;

Is there a trick so that I don't have to do this procedure? And if not, then does this procedure look OK?

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
  • @RRUZ the memo is probably named `HTML`. – David Heffernan Dec 11 '11 at 19:31
  • 3
    Personally I'd sooner create a component derived from the standard memo and handle the key press there so that you don't need to pollute all your forms with special handling code. – David Heffernan Dec 11 '11 at 19:33
  • 2
    @David: Do you know if a standard Windows edit control in multiline mode disallows the Ctrl+A command, or if there is a problem with the VCL wrapper? (The `TEdit` handles Ctrl+A as one would expect.) – Andreas Rejbrand Dec 11 '11 at 19:35
  • @Andreas Don't know why TMemo differs from TEdit but I'd guess the difference was at the system level rather than the VCL level. VCL is a very thin layer on standard controls. – David Heffernan Dec 11 '11 at 19:39
  • 1
    @Andreas Once I can get to a machine with a compiler I'll try and produce a raw win32 petzold prog and check it out. – David Heffernan Dec 11 '11 at 19:51
  • 1
    It seems to be an OS _issue_ with no documented explanation. Many posts about it are just based on _code this by your own_. – TLama Dec 11 '11 at 19:54
  • @Andreas Yes, bit of websearch reveals lots of people saying "why does CTRL+A stop working when I switch from single line to multi-line?" Clearly the system, but why it does it is anyone's guess. No doubt there is a good reason. – David Heffernan Dec 11 '11 at 19:55
  • @DavidHeffernan not a bad idea to wrap the TMemo into my own class and handle all that myself :D Whether it's a problem with Windows, VCL, or whatever it may be, it would be a good idea to do this anyway, as well as possibly introduce some new custom shortcuts – Jerry Dodge Dec 11 '11 at 20:27
  • 2
    @David: If I am not mistaken, Raymond asks his readers every now and then about things to write about. This would be an interesting topic. – Andreas Rejbrand Dec 11 '11 at 20:56

3 Answers3

29

This is more elegant:

procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = ^A then
  begin
    (Sender as TMemo).SelectAll;
    Key := #0;
  end;
end;
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • 4
    I know I'm going on a bit but could you possibly explain the `^A` for the uninitiated (e.g. me!) – David Heffernan Dec 11 '11 at 19:44
  • 10
    The Ctrl+A keystroke is send as a character with ordinal value 1 (Ctrl+B as 2, Ctrl+C as 3, etc.). Basically I think that this is a remnant from older times. These 'characters' are usually written `^A`, `^B`, etc., and Delphi supports these. You can see them in ASCII tables, like [at Wikipedia](http://en.wikipedia.org/wiki/ASCII). – Andreas Rejbrand Dec 11 '11 at 19:48
  • 2
    Haven't seen that for years, must be from TP days. – Tony Hopkinson Dec 11 '11 at 20:01
  • Any word on compatibility for this method across Delphi versions and/or Windows versions? – Jerry Dodge Dec 11 '11 at 20:52
  • @Jerry: As far as I know, it will work perfectly on all existing Delphi and Windows versions. – Andreas Rejbrand Dec 11 '11 at 20:54
  • Just looked it up in the TP 3 manual. It's there. ^G (BELL) was used to alert for some input for example. – LU RD Dec 11 '11 at 21:38
  • I'm tempted to accept this answer because it is about half the code I posted above with the same result :D But I have a feeling there's some sort of fix with the TEdit control its self so that I don't have to use this procedure at all. – Jerry Dodge Dec 11 '11 at 21:45
  • 5
    @Jerry: The result is not exactly the same. Your code will not handle the annoying 'beep' sound! – Andreas Rejbrand Dec 11 '11 at 21:46
  • 2
    @Jerry - AFAIK there's no fix to this. This is the default behavior of a windows edit control, you can read about expected behavior of an edit control [here](http://msdn.microsoft.com/en-us/library/bb775460%28v=vs.85%29.aspx). – Sertac Akyuz Dec 11 '11 at 22:43
  • Correction (durr) I meant `TMemo` not `TEdit` – Jerry Dodge Dec 12 '11 at 13:30
  • 1
    @All: See also [Hidden features in the Delphi language](http://stackoverflow.com/a/6063483/757830) _(which should be voted reopen)_ and [Delphi ^A syntax: Documented, implied, or undocumented?](http://stackoverflow.com/q/4915941/757830). – NGLN Dec 15 '11 at 04:51
  • @Jerry - A TMemo is still a windows edit control - a one that's multiline. – Sertac Akyuz Jan 30 '14 at 23:41
  • @SertacAkyuz Thanks for the clarification, although this was 2 years ago and I'm well aware of that now :-) – Jerry Dodge Jan 30 '14 at 23:48
  • @Jerry - Yeah.. just that we don't leave the discussion incomplete.. :) – Sertac Akyuz Jan 31 '14 at 00:21
1

While the accepted answer by Andreas Rejbrand is correct, it is not the expected Windows visual behavior. It leaves the cursor position unchanged. Ctrl-A (Select All) should leave the cursor at the bottom of the text and scroll the control so the cursor is in view.

If this is not done, the control exhibits odd behavior. For example, assume there is more text than fits the window, and the window is not scrolled to the bottom. You press Ctrl-A, and all text is Selected. Ctrl-C will now copy all text to the clipboard. Although you can't see it the cursor is now at the bottom of the View, which has not scrolled. If you now press Ctrl-Down the Selected Text becomes just the text in view, then the cursor moves down and window scrolls down one line. The new bottom line is not selected. This makes it look like the Select All only selected the visible text.

The fix is simply to move the caret to the end of text before SelectAll.

procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = ^A then begin
    With Sender as TMemo do begin
      SelStart := Length(Text);
      Perform(EM_SCROLLCARET, 0, 0);
      SelectAll;
    end;
    Key := #0;    //Eat the key to suppress the beep
  end;
end;

Note that 'Eat the key' only works in the OnKeyPress event, not the OnKeyDown or OnKeyUp events.

Guy Gordon
  • 740
  • 6
  • 13
0

I used the previous answer and discussion to create a standalone component which handles the KeyPress event which I use in small test programs.

TSelectMemo = class(TMemo)
protected
  procedure KeyPress(var Key: Char); override;
end;

...

procedure TSelectMemo.KeyPress(var Key: Char);
begin
  inherited;
  if Key = ^A then
    SelectAll;
end;

Another way of adding "select all" behavior to all components on a form is to add an action list to your form with a standard select all action.

nurettin
  • 11,090
  • 5
  • 65
  • 85