Recent

Author Topic: TMemo, caret position and selection  (Read 4921 times)

RobA

  • New Member
  • *
  • Posts: 14
TMemo, caret position and selection
« on: November 29, 2021, 07:31:59 pm »
Hi all,

I'm using Lazarus 2.0.12 on Windows 10.

I have a TMemo on a form, and want to track the caret position in the format row nn : col nn.

I'm handling OnChnage, OnKeyDown, OnKeyUp and also mouse events, calling a procedure that updates a TStatusBar with the Memo's caret position from each event handler:

Code: Pascal  [Select][+][-]
  1. StatusBar1.Panels[0].Text:= 'Row '+ IntToStr(TheMemo.CaretPos.y+1) + ' : Col '+IntToStr(TheMemo.caretPos.x+1);

This works, in that moving the caret around with the cursor keys, typing text, or clicking the mouse gives the correct caret position. However, things go a bit awry when I hold down shift to select some text. Suppose I have the following two lines of text in the Memo:

Code: Pascal  [Select][+][-]
  1. abcdefghijklmnopqrstuvwxyz
  2. abcdefghijklmnopqrstuvwxyz

If I move the caret to the middle of a line, hold down shift and move the cursor to the right, the caret position reflects the end of the selection. If I hold down shift and move to the left, the caret position doesn't change, even though there is a blinking caret at the left end of the selection. If I position the caret in the second line (say, immediately before the letter p, then hold down shift and move up a line, such that the selection starts with "p" in the first line and ends with "o" in the second line, the caret position is reported as Row 2 : Col 44.

I'm guessing I could probably work around that somehow with SelStart and SelLength, but SelStart returns a character index into the text of the Memo. How can I convert that back to a line number? I don't see any equivalent of EM_LINEFROMCHAR?

I'm probably overlooking something really obvious here, but any suggestions would be very much appreciated.

TIA

Rob.

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: TMemo, caret position and selection
« Reply #1 on: November 29, 2021, 08:45:56 pm »
You will not get a line for line representation using a memo in windows.

if any line happens to wrap around then all lines after that are move down in count..

Windows treats the memo as one long single string with the line endings in the middle of it, so to get real line numbers you need to use window memo control messages to obtain that or use the lines property which is a stringlist but that of course does not follow the 1:1 line number between screen and stringlist

The only true wisdom is knowing you know nothing

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: TMemo, caret position and selection
« Reply #2 on: November 29, 2021, 10:35:19 pm »
Hi!

You have to use
Code: Pascal  [Select][+][-]
  1.  
  2. Memo1.caretpos

which returns  a point that gives you the line (y) and the row (x)

Winni

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: TMemo, caret position and selection
« Reply #3 on: November 29, 2021, 11:52:59 pm »
You have to use
Code: Pascal  [Select][+][-]
  1.  
  2. Memo1.caretpos

which returns  a point that gives you the line (y) and the row (x)

Noting that in this case "point" is defined in terms of characters/lines rather than pixels.

https://lazarus-ccr.sourceforge.io/docs/lcl/stdctrls/tcustomedit.caretpos.html

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

RobA

  • New Member
  • *
  • Posts: 14
Re: TMemo, caret position and selection
« Reply #4 on: December 03, 2021, 05:47:31 pm »
Hi all,

Many thanks for your suggestions. As I mentioned in the original post, I was already using CaretPos, and found some strange values with selected text (nothing's ever straight forward, is it? :) )

I finally came up with the following procedure, called from event handlers for OnChnage, OnKeyDown, OnKeyUp and also mouse events. TheMemo is the TMemo in question, and SelBegin is a global variable that gets the caret position when a selection is first begun.

Code: Pascal  [Select][+][-]
  1.      LineIndex := SendMessage(TheMemo.Handle,EM_LINEINDEX,-1,0);
  2.      if TheMemo.SelLength = 0 then
  3.      begin
  4.         Col := TheMemo.SelStart - LineIndex;
  5.         SelBegin := TheMemo.SelStart;
  6.         Row := SendMessage(TheMemo.Handle,EM_LINEFROMCHAR,TheMemo.SelStart,0);
  7.      end
  8.      else
  9.      begin
  10.        if TheMemo.SelStart < SelBegin then
  11.           Col := TheMemo.SelStart - LineIndex
  12.        else
  13.            Col := (TheMemo.SelStart + TheMemo.SelLength) - LineIndex;
  14.        Row := SendMessage(TheMemo.Handle,EM_LINEFROMCHAR,TheMemo.SelStart+TheMemo.SelLength,0);
  15.      end;
  16.      StatusBar1.Panels[0].Text:= 'Row '+ IntToStr(Row + 1) + ' : Col '+IntToStr(Col + 1);

I'm a little rusty on Pascal, so if anyone has any thoughts for improvements, I'd love to hear them.

 

TinyPortal © 2005-2018