Numery linii

Adam Boduch

Zapewne w wielu edytorach widziałeś takie coś: na
dole napisane jest, w której linii znajduje się kursor. My pokażemy
Wam dodatkowo jak przechodzić do dowolnej linii w Memo lub RichEdit i wstawiać tekst właśnie w miejsce ustawionego kursora.

Najpierw funkcja pobierająca numer linii oraz znaku w Memo.

function TMainForm.GetCursor: TPoint;
begin
{
  Jako rezultat Y zostaje przypisana wartość płynąca z komunikatu
  EM_LINEFROMACHER. Komunikat podaje numer linii, w której znajduje
  się kursor. Jeżeli parametr wParam to $FFFF to podaje numer linii, w której
  umieszczony jest kursor.
}
  Result.Y := SendMessage(Memo.Handle, EM_LINEFROMCHAR, -1, 0);
{
  Poniższa wartość niesie ze sobą informacje o numerze znaku w linii.

  Komunikat EM_LINEINDEX to informacja o pozycji pierwszego znaku w linii określonej
  poprzez parametr wParam. Jeżeli wParam to $FFFF zwraca numer pierwszego znaku
  w linii, w której znajduje się kursor.

  Jeżeli od wartości SelStart komponentu Memo odejmiemy rezultat wykonania
  komunikatu to otrzymamy numer znaku, w którym znajduje się kursor.
}
  Result.X := Memo.SelStart - SendMessage(Memo.Handle, EM_LINEINDEX, -1, 0);
{
  Obie wartości trzeba podnieść o jeden gdyż w Delphi pierwsza linia będzie
  oznaczona jako 0, a my tego nie chcemy :)
}
  Inc(Result.X);
  Inc(Result.Y);
end;

Funkcja ta jako rezultat zwraca zmienną typu TPoint, która przechowuje współrzędne X i Y które oznaczają pozycję w Memo. Właściwie większość ważnych rzeczy została już opisane w komentarzach.

Teraz procedura ustawiająca kursor- trochę prostsza:

procedure TMainForm.SetCursor(Row, Column: Integer);
begin
{
  Pierwsze polecenie ustawia kursor w linii określonej parametrem Row.
  Drugie polecenie dodaje do tego wartość parametru Column, które oznacza
  numer znaku w linii.
}
  Memo.SelStart := SendMessage(Memo.Handle, EM_LINEINDEX, -1, 0);
  Memo.SelStart := SendMessage(Memo.Handle, EM_LINEINDEX, -1, 0) + Column;
end;

Dwie główne procedury już mamy - teraz pozostaje je tylko
wykorzystać. Informacje dotyczące pozycji kursora będą wyświetlane
w komponencie typu TPanel:

(****************************************************************)
(*                                                              *)
(*       Copyright (C) 2001 by Service for programmers          *)
(*            Build:  13.04.2001  -  Adam Boduch                *)
(*             HTTP://WWW.PROGRAMOWANIE.OF.PL                   *)
(*      E - mail:  boduch@poland.com                            *)
(*                                                              *)
(****************************************************************)

unit MainFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TMainForm = class(TForm)
    Memo: TMemo;
    Panel: TPanel;
    btnSetCursor: TButton;
    edtLine: TEdit;
    edtChar: TEdit;
    procedure MemoKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure btnSetCursorClick(Sender: TObject);
  private
    procedure SetCursor(Row, Column : LongInt);
    function GetCursor : TPoint;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.DFM}


function TMainForm.GetCursor: TPoint;
begin
{
  Jako rezultad Y zostaje przypisana wartosc plynaca z komunikatu
  EM_LINEFROMACHER. Komunikat podaje numer linii, w ktorej znajduje
  sie kursor. Jezeli parametr wParam to $FFFF to podaje numer linii, w ktorej
  umieszczony jest kursor.
}
  Result.Y := SendMessage(Memo.Handle, EM_LINEFROMCHAR, -1, 0);
{
  Ponizsza wartosc niesie ze soba informacje o numerze znaku w linii.

  Komunikat EM_LINEINDEX to informacja o pozycji pierwszego znaku w linii okreslonej
  poprzez parametr wParam. Jezeli wParam to $FFFF zwraca numer pierwszego znaku
  w linii, w ktorej znajduje sie kursor.

  Jezeli od wartosci SelStart komponentu Memo odejmiemy rezultad wykonania
  komunikatu to otrzymamy numer znaku, w ktorym znajduje sie kursor.
}
  Result.X := Memo.SelStart - SendMessage(Memo.Handle, EM_LINEINDEX, -1, 0);
{
  Obie wartosci trzeba podniesc o jeden gdyz w Delphi pierwsza linia bedzie
  oznaczona jako 0, a my tego nie chcemy :)
}
  Inc(Result.X);
  Inc(Result.Y);
end;

procedure TMainForm.SetCursor(Row, Column: Integer);
begin
{
  Pierwsze polecenie ustawia kursor w linii okreslonej parametrem Row.
  Drugie polecenie dodaje do tego wartosc parametru Column, ktore oznacza
  numer znaku w linii.
}
  Memo.SelStart := SendMessage(Memo.Handle, EM_LINEINDEX, -1, 0);
  Memo.SelStart := SendMessage(Memo.Handle, EM_LINEINDEX, -1, 0) + Column;
end;

procedure TMainForm.MemoKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
{ Jezeli uzytkownik nacisnie klawisz to wywolana zostanie procedura, a
  informacja o numerze linii i znaku pojawi sie na Panelu. }
  Panel.Caption := Format('Linia %d, znak: %d', [GetCursor.Y, GetCursor.X]);
end;

procedure TMainForm.btnSetCursorClick(Sender: TObject);
begin
{
  Po nacisnieciu przycisku pobierane zostaja dane z komponentow tekstowych
  i na podst. tych danych w komponencie Memo ustawiany zostaje kursor, a
  w miejsce kursora dodany zostaje tekst.
}
  SetCursor(StrToInt(edtLine.Text), StrToInt(edtChar.Text));
  Memo.SelText := 'www.4programmers.net';
end;

end.

4 komentarzy

Do (|) i tyle :P
Zamiast:

Memo.Perform(EM_LINEFROMCHAR, $FFFF, 0)

Powinno być:

SendMessage(Memo.Handle, EM_LINEFROMCHAR, -1, 0)

I wtedy działa OK

a dlaczego nie piszesz nic o WordWrap? Gdy WordWrap = True, to zwracany jest numer linii i kolumny z ekranu (zawinięcie wiersza zwiększa ilość linii).
Stawiam browar dla tego kto wie jak wyciągnąć prawdziwy numer linii (w miarę szybko, bez skanowania wszystkich znaków EOL).

"rezultad", "Komunikat podaje numer linii, w ktorej znajduje
sie kursor. Jezeli parametr wParam to $FFFF to podaje numer linii, w ktorej
umieszczony jest kursor."
i to Ty Adamie? :P

wstawiłem i mi wyskoczyło duuuuużo błedów