Ru-Board.club
← Вернуться в раздел «Прикладное программирование»

» Delphi: Создание простого цветного TMemo или TEdit

Автор: StalkerSoftware
Дата сообщения: 24.09.2009 14:01
ShIvADeSt

Цитата:
Не могу скачать - сервер не отвечает
Да, сервер похоже умер. Буду его подымать.


Цитата:
Так же снимание выделения при потере фокуса теперь зависит от встроенного свойства Мемо - HideSelection

Согласен, действительно есть такое родное свойство и другое тут не нужно.


Цитата:
теперь список слов для подсветки задается в переменной HighlightWords, для массового добавления слов служит процедура
procedure AddHighlightWords(Words:string;Separator:char);
Тоже вариант, хотя ИМНО обработчик событий в данном случае более универсальный вариант.

Я смотрю, ты помимо озвученных тобой изменений в очередной раз поменял внутренности обработчиков WMKillFocus и WMSetFocus, с чем это связанно ?

Добавлено:
ShIvADeSt

Цитата:
Не могу скачать - сервер не отвечает.

Уже работает.

Добавлено:
2 All

Немного обновил код TColorMemo. Убрал свойство KeepSelOnLostFocus.
Вместо него теперь как и должно быть работает стандартное свойство HideSelection.

Скачать TColorMemo можно здесь (10 кб).

Отзывы и сообщения об ошибках оставляем в этой теме.
Автор: delover
Дата сообщения: 24.09.2009 20:55
ShIvADeSt

Цитата:
Ты как всегда жжешь, я ни фига не понял, что ты хотел сказать и к чему это было

Надеюсь, я пропустил из виду появление очень интересных свойств редакторов. В смысле подключаться буду с последним апдейтом.

Кстати на счёт цветного мемо. Ребят, Вы не видели случайно легальные коды RGB, для цвета clWebLightSaphire? Пока что я "притормозил" разработки именно из-за этого. Пока, что плексиглаз набор мне кажется слишком маленьким. Да и в фаст-репорт добавлять пока нечего. (ИМХО). Там надо "ColorOptions" взаимосвязанный с галками экономии тонера на принт. Ну правда, очень сложно мне смотреть идентификатор "string" не жирным шрифтом. По дефолту от TVision этот идент должен быть болд. А приписывать эту особенность редактора в релизе (fr) к "экономии" тонера - беспочвенно. Математика строк в fr особенная, она своя, и содержит свою нагрузку на термин. Это ясно, а вот хеширование отстаёт. Лицензия на fr у меня недействительна, иногда обыдно. Пекатить это легко в ран-тайме (Имхо), но вот наблюдать разницу fr с Turbo Pascal 6.5 (ибн ~pure pascal) не так то это просто. Я бы лучше в memo углубился. Что-то произошло с буфером обмена.

#
Автор: ShIvADeSt
Дата сообщения: 24.09.2009 22:51
StalkerSoftware

Цитата:
Я смотрю, ты помимо озвученных тобой изменений в очередной раз поменял внутренности обработчиков WMKillFocus и WMSetFocus, с чем это связанно ?

При смене фокуса и возврате обратно было видно мерцание выделенных строк. Когда строк выделено более 1 мерцание вообще мерзкое. Пришлось разбираться.

Цитата:
Тоже вариант, хотя ИМНО обработчик событий в данном случае более универсальный вариант.

Ээээ а для чего тут обработчик событий? Здесь же никаких событий не происходит - просто когда надо (в любой момент) добавляем слова процедурой или просто
HighlightWords.Add() // это если по одному слову.

Автор: ShIvADeSt
Дата сообщения: 25.09.2009 02:19
StalkerSoftware
Посмотрел код компонента, насчет волнистой ты обманул Скорее зигзаг получается (так как идет +-1 относительно У. Я думал, там синусоида рисуется, хотя на таких мелких скачках (+- 1) пофигу что рисовать.

ЗЫ. Добавь в код мои обновления для WMSetFocus и WMKillFocus - с ними ИМХО поведение идеальное.
Автор: delover
Дата сообщения: 25.09.2009 10:54
ShIvADeSt
У Вас в программе видел приблизительно такой код (строка 310):
[more="example"][no]
procedure TForm.FormCreate(Sender: TObject);
begin
if (WndSize.X <> 0) and (WndSize.Y <> 0) then
begin
if Screen.WorkAreaWidth < WndSize.X then
Width := Screen.WorkAreaWidth else
Width := WndSize.X;
if Screen.WorkAreaHeight < WndSize.Y then
Height := Screen.WorkAreaHeight else
Height := WndSize.Y;
end;
end;[/no][/more]
А не проще ли использовать компоненты nnStorage? Правда вполне возможно, что тут у Вас получается другая очередь событий (_win_msg)... Но я понимаю, что Вам неверное даже и не интересно думать о спай логах не сейчас и не в будущем. Зигзаг а надо блакнот ++.
Автор: ShIvADeSt
Дата сообщения: 25.09.2009 12:34
delover

Цитата:
У Вас в программе видел приблизительно такой код (строка 310):

Хз я ЭТОТ код не писал. Я писал код только по Мемо. Остальное даже не смотрел
Автор: StalkerSoftware
Дата сообщения: 25.09.2009 13:17
ShIvADeSt

Цитата:
насчет волнистой ты обманул Скорее зигзаг получается

Ну что получилось, то получилось. А так как амплитуда там маленькая, то выглядит вполне себе волнисто.
Если хочешь, можешь сделать ее более волнистой и я включу этот код в компонент.


Цитата:
Добавь в код мои обновления для WMSetFocus и WMKillFocus - с ними ИМХО поведение идеальное.

Сделал.
Вот немного подправленный TColorMemo, версию ему я не менял.
Скачать TColorMemo можно здесь (10 кб).


Добавлено:
2 ShIvADeSt

В соответствии с первоначальными задумками и данной темой
я стал на базе TColorMemo сделать TColorEdit, но что оно не очень оно у меня молучается.
Отрисовка идет не правильная.

К TEdit я добавил три свойства WordWrap, WantReturns, Alignment, что бы
можно было в Edit визуально вводить текст в виде виде нескольких строк
(хотя конечно реально там одна строка). Все эти свойства работают и
отрисовывают текст для стандартного TEdit нормально.
В ряде случаев такой псевдо многострочный Edit удобнее чем Memo.

А потом я добавил в него функциональность ColorMemo.
Само цветовое выделение текста работает вроде бы нормально, но вот отрисовка всего этого глючит.
Вот ссылка на код ColorEdit и небольшую демку к нему.

Как я понимаю, основная проблема тут зарыта в обработчиках WMPaint,
WMPrintClient и в PaintLine, так где идет расчет координат и непосредственная
отрисовка текста. Пробовал их менять, но честно говоря получилось весьма не
очень. Все же получившийся ColorMemo оказалось перенести на Edit гораздо
сложнее, чем я первоначально думал.

Автор: delover
Дата сообщения: 25.09.2009 18:28
ShIvADeSt

Цитата:
я ЭТОТ код не писал. Я писал код только по Мемо.

Ну тогда простите, писали писали, не отнекивайтесь.

Добавлено:
Добавлено:
Нашёл как это делается. Бу завтра. Надеюсь то, что высмотрю можно будет использовать и мне.
Автор: delover
Дата сообщения: 26.09.2009 22:07
http_//cc.codegear.com/Item/24263
ntfszip.dpr
строка 71
// ru-board src
Автор: delover
Дата сообщения: 28.09.2009 15:47
(unit1) Если я правильно понял - бэгграунд в мемках = clBlack?
Автор: ShIvADeSt
Дата сообщения: 29.09.2009 03:12

Цитата:
А потом я добавил в него функциональность ColorMemo.
Само цветовое выделение текста работает вроде бы нормально, но вот отрисовка всего этого глючит.
Вот ссылка на код ColorEdit и небольшую демку к нему.

Вот доработанный код для ColorEdit
[more]

Код:
{****************************************************}
{ }
{ ColorEdit v1.0 }
{ }
{ Copyright (c) 2004 by Gon Perez-Jimenez }
{ http://www.torry.net/authorsmore.php?id=3649 }
{ }
{ Copyright (c) 2009 by ShIvADeSt Moderator Ru-Board }
{ }
{ Copyright (c) 2009 by Stalker SoftWare }
{ }
{****************************************************}

unit ColorEdit;

interface

uses
Windows, Messages, Classes, Graphics, StdCtrls, Controls;

type
TWordType = (wtSeparator, wtSpace, wtNumber, wtText);
TCheckWordEvent = procedure(Sender :TObject; cWord :String; WordType :TWordType; var FontColor, BackgroundColor :TColor; var IsUnderline :Boolean) of object;

TColorEdit = class(TEdit)
private
{ Private declarations }
FStartCaretPos :TSize;
FLineCaretPos :TSize;
FFocusLost :Boolean;
FSeparators :TStrings;
FOnCheckWord :TCheckWordEvent;
FKeepSelOnLostFocus :Boolean;
FAlignment :TAlignment;
FWordWrap :Boolean;
FWantReturns :Boolean;

procedure SetRedraw(Handle :THandle; Flag :Boolean);
procedure CharToCaret(nCharPos :Integer; var nRow, nColumn :Integer);

function TextFromLine(nRow:integer):string;

procedure WMKillFocus(var Message :TWMKillFocus); message WM_KILLFOCUS;
procedure WMSetFocus(var Message :TWMSetFocus); message WM_SETFOCUS;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP;
procedure WMCHAR(var Message: TWMCHAR); message WM_CHAR;
procedure WMPrintClient(var Message: TWMPaint); message WM_PRINTCLIENT;
procedure WMERASEBKGND(var Message: TWMERASEBKGND); message WM_ERASEBKGND;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMMove(var Message: TWMMove); message WM_MOVE;
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;

procedure PaintLine(oCanvas :TCanvas; cLineText :String; nCurLine :Integer; rTxtRect :TRect);
procedure DrawUnderline(oCanvas :TCanvas; nFromX, nFromY, nToX :Integer);

procedure SetSeparators(const Value :TStrings);

function NextWord(var cStr :String) :String;

function GetTextStart(nRow :Integer) :TSize;

procedure SetAlignment(const Value: TAlignment);
procedure SetWordWrap(const Value: Boolean);
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;

protected
{ Protected declarations }
procedure CreateParams(var Params :TCreateParams); override;

public
{ Public declarations }
procedure GotoXY(nCol, nLine :Integer);
function Line() :Integer;
function Col() :Integer;
function TopLine() :Integer;
function VisibleLines() :Integer;
function IsSeparator(cStr :Char) :Boolean;
function IsNumber(cStr :String) :Boolean;

published
{ Published declarations }
constructor Create(AOwner :TComponent); override;
destructor Destroy(); override;

property Separators :TStrings read FSeparators write SetSeparators;
property OnCheckWord :TCheckWordEvent read FOnCheckWord write FOnCheckWord;
property KeepSelOnLostFocus :Boolean read FKeepSelOnLostFocus write FKeepSelOnLostFocus default False;

property WordWrap :Boolean read FWordWrap write SetWordWrap default False;
property WantReturns :Boolean read FWantReturns write FWantReturns default False;
property Alignment :TAlignment read FAlignment write SetAlignment default taLeftJustify;

end; { TColorEdit }

procedure Register;

implementation

constructor TColorEdit.Create(AOwner: TComponent);
begin
inherited;
FSeparators := TStringList.Create;

FSeparators.Add('.');
FSeparators.Add(',');
FSeparators.Add('|');
FSeparators.Add(' ');
FSeparators.Add(';');
FSeparators.Add(':');
FSeparators.Add('"');
FSeparators.Add('''');
FSeparators.Add('^');
FSeparators.Add('+');
FSeparators.Add('-');
FSeparators.Add('*');
FSeparators.Add('/');
FSeparators.Add('\');
FSeparators.Add('`');
FSeparators.Add('~');
FSeparators.Add('[');
FSeparators.Add(']');
FSeparators.Add('(');
FSeparators.Add(')');
FSeparators.Add('{');
FSeparators.Add('}');
FSeparators.Add('?');
FSeparators.Add('!');
FSeparators.Add('%');
FSeparators.Add('=');
FSeparators.Add('<');
FSeparators.Add('>');

FFocusLost := False;
FKeepSelOnLostFocus := False;

FAlignment := taLeftJustify;
FWordWrap := False;
FWantReturns := False;

end; { Create }

destructor TColorEdit.Destroy;
begin
FSeparators.Free;
inherited;
end; { Destroy }

function TColorEdit.TextFromLine(nRow:integer):string;
var
Text: array[0..4095] of Char;
L: Integer;
begin
Word((@Text)^) := SizeOf(Text);
L := SendMessage(Self.Handle, EM_GETLINE, nRow, Longint(@Text));
if (Text[L - 2] = #13) and (Text[L - 1] = #10) then Dec(L, 2);
SetString(Result, Text, L);
end;

procedure TColorEdit.SetSeparators(const Value :TStrings);
begin
FSeparators.Assign(Value);
end; { SetSeparators }

procedure TColorEdit.SetAlignment(const Value :TAlignment);
begin

if FAlignment <> Value then begin
FAlignment := Value;
RecreateWnd;
end; { if }

end; { SetAlignment }

procedure TColorEdit.SetWordWrap(const Value :Boolean);
begin

if Value <> FWordWrap then begin
FWordWrap := Value;
RecreateWnd;
end; { if }

end; { SetWordWrap }

procedure TColorEdit.WMGetDlgCode(var Message :TWMGetDlgCode);
begin

inherited;

if not FWantReturns then
Message.Result := (Message.Result and not DLGC_WANTALLKEYS);

end; { WMGetDlgCode }

procedure TColorEdit.CreateParams(var Params: TCreateParams);
const
Alignments: array[TAlignment] of Dword = (ES_LEFT, ES_RIGHT, ES_CENTER);
WordWraps: array[Boolean] of DWORD = (0, ES_AUTOHSCROLL);

begin

inherited CreateParams(Params);

if FWordWrap then
Params.Style := Params.Style and not WordWraps[FWordWrap] or ES_MULTILINE or
Alignments[FAlignment]
else
Params.Style := Params.Style or Alignments[FAlignment]

end; { CreateParams }

function TColorEdit.IsSeparator(cStr :Char) :Boolean;
begin
Result := (FSeparators.IndexOf(cStr) <> -1);
end; { IsSeparator }

function TColorEdit.NextWord(var cStr :String) :String;
begin

Result := '';

if cStr = '' then Exit;

if IsSeparator(cStr[1]) then begin
Result := Result+cStr[1];
Delete(cStr, 1, 1);
end else
while (cStr <> '') and (not IsSeparator(cStr[1])) do begin
Result := Result+cStr[1];
Delete(cStr, 1, 1);
end; { while }

end; { NextWord }

function TColorEdit.IsNumber(cStr :String) :Boolean;
var
i: Integer;

begin

Result := False;

for i:= 1 to Length(cStr) do
case cStr[i] of
'0'..'9':;
else
Exit;
end; { case }

Result := True;

end; { IsNumber }

function TColorEdit.VisibleLines() :Integer;
begin
Result := (Height div (Abs(Font.Height)+2));
end; { VisibleLines }

procedure TColorEdit.GotoXY(nCol, nLine :Integer);
begin

Dec(nLine);
SelStart := 0;
SelLength := 0;
SelStart := nCol+Perform(EM_LINEINDEX, nLine, 0);
SelLength :=0;

end; { GotoXY }

function TColorEdit.GetTextStart(nRow :Integer) :TSize;
var
ChrInd :Integer;
Res :LResult;
Caret : TPoint;
TxtRect:TRect;
begin

Result.cx := 0;
Result.cy := 0;

if Text = '' then Exit;

ChrInd := SendMessage(Handle, EM_LINEINDEX, nRow, 0);
Res := SendMessage(Handle, EM_POSFROMCHAR, ChrInd, 0);

if Res > 0 then begin
Result.cx := LoWord(Res);
if Result.cx>Self.ClientRect.Right then Result.cx:=Result.cx-65535-FStartCaretPos.cx;
Result.cy := HiWord(Res);
end; { if }
if NOT WordWrap then begin
Windows.GetCaretPos(Caret);
SendMessage(Handle,EM_GETRECT,0,LPARAM(@TxtRect));
Result.cy:= TxtRect.Top;
end;
end; { GetTextStart }

function TColorEdit.TopLine() :Integer;
begin
Result := SendMessage(Handle, EM_GETFIRSTVISIBLELINE, 0, 0);
end; { TopLine }

function TColorEdit.Line() :Integer;
begin
Result := SendMessage(Handle, EM_LINEFROMCHAR, SelStart, 0);
end; { Line }

function TColorEdit.Col() :Integer;
begin
Result := SelStart - SendMessage(Handle, EM_LINEINDEX, SendMessage(Handle, EM_LINEFROMCHAR, SelStart, 0), 0);
end; { Col }

procedure TColorEdit.SetRedraw(Handle :THandle; Flag :Boolean);
begin
SendMessage(Handle, WM_SETREDRAW, Ord(Flag), 0);
end; { SetRedraw }

procedure TColorEdit.WMVScroll(var Message :TWMVScroll);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMVScroll }

procedure TColorEdit.WMKillFocus(var Message :TWMKillFocus);

var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(GetDesktopWindow());
FFocusLost:=True;
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }
end; { WMKillFocus }

procedure TColorEdit.WMSetFocus(var Message :TWMSetFocus);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(GetDesktopWindow());
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMSetFocus }

procedure TColorEdit.WMHScroll(var Message :TWMHScroll);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMHScroll }

procedure TColorEdit.WMSize(var Message :TWMSize);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMSize }

procedure TColorEdit.WMEraseBKGND(var Message :TWMERASEBKGND);
begin
// не удалять данную пустую процедуру, иначе будут глюки с изображением
end; { WMEraseBKGND }

procedure TColorEdit.WMMove(var Message :TWMMove);
begin
Invalidate;
inherited;
end; { WMMove }

procedure TColorEdit.CharToCaret(nCharPos :Integer; var nRow, nColumn :Integer);
begin
nRow := SendMessage(Handle, EM_LINEFROMCHAR, nCharPos, 0)+1;
nColumn := nCharPos - SendMessage(Handle, EM_LINEINDEX, SendMessage(Handle, EM_LINEFROMCHAR, nCharPos, 0), 0);
end; { CharToCaret }

procedure TColorEdit.WMLButtonUp(var Message :TWMLButtonUp);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonUp }

procedure TColorEdit.WMLButtonDown(var Message :TWMLButtonDown);
var
Locked :Boolean;

begin

Locked := False;

if FFocusLost then begin
SetRedraw(Handle, False);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
SetRedraw(Handle, True);
end else
try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonDown }

procedure TColorEdit.WMLButtonDblClk(var Message: TWMLButtonDblClk);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonDblClk }

procedure TColorEdit.WMMouseMove(var Message :TWMMouseMove);
var
Locked :Boolean;

begin

Locked := False;

if (Message.Keys and MK_LBUTTON) = 0 then
inherited
else begin

try

RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);

if not FFocusLost then
Locked := LockWindowUpdate(Handle)
else
FFocusLost := False;

inherited;

finally
if (Locked) and (not FFocusLost) then LockWindowUpdate(0);
end; { try }

end; { if }

end; { WMMouseMove }

procedure TColorEdit.WMMouseWheel(var Message :TWMMouseWheel);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMMousewheel }

procedure TColorEdit.WMCHAR(var Message :TWMCHAR);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMCHAR }

procedure TColorEdit.WMKeyDown(var Message :TWMKeyDown);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMKeyDown }

procedure TColorEdit.WMKeyUp(var Message :TWMKeyUp);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMKeyUp }

procedure TColorEdit.PaintLine(oCanvas :TCanvas; cLineText :String; nCurLine :Integer; rTxtRect :TRect);
const
HilightFont = clHighlightText;
HilightBack = clHighlight;

type
TxtAttr = record
FontColor, BckColor :TColor;
Underline :Boolean;
end; { TxtAttr }

var
i, j, px :Integer;
nLineBeg, nColBeg, nLineEnd, nColEnd :Integer;
LastFont, LastBck :TColor;
LastUnderline :Boolean;
rSize :TSize;
t, cCurWord :String;
aCharsColor :array of TxtAttr;
evFontColor, evBckColor :TColor;
evUnderline :Boolean;

begin

try

CharToCaret(SelStart, nLineBeg, nColBeg);
CharToCaret(SelStart+SelLength, nLineEnd, nColEnd);

with oCanvas do begin

px := rTxtRect.Left;
t := cLineText+' ';
SetLength(aCharsColor, Length(cLineText)+1);

for i := 0 to High(aCharsColor) do begin // Инициализируем массив цветов символов
aCharsColor[i].FontColor := Self.Font.Color;
aCharsColor[i].BckColor := Self.Color;
aCharsColor[i].Underline := False;
end; { for }

i := 0;

repeat // Проходим по всей строке и задаем для каждого символа его цвет и цвет его фона

cCurWord := NextWord(t);

if cCurWord <> '' then begin

if cCurWord = ' ' then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtSpace, evFontColor, evBckColor, evUnderline);

aCharsColor[i].FontColor := evFontColor;
aCharsColor[i].BckColor := evBckColor;
aCharsColor[i].Underline := evUnderline;
Inc(i);

end else
if IsSeparator(cCurWord[1]) then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtSeparator, evFontColor, evBckColor, evUnderline);

aCharsColor[i].FontColor := evFontColor;
aCharsColor[i].BckColor := evBckColor;
aCharsColor[i].Underline := evUnderline;
Inc(i);

end else
if IsNumber(cCurWord) then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtNumber, evFontColor, evBckColor, evUnderline);

for j := 1 to Length(cCurWord) do begin
aCharsColor[i+j-1].FontColor := evFontColor;
aCharsColor[i+j-1].BckColor := evBckColor;
aCharsColor[i+j-1].Underline := evUnderline;
end; { for }

Inc(i, Length(cCurWord));

end else begin // Задаем цвет остального текста

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtText, evFontColor, evBckColor, evUnderline);

for j:=1 to Length(cCurWord) do begin
aCharsColor[i+j-1].FontColor := evFontColor;
aCharsColor[i+j-1].BckColor := evBckColor;
aCharsColor[i+j-1].Underline := evUnderline;
end; { for }

Inc(i, Length(cCurWord));

end; { if }

end; { if }

until cCurWord = '';

if (Focused) or (FKeepSelOnLostFocus and not Focused) then begin // это если надо чтобы при потере фокуса исчезало выделение

if (nCurLine = nLineBeg) and (nColBeg <> nColEnd) and (nLineBeg = nLineEnd) then
for i := nColBeg+1 to nColEnd do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine > nLineBeg) and (nCurLine < nLineEnd) then
for i := 1 to Length(cLineText) do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine = nLineBeg) and (nLineBeg < nLineEnd) then
for i := nColBeg+1 to Length(cLineText) do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine = nLineEnd) and (nLineBeg < nLineEnd) then
for i := 1 to nColEnd do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

end; { if }

cCurWord := cLineText[1];
LastFont := aCharsColor[0].FontColor;
LastBck := aCharsColor[0].BckColor;
LastUnderline := aCharsColor[0].Underline;

if Length(cLineText) = 1 then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy));
end; { if }

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

end; { if }

for i := 2 to Length(cLineText) do begin

t := cLineText[i];

if (LastFont <> aCharsColor[i-1].FontColor) or
(LastBck <> aCharsColor[i-1].BckColor) or
(LastUnderline <> aCharsColor[i-1].Underline) then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy))
end; { if }

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

if aCharsColor[i-2].Underline then
DrawUnderline(oCanvas, px, rTxtRect.Top+rSize.cy, px+rSize.cx);

Inc(px, rSize.cx);

cCurWord := '';
LastFont := aCharsColor[i-1].FontColor;
LastBck := aCharsColor[i-1].BckColor;
LastUnderline := aCharsColor[i-1].Underline;

end; { if }

cCurWord := cCurWord+cLineText[i];

if px > rTxtRect.Right then Break;

if i = Length(cLineText) then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy))

end; { if }

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

if aCharsColor[i-1].Underline then
DrawUnderline(oCanvas, px, rTxtRect.Top+rSize.cy, px+rSize.cx);

end; { if }

end; { for }

end; { with }

finally
SetLength(aCharsColor, 0);
end; { try }

end; { PaintLine }

procedure TColorEdit.WMPaint(var Message :TWMPaint);
var
PS :TPaintStruct;
psRect :TRect;
DC :HDC;
hbmNew :HBITMAP;
hbmOld :HBITMAP;
oCanvas :TCanvas;
i :Integer;
rSize :TSize;
nMax :Integer;
nLineFirst :Integer;
nLineLast :Integer;
cLine :String;

begin
if (FStartCaretPos.cx = 0) and (Text <> '') and (Alignment = taLeftJustify) then
FStartCaretPos := GetTextStart(0);

BeginPaint(Handle, PS);

psRect := Self.ClientRect;
DC := CreateCompatibleDC(ps.hdc);
hbmNew := CreateCompatibleBitmap(PS.hdc, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);

oCanvas := TCanvas.Create;
try

oCanvas.Handle := DC;
oCanvas.Font := Self.Font;

with oCanvas do begin

if WordWrap then begin
nMax := TopLine()+VisibleLines();
if nMax > SendMessage(Handle, EM_GETLINECOUNT,0,0) then nMax := SendMessage(Handle, EM_GETLINECOUNT,0,0);
end
else
nMax := 0;

Brush.Color := Self.Color;
FillRect(Self.ClientRect);

rSize := TextExtent(' ');

if GetForegroundWindow() = Self.Parent.Handle then begin
nLineFirst := TopLine() + (PS.rcPaint.Top div rSize.cy);
nLineLast := TopLine() + (PS.rcPaint.Bottom div rSize.cy);
end else begin
nLineFirst := TopLine();
nLineLast := nMax;
end; { if }

for i := nLineFirst to nLineLast do begin

FLineCaretPos := GetTextStart(i);
cLine := TextFromLine(i); //Lines[i];
if cLine = '' then cLine :=' ';
rSize := TextExtent(cLine);

if FLineCaretPos.cy+rSize.cy <= psRect.Bottom then
PaintLine(oCanvas, cLine, i+1, Rect(FLineCaretPos.cx, FLineCaretPos.cy, Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy));

end; { for }

end; { with }

finally
BitBlt(PS.hdc, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
EndPaint(Handle, PS);
oCanvas.Free;
end; { try }

end; { WMPaint }

procedure TColorEdit.WMPrintClient(var Message :TWMPaint);
var
PS :TPaintStruct;
psRect :TRect;
DC :HDC;
hbmNew :HBITMAP;
hbmOld :HBITMAP;
oCanvas :TCanvas;
i :Integer;
rSize :TSize;
nMax :Integer;
nLineFirst :Integer;
nLineLast :Integer;
cLine :String;

begin

BeginPaint(Handle, PS);

psRect := Self.ClientRect;
DC := CreateCompatibleDC(ps.hdc);
hbmNew := CreateCompatibleBitmap(PS.hdc, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);

oCanvas := TCanvas.Create;
try

oCanvas.Handle := DC;
oCanvas.Font := Self.Font;

with oCanvas do begin

if WordWrap then begin
nMax := TopLine()+VisibleLines();
if nMax > SendMessage(Handle, EM_GETLINECOUNT,0,0) then nMax := SendMessage(Handle, EM_GETLINECOUNT,0,0);
end
else
nMax := 0;

Brush.Color := Self.Color;
FillRect(Self.ClientRect);
rSize := TextExtent(' ');

if GetForegroundWindow=Self.Parent.Handle then begin
nLineFirst := TopLine() + (PS.rcPaint.Top div rSize.cy);
nLineLast := TopLine() + (PS.rcPaint.Bottom div rSize.cy);
end else begin
nLineFirst := TopLine();
nLineLast := nMax;
end; { if }

for i := nLineFirst to nLineLast do begin

FLineCaretPos := GetTextStart(i);
cLine := TextFromLine(i); //Lines[i];
if cLine = '' then cLine := ' ';
rSize := TextExtent(cLine);

if FLineCaretPos.cy+rSize.cy <= psRect.Bottom then
PaintLine(oCanvas, cLine, i+1, Rect(FLineCaretPos.cx, FLineCaretPos.cy,Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy));

end; { for }

end; { with }

finally
BitBlt(PS.hdc, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
EndPaint(Handle, PS);
oCanvas.Free;
end; { try }

end; { WMPrintClient }

procedure TColorEdit.DrawUnderline(oCanvas :TCanvas; nFromX, nFromY, nToX: Integer);
var
X :Integer;

begin

with oCanvas do
for X := nFromX to nToX do
oCanvas.Pixels[X, nFromY+Round(Sin(X))] := clRed;

end; { DrawUnderline }

procedure Register;
begin

RegisterComponents('Samples', [TColorEdit]);

end; { Register }

end.
Автор: delover
Дата сообщения: 29.09.2009 08:46
ShIvADeSt
У меня под d2006 выявилась можно сказать бага. После запуска проги встроенные в дельфу мемки на редакторе инспектора стринглиста стали копировать текст вместе со стилем из микрософт ворда или оутлука. ))) Кстати очень забавно, возможно ричь плаинт не вырубился.
Автор: StalkerSoftware
Дата сообщения: 29.09.2009 20:11
ShIvADeSt

Цитата:
Вот доработанный код для ColorEdit


1) Если WordWrap = False, то при выделении текста, получается черные буквы на темно-синем фоне (в общем ничего не видно).

2) У меня такое впечатление, что обработчик WMPrintClient вообще не вызывается для ColorEdit (ставил в его начало Break Point). Все время идет вызов WMPrint.

3) Как то немного странно отрабатывает WMPaint. Включил WordWrap и WantReturns, набираю длинный текст, пробовал и автоперенос и ручной перенос (жал Enter), выделял текст как на одной строке так и на двух. Внешне все работает нормально, но под отладчиком у меня все время nMax равен 0, а nLineFirst все время равен nLineLast.

4) Если не трудно, то разъясни смысл функции TextFromLine.
Особенно меня там смущает массив с именем Text размером 4 кб, а ведь Edit у Edit и так есть свойство Text.


Цитата:
Переделал процедуру DrawLine - теперь более на волну похоже.

Да, волна стала более плавная, но ее амплитуда несколько большая, если бы ее немного уменьшить, то было бы в самый раз.



Автор: ShIvADeSt
Дата сообщения: 30.09.2009 02:52
StalkerSoftware

Цитата:
1) Если WordWrap = False, то при выделении текста, получается черные буквы на темно-синем фоне (в общем ничего не видно).

2) У меня такое впечатление, что обработчик WMPrintClient вообще не вызывается для ColorEdit (ставил в его начало Break Point). Все время идет вызов WMPrint.

3) Как то немного странно отрабатывает WMPaint. Включил WordWrap и WantReturns, набираю длинный текст, пробовал и автоперенос и ручной перенос (жал Enter), выделял текст как на одной строке так и на двух. Внешне все работает нормально, но под отладчиком у меня все время nMax равен 0, а nLineFirst все время равен nLineLast.

4) Если не трудно, то разъясни смысл функции TextFromLine.
Особенно меня там смущает массив с именем Text размером 4 кб, а ведь Edit у Edit и так есть свойство Text.


по 1 вопросу - для сингллайн Едита срабатывает видимо внутренняя отрисовка, ее вообще не знаю как подавить. То есть она срабатывает уже после события отрисовки. При этом цвет берет по странному критерию. В общем не буду разбираться.
2. Может и не вызывается - если мешает удали.
3. Хз у меня вроде все нормально было, да и работает
4. Смысл следует из самого названия Для конкретной строки (надо было вместо Line написать Row) получает текст. Так как в отличие от TMemo у едита нет свойства Strings то мне надо как то определять какой текст для данной строки рисовать. Пришлось добавлять еще один способ.

Добавил в отрисовку след код

Код:
SendMessage(Self.Handle,EM_GETRECT,0,LPARAM(@TxtOffset));
Brush.Color := Self.Color;
FillRect(Rect(0, rTxtRect.Top, TxtOffset.Left, rTxtRect.Top+rSize.cy))

end; { with }


finally
SetLength(aCharsColor, 0);
end; { try }

end; { PaintLine }
Автор: StalkerSoftware
Дата сообщения: 30.09.2009 15:20
ShIvADeSt

Цитата:
по 1 вопросу - для сингллайн Едита срабатывает видимо внутренняя отрисовка, ее вообще не знаю как подавить. То есть она срабатывает уже после события отрисовки. При этом цвет берет по странному критерию. В общем не буду разбираться.
Жаль, без исправления этой ошибки, ColorEdit'ом врядли можно будет пользоваться


Цитата:
2. Может и не вызывается - если мешает удали.
Не то что бы мешает, но если WMPrintClient почему то все равно не вызывается, то зачем держать лишний код ...
Или у тебя он вызывается ?


Цитата:
4. Смысл следует из самого названия
Нет, общий ее смысл я то понял. Просто меня смутила переменная Text, полностью созвучная со свойством Text этого компонента. Ну и не совсем понятно, почему буфер взят равным именно 4 кб ?

Нашел еще одну ошибку в ColorEdit:
Если в него ввести несколько строк текста, а потом вызвать например TM (т.е. что бы демка с ColorEdit потеряла фокус), то в ColorEdit остается видна только первая строка, когда фокус возвращается, то становятся видны все строки.
Автор: ShIvADeSt
Дата сообщения: 01.10.2009 01:12
StalkerSoftware

Цитата:
Нет, общий ее смысл я то понял. Просто меня смутила переменная Text, полностью созвучная со свойством Text этого компонента. Ну и не совсем понятно, почему буфер взят равным именно 4 кб ?

вот переделанный код (заодно пофиксил баг с исчезновением нижних строк - там Handle видимо прыгал от окна родителя к окну контрола). Вроде бы пофиксил выделение в сингллайн едите, работает кривовато - потом может что придумаю.
[more]

Код:
{****************************************************}
{ }
{ ColorEdit v1.0 }
{ }
{ Copyright (c) 2004 by Gon Perez-Jimenez }
{ http://www.torry.net/authorsmore.php?id=3649 }
{ }
{ Copyright (c) 2009 by ShIvADeSt Moderator Ru-Board }
{ }
{ Copyright (c) 2009 by Stalker SoftWare }
{ }
{****************************************************}

unit ColorEdit;

interface

uses
Windows, Messages, Classes, Graphics, StdCtrls, Controls;


type
TWordType = (wtSeparator, wtSpace, wtNumber, wtText);
TCheckWordEvent = procedure(Sender :TObject; cWord :String; WordType :TWordType; var FontColor, BackgroundColor :TColor; var IsUnderline :Boolean) of object;

TColorEdit = class(TEdit)
private
{ Private declarations }
FStartCaretPos :TSize;
FLineCaretPos :TSize;
FFocusLost :Boolean;
FSeparators :TStrings;
FOnCheckWord :TCheckWordEvent;
FKeepSelOnLostFocus :Boolean;
FAlignment :TAlignment;
FWordWrap :Boolean;
FWantReturns :Boolean;

procedure SetRedraw(Handle :THandle; Flag :Boolean);
procedure CharToCaret(nCharPos :Integer; var nRow, nColumn :Integer);

function TextFromLine(nRow:integer):string;

procedure WMKillFocus(var Message :TWMKillFocus); message WM_KILLFOCUS;
procedure WMSetFocus(var Message :TWMSetFocus); message WM_SETFOCUS;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP;
procedure WMCHAR(var Message: TWMCHAR); message WM_CHAR;
procedure WMPrintClient(var Message: TWMPaint); message WM_PRINTCLIENT;
procedure WMERASEBKGND(var Message: TWMERASEBKGND); message WM_ERASEBKGND;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMMove(var Message: TWMMove); message WM_MOVE;
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;

procedure PaintLine(oCanvas :TCanvas; cLineText :String; nCurLine :Integer; rTxtRect :TRect);
procedure DrawUnderline(oCanvas :TCanvas; nFromX, nFromY, nToX :Integer);

procedure SetSeparators(const Value :TStrings);

function NextWord(var cStr :String) :String;

function GetTextStart(nRow :Integer) :TSize;

procedure SetAlignment(const Value: TAlignment);
procedure SetWordWrap(const Value: Boolean);
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;

protected
{ Protected declarations }
procedure CreateParams(var Params :TCreateParams); override;

public
{ Public declarations }
procedure GotoXY(nCol, nLine :Integer);
function Line() :Integer;
function Col() :Integer;
function TopLine() :Integer;
function VisibleLines() :Integer;
function IsSeparator(cStr :Char) :Boolean;
function IsNumber(cStr :String) :Boolean;

published
{ Published declarations }
constructor Create(AOwner :TComponent); override;
destructor Destroy(); override;

property Separators :TStrings read FSeparators write SetSeparators;
property OnCheckWord :TCheckWordEvent read FOnCheckWord write FOnCheckWord;
property KeepSelOnLostFocus :Boolean read FKeepSelOnLostFocus write FKeepSelOnLostFocus default False;

property WordWrap :Boolean read FWordWrap write SetWordWrap default False;
property WantReturns :Boolean read FWantReturns write FWantReturns default False;
property Alignment :TAlignment read FAlignment write SetAlignment default taLeftJustify;

end; { TColorEdit }

procedure Register;

implementation


constructor TColorEdit.Create(AOwner: TComponent);
begin
inherited;
FSeparators := TStringList.Create;
FSeparators.Add('.');
FSeparators.Add(',');
FSeparators.Add('|');
FSeparators.Add(' ');
FSeparators.Add(';');
FSeparators.Add(':');
FSeparators.Add('"');
FSeparators.Add('''');
FSeparators.Add('^');
FSeparators.Add('+');
FSeparators.Add('-');
FSeparators.Add('*');
FSeparators.Add('/');
FSeparators.Add('\');
FSeparators.Add('`');
FSeparators.Add('~');
FSeparators.Add('[');
FSeparators.Add(']');
FSeparators.Add('(');
FSeparators.Add(')');
FSeparators.Add('{');
FSeparators.Add('}');
FSeparators.Add('?');
FSeparators.Add('!');
FSeparators.Add('%');
FSeparators.Add('=');
FSeparators.Add('<');
FSeparators.Add('>');

FFocusLost := False;
FKeepSelOnLostFocus := False;

FAlignment := taLeftJustify;
FWordWrap := False;
FWantReturns := False;

end; { Create }

destructor TColorEdit.Destroy;
begin
FSeparators.Free;
inherited;
end; { Destroy }


function TColorEdit.TextFromLine(nRow:integer):string;
var
EditText: array[0..4095] of Char;
L: Integer;
begin
Word((@EditText)^) := SizeOf(EditText);
L := SendMessage(Self.Handle, EM_GETLINE, nRow, Longint(@EditText));
if (EditText[L - 2] = #13) and (EditText[L - 1] = #10) then Dec(L, 2);
SetString(Result, EditText, L);
end;

procedure TColorEdit.SetSeparators(const Value :TStrings);
begin
FSeparators.Assign(Value);
end; { SetSeparators }

procedure TColorEdit.SetAlignment(const Value :TAlignment);
begin

if FAlignment <> Value then begin
FAlignment := Value;
RecreateWnd;
end; { if }

end; { SetAlignment }

procedure TColorEdit.SetWordWrap(const Value :Boolean);
begin

if Value <> FWordWrap then begin
FWordWrap := Value;
RecreateWnd;
end; { if }
end; { SetWordWrap }

procedure TColorEdit.WMGetDlgCode(var Message :TWMGetDlgCode);
begin

inherited;

if not FWantReturns then
Message.Result := (Message.Result and not DLGC_WANTALLKEYS);

end; { WMGetDlgCode }

procedure TColorEdit.CreateParams(var Params: TCreateParams);
const
Alignments: array[TAlignment] of Dword = (ES_LEFT, ES_RIGHT, ES_CENTER);
WordWraps: array[Boolean] of DWORD = (0, ES_AUTOHSCROLL);

begin

inherited CreateParams(Params);

if FWordWrap then
Params.Style := Params.Style and not WordWraps[FWordWrap] or ES_MULTILINE or
Alignments[FAlignment]
else
Params.Style := Params.Style or Alignments[FAlignment]

end; { CreateParams }

function TColorEdit.IsSeparator(cStr :Char) :Boolean;
begin
Result := (FSeparators.IndexOf(cStr) <> -1);
end; { IsSeparator }

function TColorEdit.NextWord(var cStr :String) :String;
begin

Result := '';

if cStr = '' then Exit;

if IsSeparator(cStr[1]) then begin
Result := Result+cStr[1];
Delete(cStr, 1, 1);
end else
while (cStr <> '') and (not IsSeparator(cStr[1])) do begin
Result := Result+cStr[1];
Delete(cStr, 1, 1);
end; { while }

end; { NextWord }

function TColorEdit.IsNumber(cStr :String) :Boolean;
var
i: Integer;

begin

Result := False;

for i:= 1 to Length(cStr) do
case cStr[i] of
'0'..'9':;
else
Exit;
end; { case }

Result := True;

end; { IsNumber }

function TColorEdit.VisibleLines() :Integer;
begin
Result := (Height div (Abs(Font.Height)+2));
end; { VisibleLines }

procedure TColorEdit.GotoXY(nCol, nLine :Integer);
begin

Dec(nLine);
SelStart := 0;
SelLength := 0;
SelStart := nCol+Perform(EM_LINEINDEX, nLine, 0);
SelLength :=0;

end; { GotoXY }

function TColorEdit.GetTextStart(nRow :Integer) :TSize;
var
ChrInd :Integer;
Res :LResult;
Caret : TPoint;
TxtRect:TRect;
begin

Result.cx := 0;
Result.cy := 0;

if Text = '' then Exit;

ChrInd := SendMessage(Handle, EM_LINEINDEX, nRow, 0);
Res := SendMessage(Handle, EM_POSFROMCHAR, ChrInd, 0);

if Res > 0 then begin
Result.cx := LoWord(Res);
if Result.cx>Self.ClientRect.Right then Result.cx:=Result.cx-65535-FStartCaretPos.cx;
Result.cy := HiWord(Res);
end; { if }
if NOT WordWrap then begin
Windows.GetCaretPos(Caret);
SendMessage(Handle,EM_GETRECT,0,LPARAM(@TxtRect));
Result.cy:= TxtRect.Top;
end;
end; { GetTextStart }

function TColorEdit.TopLine() :Integer;
begin
Result := SendMessage(Handle, EM_GETFIRSTVISIBLELINE, 0, 0);
end; { TopLine }

function TColorEdit.Line() :Integer;
begin
Result := SendMessage(Handle, EM_LINEFROMCHAR, SelStart, 0);
end; { Line }

function TColorEdit.Col() :Integer;
begin
Result := SelStart - SendMessage(Handle, EM_LINEINDEX, SendMessage(Handle, EM_LINEFROMCHAR, SelStart, 0), 0);
end; { Col }

procedure TColorEdit.SetRedraw(Handle :THandle; Flag :Boolean);
begin
SendMessage(Handle, WM_SETREDRAW, Ord(Flag), 0);
end; { SetRedraw }

procedure TColorEdit.WMVScroll(var Message :TWMVScroll);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMVScroll }

procedure TColorEdit.WMKillFocus(var Message :TWMKillFocus);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(GetDesktopWindow());
FFocusLost:=True;
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }
end; { WMKillFocus }

procedure TColorEdit.WMSetFocus(var Message :TWMSetFocus);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(GetDesktopWindow());
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMSetFocus }

procedure TColorEdit.WMHScroll(var Message :TWMHScroll);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMHScroll }

procedure TColorEdit.WMSize(var Message :TWMSize);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMSize }

procedure TColorEdit.WMEraseBKGND(var Message :TWMERASEBKGND);
begin
// не удалять данную пустую процедуру, иначе будут глюки с изображением
end; { WMEraseBKGND }

procedure TColorEdit.WMMove(var Message :TWMMove);
begin
Invalidate;
inherited;
end; { WMMove }

procedure TColorEdit.CharToCaret(nCharPos :Integer; var nRow, nColumn :Integer);
begin
nRow := SendMessage(Handle, EM_LINEFROMCHAR, nCharPos, 0)+1;
nColumn := nCharPos - SendMessage(Handle, EM_LINEINDEX, SendMessage(Handle, EM_LINEFROMCHAR, nCharPos, 0), 0);
end; { CharToCaret }

procedure TColorEdit.WMLButtonUp(var Message :TWMLButtonUp);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonUp }

procedure TColorEdit.WMLButtonDown(var Message :TWMLButtonDown);
var
Locked :Boolean;

begin

Locked := False;

if FFocusLost then begin
SetRedraw(Handle, False);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
SetRedraw(Handle, True);
end else
try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonDown }

procedure TColorEdit.WMLButtonDblClk(var Message: TWMLButtonDblClk);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonDblClk }

procedure TColorEdit.WMMouseMove(var Message :TWMMouseMove);
var
Locked :Boolean;

begin

Locked := False;

if (Message.Keys and MK_LBUTTON) = 0 then
inherited
else begin

try

RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);

if not FFocusLost then
Locked := LockWindowUpdate(Handle)
else
FFocusLost := False;

inherited;

finally
if (Locked) and (not FFocusLost) then LockWindowUpdate(0);
end; { try }

end; { if }

end; { WMMouseMove }

procedure TColorEdit.WMMouseWheel(var Message :TWMMouseWheel);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMMousewheel }

procedure TColorEdit.WMCHAR(var Message :TWMCHAR);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMCHAR }

procedure TColorEdit.WMKeyDown(var Message :TWMKeyDown);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMKeyDown }

procedure TColorEdit.WMKeyUp(var Message :TWMKeyUp);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMKeyUp }

procedure TColorEdit.PaintLine(oCanvas :TCanvas; cLineText :String; nCurLine :Integer; rTxtRect :TRect);
const
HilightFont = clHighlightText;
HilightBack = clHighlight;

type
TxtAttr = record
FontColor, BckColor :TColor;
Underline :Boolean;
end; { TxtAttr }

var
i, j, px :Integer;
nLineBeg, nColBeg, nLineEnd, nColEnd :Integer;
LastFont, LastBck :TColor;
LastUnderline :Boolean;
rSize :TSize;
t, cCurWord :String;
aCharsColor :array of TxtAttr;
evFontColor, evBckColor :TColor;
evUnderline :Boolean;
TxtOffset :TRect;
begin

try

CharToCaret(SelStart, nLineBeg, nColBeg);
CharToCaret(SelStart+SelLength, nLineEnd, nColEnd);

with oCanvas do begin

px := rTxtRect.Left;
t := cLineText+' ';
SetLength(aCharsColor, Length(cLineText)+1);

for i := 0 to High(aCharsColor) do begin // Инициализируем массив цветов символов
aCharsColor[i].FontColor := Self.Font.Color;
aCharsColor[i].BckColor := Self.Color;
aCharsColor[i].Underline := False;
end; { for }

i := 0;

repeat // Проходим по всей строке и задаем для каждого символа его цвет и цвет его фона

cCurWord := NextWord(t);

if cCurWord <> '' then begin

if cCurWord = ' ' then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtSpace, evFontColor, evBckColor, evUnderline);

aCharsColor[i].FontColor := evFontColor;
aCharsColor[i].BckColor := evBckColor;
aCharsColor[i].Underline := evUnderline;
Inc(i);

end else
if IsSeparator(cCurWord[1]) then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtSeparator, evFontColor, evBckColor, evUnderline);

aCharsColor[i].FontColor := evFontColor;
aCharsColor[i].BckColor := evBckColor;
aCharsColor[i].Underline := evUnderline;
Inc(i);

end else
if IsNumber(cCurWord) then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtNumber, evFontColor, evBckColor, evUnderline);

for j := 1 to Length(cCurWord) do begin
aCharsColor[i+j-1].FontColor := evFontColor;
aCharsColor[i+j-1].BckColor := evBckColor;
aCharsColor[i+j-1].Underline := evUnderline;
end; { for }

Inc(i, Length(cCurWord));

end else begin // Задаем цвет остального текста

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtText, evFontColor, evBckColor, evUnderline);

for j:=1 to Length(cCurWord) do begin
aCharsColor[i+j-1].FontColor := evFontColor;
aCharsColor[i+j-1].BckColor := evBckColor;
aCharsColor[i+j-1].Underline := evUnderline;
end; { for }

Inc(i, Length(cCurWord));

end; { if }

end; { if }

until cCurWord = '';

if (Focused) or (FKeepSelOnLostFocus and not Focused) then begin // это если надо чтобы при потере фокуса исчезало выделение

if (nCurLine = nLineBeg) and (nColBeg <> nColEnd) and (nLineBeg = nLineEnd) then
for i := nColBeg+1 to nColEnd do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine > nLineBeg) and (nCurLine < nLineEnd) then
for i := 1 to Length(cLineText) do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine = nLineBeg) and (nLineBeg < nLineEnd) then
for i := nColBeg+1 to Length(cLineText) do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine = nLineEnd) and (nLineBeg < nLineEnd) then
for i := 1 to nColEnd do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

end; { if }

cCurWord := cLineText[1];
LastFont := aCharsColor[0].FontColor;
LastBck := aCharsColor[0].BckColor;
LastUnderline := aCharsColor[0].Underline;

if Length(cLineText) = 1 then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy));
end; { if }

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

end; { if }

for i := 2 to Length(cLineText) do begin

t := cLineText[i];

if (LastFont <> aCharsColor[i-1].FontColor) or
(LastBck <> aCharsColor[i-1].BckColor) or
(LastUnderline <> aCharsColor[i-1].Underline) then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy))
end; { if }

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

if aCharsColor[i-2].Underline then
DrawUnderline(oCanvas, px, rTxtRect.Top+rSize.cy, px+rSize.cx);

Inc(px, rSize.cx);

cCurWord := '';
LastBck := aCharsColor[i-1].BckColor;
LastFont := aCharsColor[i-1].FontColor;
LastUnderline := aCharsColor[i-1].Underline;

end; { if }

cCurWord := cCurWord+cLineText[i];

if px > rTxtRect.Right then Break;

if i = Length(cLineText) then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy))

end;

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

if aCharsColor[i-1].Underline then
DrawUnderline(oCanvas, px, rTxtRect.Top+rSize.cy, px+rSize.cx);

end; { if }

end; { for }

SendMessage(Self.Handle,EM_GETRECT,0,LPARAM(@TxtOffset));
Brush.Color := Self.Color;
FillRect(Rect(0, rTxtRect.Top, TxtOffset.Left, rTxtRect.Top+rSize.cy))

end; { with }


finally
SetLength(aCharsColor, 0);
end; { try }

end; { PaintLine }

procedure TColorEdit.WMPaint(var Message :TWMPaint);
var
PS :TPaintStruct;
psRect :TRect;
DC :HDC;
hbmNew :HBITMAP;
hbmOld :HBITMAP;
oCanvas,
sCanvas :TCanvas;
i :Integer;
rSize :TSize;
nMax :Integer;
nLineFirst :Integer;
nLineLast :Integer;
nLineCount :Integer;
cLine :String;

begin
// inherited;
if (FStartCaretPos.cx = 0) and (Text <> '') and (Alignment = taLeftJustify) then
FStartCaretPos := GetTextStart(0);

BeginPaint(Handle, PS);

psRect := Self.ClientRect;
DC := CreateCompatibleDC(ps.hdc);
hbmNew := CreateCompatibleBitmap(PS.hdc, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);

oCanvas := TCanvas.Create;
try

oCanvas.Handle := DC;
oCanvas.Font := Self.Font;

with oCanvas do begin

if WordWrap then begin
nLineCount := SendMessage(Self.Handle, EM_GETLINECOUNT,0,0);
nMax := TopLine()+VisibleLines();
if nMax > nLineCount then nMax := nLineCount;
end
else
nMax := 0;

Brush.Color := Self.Color;
FillRect(Self.ClientRect);

rSize := TextExtent(' ');

if GetForegroundWindow() = Self.Parent.Handle then begin
nLineFirst := TopLine() + (PS.rcPaint.Top div rSize.cy);
nLineLast := TopLine() + (PS.rcPaint.Bottom div rSize.cy);
end else begin
nLineFirst := TopLine();
nLineLast := nMax;
end; { if }

for i := nLineFirst to nLineLast do begin

FLineCaretPos := GetTextStart(i);
cLine := TextFromLine(i); //Lines[i];
if cLine = '' then cLine :=' ';
rSize := TextExtent(cLine);

if FLineCaretPos.cy+rSize.cy <= psRect.Bottom then begin
if WordWrap then
PaintLine(oCanvas, cLine, i+1, Rect(FLineCaretPos.cx, FLineCaretPos.cy, Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy))
else begin
EndPaint(Self.Handle,PS);
sCanvas := TCanvas.Create;
sCanvas.Handle := GetDC(Self.Handle);
sCanvas.Font := Self.Font;
sCanvas.Brush.Color := Self.Color;
sCanvas.FillRect(Self.ClientRect);
PaintLine(sCanvas, cLine, i+1, Rect(FLineCaretPos.cx, FLineCaretPos.cy, Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy));
ShowCaret(Self.Handle);
ReleaseDC(Self.Handle,sCanvas.Handle);
sCanvas.Free;
Exit;
end;
end;

end; { for }

end; { with }

finally
BitBlt(PS.hdc, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
EndPaint(Handle, PS);
oCanvas.Free;
end; { try }
Message.Result:=1;
end; { WMPaint }

procedure TColorEdit.WMPrintClient(var Message :TWMPaint);
var
PS :TPaintStruct;
psRect :TRect;
DC :HDC;
hbmNew :HBITMAP;
hbmOld :HBITMAP;
oCanvas :TCanvas;
i :Integer;
rSize :TSize;
nMax :Integer;
nLineFirst :Integer;
nLineLast :Integer;
cLine :String;

begin

BeginPaint(Handle, PS);

psRect := Self.ClientRect;
DC := CreateCompatibleDC(ps.hdc);
hbmNew := CreateCompatibleBitmap(PS.hdc, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);

oCanvas := TCanvas.Create;
try

oCanvas.Handle := DC;
oCanvas.Font := Self.Font;

with oCanvas do begin

if WordWrap then begin
nMax := TopLine()+VisibleLines();
if nMax > SendMessage(Handle, EM_GETLINECOUNT,0,0) then nMax := SendMessage(Handle, EM_GETLINECOUNT,0,0);
end
else
nMax := 0;

Brush.Color := Self.Color;
FillRect(Self.ClientRect);
rSize := TextExtent(' ');

if GetForegroundWindow=Self.Parent.Handle then begin
nLineFirst := TopLine() + (PS.rcPaint.Top div rSize.cy);
nLineLast := TopLine() + (PS.rcPaint.Bottom div rSize.cy);
end else begin
nLineFirst := TopLine();
nLineLast := nMax;
end; { if }

for i := nLineFirst to nLineLast do begin

FLineCaretPos := GetTextStart(i);
cLine := TextFromLine(i); //Lines[i];
if cLine = '' then cLine := ' ';
rSize := TextExtent(cLine);

if FLineCaretPos.cy+rSize.cy <= psRect.Bottom then
PaintLine(oCanvas, cLine, i+1, Rect(FLineCaretPos.cx, FLineCaretPos.cy,Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy));

end; { for }

end; { with }

finally
BitBlt(PS.hdc, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
EndPaint(Handle, PS);
oCanvas.Free;
end; { try }

end; { WMPrintClient }

procedure TColorEdit.DrawUnderline(oCanvas :TCanvas; nFromX, nFromY, nToX: Integer);
var
X, J, Y :Integer;

begin
Y:=0;
J:=1;
with oCanvas do
for X := nFromX to nToX do begin
oCanvas.Pixels[X, nFromY+Y] := clRed;
Y := Y+J;
if Y = 2 then J := -1;
if Y = 0 then J := 1;
end;
end; { DrawUnderline }

procedure Register;
begin

RegisterComponents('Samples', [TColorEdit]);

end; { Register }

end.
Автор: StalkerSoftware
Дата сообщения: 01.10.2009 16:10
ShIvADeSt

Цитата:
Посмотрел как в Мелкософт офисе сделано подчеркивание - сделал здесь так же (волнистая черта не айс). Зато зубчики теперь всегда начинаются сверху (у тебя было от центра).
Теперь линия весьма красивая.


Цитата:
вот переделанный код (заодно пофиксил баг с исчезновением нижних строк - там Handle видимо прыгал от окна родителя к окну контрола).
Да, теперь при потере фокуса текст не пропадает.


Цитата:
Вроде бы пофиксил выделение в сингллайн едите, работает кривовато - потом может что придумаю.
При выделении мышкой или просто тыканье мышкой в сингллайн едите очень заметно мерцание текста в едите. А при наборе текста видно некоторое замедление отображения курсора текста. Хотя само выделение теперь стало правильным.



Автор: ShIvADeSt
Дата сообщения: 02.10.2009 00:54
StalkerSoftware

Цитата:
При выделении мышкой или просто тыканье мышкой в сингллайн едите очень заметно мерцание текста в едите. А при наборе текста видно некоторое замедление отображения курсора текста. Хотя само выделение теперь стало правильным.

Мерцание происходит потому что пришлось для сингллайна отказаться от двойной буферизации (в родном едите тоже мерцает, можешь проверить). Замедление отображения - попробую побороть, просто знаю почему это происходит, а вот как это исправить пока нет. Если не удастся - то это окончательный вариант, просто сингллайн едит очень странный контрол, он упорно пытается отрисовать себя сам, даже когда это не надо (если ты заметил, то для подавления самостоятельной отрисовки пришлось принудительно выходить из обработчика WM_PAINT, что можно (видел случаи), но не красиво).

Добавлено

Забейте на написаное выше, вроде бы (в очередной раз БЫ) победил глюки для single-line edit
[more]

Код:
{****************************************************}
{ }
{ ColorEdit v1.0 }
{ }
{ Copyright (c) 2004 by Gon Perez-Jimenez }
{ http://www.torry.net/authorsmore.php?id=3649 }
{ }
{ Copyright (c) 2009 by ShIvADeSt Moderator Ru-Board }
{ }
{ Copyright (c) 2009 by Stalker SoftWare }
{ }
{****************************************************}

unit ColorEdit;

interface

uses
Windows, Messages, Classes, Graphics, StdCtrls, Controls, SysUtils;


type
TWordType = (wtSeparator, wtSpace, wtNumber, wtText);
TCheckWordEvent = procedure(Sender :TObject; cWord :String; WordType :TWordType; var FontColor, BackgroundColor :TColor; var IsUnderline :Boolean) of object;

TColorEdit = class(TEdit)
private
{ Private declarations }
FStartCaretPos :TSize;
FLineCaretPos :TSize;
FFocusLost :Boolean;
FSeparators :TStrings;
FOnCheckWord :TCheckWordEvent;
FKeepSelOnLostFocus :Boolean;
FAlignment :TAlignment;
FWordWrap :Boolean;
FWantReturns :Boolean;
Start :boolean;
procedure SetRedraw(Handle :THandle; Flag :Boolean);
procedure CharToCaret(nCharPos :Integer; var nRow, nColumn :Integer);

function TextFromLine(nRow:integer):string;

procedure WMKillFocus(var Message :TWMKillFocus); message WM_KILLFOCUS;
procedure WMSetFocus(var Message :TWMSetFocus); message WM_SETFOCUS;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP;
procedure WMCHAR(var Message: TWMCHAR); message WM_CHAR;
procedure WMPrintClient(var Message: TWMPaint); message WM_PRINTCLIENT;
procedure WMERASEBKGND(var Message: TWMERASEBKGND); message WM_ERASEBKGND;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMMove(var Message: TWMMove); message WM_MOVE;
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;

procedure PaintLine(oCanvas :TCanvas; cLineText :String; nCurLine :Integer; rTxtRect :TRect);
procedure DrawUnderline(oCanvas :TCanvas; nFromX, nFromY, nToX :Integer);

procedure SetSeparators(const Value :TStrings);

function NextWord(var cStr :String) :String;

function GetTextStart(nRow :Integer) :TSize;

procedure SetAlignment(const Value: TAlignment);
procedure SetWordWrap(const Value: Boolean);
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
protected
{ Protected declarations }
procedure CreateParams(var Params :TCreateParams); override;

public
{ Public declarations }
procedure GotoXY(nCol, nLine :Integer);
function Line() :Integer;
function Col() :Integer;
function TopLine() :Integer;
function VisibleLines() :Integer;
function IsSeparator(cStr :Char) :Boolean;
function IsNumber(cStr :String) :Boolean;

published
{ Published declarations }
constructor Create(AOwner :TComponent); override;
destructor Destroy(); override;

property Separators :TStrings read FSeparators write SetSeparators;
property OnCheckWord :TCheckWordEvent read FOnCheckWord write FOnCheckWord;
property KeepSelOnLostFocus :Boolean read FKeepSelOnLostFocus write FKeepSelOnLostFocus default False;

property WordWrap :Boolean read FWordWrap write SetWordWrap default False;
property WantReturns :Boolean read FWantReturns write FWantReturns default False;
property Alignment :TAlignment read FAlignment write SetAlignment default taLeftJustify;

end; { TColorEdit }

procedure Register;

implementation


constructor TColorEdit.Create(AOwner: TComponent);
begin
inherited;
FSeparators := TStringList.Create;
FSeparators.Add('.');
FSeparators.Add(',');
FSeparators.Add('|');
FSeparators.Add(' ');
FSeparators.Add(';');
FSeparators.Add(':');
FSeparators.Add('"');
FSeparators.Add('''');
FSeparators.Add('^');
FSeparators.Add('+');
FSeparators.Add('-');
FSeparators.Add('*');
FSeparators.Add('/');
FSeparators.Add('\');
FSeparators.Add('`');
FSeparators.Add('~');
FSeparators.Add('[');
FSeparators.Add(']');
FSeparators.Add('(');
FSeparators.Add(')');
FSeparators.Add('{');
FSeparators.Add('}');
FSeparators.Add('?');
FSeparators.Add('!');
FSeparators.Add('%');
FSeparators.Add('=');
FSeparators.Add('<');
FSeparators.Add('>');

FFocusLost := False;
FKeepSelOnLostFocus := False;

FAlignment := taLeftJustify;
FWordWrap := False;
FWantReturns := False;

end; { Create }

destructor TColorEdit.Destroy;
begin
FSeparators.Free;
inherited;
end; { Destroy }

function TColorEdit.TextFromLine(nRow:integer):string;
var
EditText: array[0..4095] of Char;
L: Integer;
begin
Word((@EditText)^) := SizeOf(EditText);
L := SendMessage(Self.Handle, EM_GETLINE, nRow, Longint(@EditText));
if (EditText[L - 2] = #13) and (EditText[L - 1] = #10) then Dec(L, 2);
SetString(Result, EditText, L);
end;

procedure TColorEdit.SetSeparators(const Value :TStrings);
begin
FSeparators.Assign(Value);
end; { SetSeparators }

procedure TColorEdit.SetAlignment(const Value :TAlignment);
begin

if FAlignment <> Value then begin
FAlignment := Value;
RecreateWnd;
end; { if }

end; { SetAlignment }

procedure TColorEdit.SetWordWrap(const Value :Boolean);
begin

if Value <> FWordWrap then begin
FWordWrap := Value;
RecreateWnd;
end; { if }
end; { SetWordWrap }

procedure TColorEdit.WMGetDlgCode(var Message :TWMGetDlgCode);
begin

inherited;

if not FWantReturns then
Message.Result := (Message.Result and not DLGC_WANTALLKEYS);

end; { WMGetDlgCode }

procedure TColorEdit.CreateParams(var Params: TCreateParams);
const
Alignments: array[TAlignment] of Dword = (ES_LEFT, ES_RIGHT, ES_CENTER);
WordWraps: array[Boolean] of DWORD = (0, ES_AUTOHSCROLL);

begin

inherited CreateParams(Params);

if FWordWrap then
Params.Style := Params.Style and not WordWraps[FWordWrap] or ES_MULTILINE or
Alignments[FAlignment]
else
Params.Style := Params.Style or Alignments[FAlignment]

end; { CreateParams }

function TColorEdit.IsSeparator(cStr :Char) :Boolean;
begin
Result := (FSeparators.IndexOf(cStr) <> -1);
end; { IsSeparator }

function TColorEdit.NextWord(var cStr :String) :String;
begin

Result := '';

if cStr = '' then Exit;

if IsSeparator(cStr[1]) then begin
Result := Result+cStr[1];
Delete(cStr, 1, 1);
end else
while (cStr <> '') and (not IsSeparator(cStr[1])) do begin
Result := Result+cStr[1];
Delete(cStr, 1, 1);
end; { while }

end; { NextWord }

function TColorEdit.IsNumber(cStr :String) :Boolean;
var
i: Integer;

begin

Result := False;

for i:= 1 to Length(cStr) do
case cStr[i] of
'0'..'9':;
else
Exit;
end; { case }

Result := True;

end; { IsNumber }

function TColorEdit.VisibleLines() :Integer;
begin
Result := (Height div (Abs(Font.Height)+2));
end; { VisibleLines }

procedure TColorEdit.GotoXY(nCol, nLine :Integer);
begin

Dec(nLine);
SelStart := 0;
SelLength := 0;
SelStart := nCol+Perform(EM_LINEINDEX, nLine, 0);
SelLength :=0;

end; { GotoXY }

function TColorEdit.GetTextStart(nRow :Integer) :TSize;
var
ChrInd :Integer;
Res :LResult;
Caret : TPoint;
TxtRect:TRect;
begin

Result.cx := 0;
Result.cy := 0;

if Text = '' then Exit;

ChrInd := SendMessage(Handle, EM_LINEINDEX, nRow, 0);
Res := SendMessage(Handle, EM_POSFROMCHAR, ChrInd, 0);

if Res > 0 then begin
Result.cx := LoWord(Res);
if Result.cx>Self.ClientRect.Right then Result.cx:=Result.cx-65535-FStartCaretPos.cx;
Result.cy := HiWord(Res);
end; { if }
if NOT WordWrap then begin
Windows.GetCaretPos(Caret);
SendMessage(Handle,EM_GETRECT,0,LPARAM(@TxtRect));
Result.cy:= TxtRect.Top;
end;
end; { GetTextStart }

function TColorEdit.TopLine() :Integer;
begin
Result := SendMessage(Handle, EM_GETFIRSTVISIBLELINE, 0, 0);
end; { TopLine }

function TColorEdit.Line() :Integer;
begin
Result := SendMessage(Handle, EM_LINEFROMCHAR, SelStart, 0);
end; { Line }

function TColorEdit.Col() :Integer;
begin
Result := SelStart - SendMessage(Handle, EM_LINEINDEX, SendMessage(Handle, EM_LINEFROMCHAR, SelStart, 0), 0);
end; { Col }

procedure TColorEdit.SetRedraw(Handle :THandle; Flag :Boolean);
begin
SendMessage(Handle, WM_SETREDRAW, Ord(Flag), 0);
end; { SetRedraw }

procedure TColorEdit.WMVScroll(var Message :TWMVScroll);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMVScroll }

procedure TColorEdit.WMKillFocus(var Message :TWMKillFocus);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(GetDesktopWindow());
FFocusLost:=True;
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }
end; { WMKillFocus }

procedure TColorEdit.WMSetFocus(var Message :TWMSetFocus);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(GetDesktopWindow());
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMSetFocus }

procedure TColorEdit.WMHScroll(var Message :TWMHScroll);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMHScroll }

procedure TColorEdit.WMSize(var Message :TWMSize);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMSize }

procedure TColorEdit.WMEraseBKGND(var Message :TWMERASEBKGND);
begin
// не удалять данную пустую процедуру, иначе будут глюки с изображением
end; { WMEraseBKGND }

procedure TColorEdit.WMMove(var Message :TWMMove);
begin
Invalidate;
inherited;
end; { WMMove }

procedure TColorEdit.CharToCaret(nCharPos :Integer; var nRow, nColumn :Integer);
begin
nRow := SendMessage(Handle, EM_LINEFROMCHAR, nCharPos, 0)+1;
nColumn := nCharPos - SendMessage(Handle, EM_LINEINDEX, SendMessage(Handle, EM_LINEFROMCHAR, nCharPos, 0), 0);
end; { CharToCaret }

procedure TColorEdit.WMLButtonUp(var Message :TWMLButtonUp);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonUp }

procedure TColorEdit.WMLButtonDown(var Message :TWMLButtonDown);
var
Locked :Boolean;

begin

Locked := False;

if FFocusLost then begin
SetRedraw(Handle, False);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
SetRedraw(Handle, True);
end else
try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonDown }

procedure TColorEdit.WMLButtonDblClk(var Message: TWMLButtonDblClk);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonDblClk }

procedure TColorEdit.WMMouseMove(var Message :TWMMouseMove);
var
Locked :Boolean;

begin

Locked := False;

if (Message.Keys and MK_LBUTTON) = 0 then
inherited
else begin

try

RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);

if not FFocusLost then
Locked := LockWindowUpdate(Handle)
else
FFocusLost := False;

inherited;

finally
if (Locked) and (not FFocusLost) then LockWindowUpdate(0);
end; { try }

end; { if }

end; { WMMouseMove }

procedure TColorEdit.WMMouseWheel(var Message :TWMMouseWheel);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMMousewheel }

procedure TColorEdit.WMCHAR(var Message :TWMCHAR);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMCHAR }

procedure TColorEdit.WMKeyDown(var Message :TWMKeyDown);
var
Locked :Boolean;

begin
Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }
end; { WMKeyDown }

procedure TColorEdit.WMKeyUp(var Message :TWMKeyUp);
var
Locked :Boolean;

begin
Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMKeyUp }

procedure TColorEdit.PaintLine(oCanvas :TCanvas; cLineText :String; nCurLine :Integer; rTxtRect :TRect);
const
HilightFont = clHighlightText;
HilightBack = clHighlight;

type
TxtAttr = record
FontColor, BckColor :TColor;
Underline :Boolean;
end; { TxtAttr }

var
i, j, px :Integer;
nLineBeg, nColBeg, nLineEnd, nColEnd :Integer;
LastFont, LastBck :TColor;
LastUnderline :Boolean;
rSize :TSize;
t, cCurWord :String;
aCharsColor :array of TxtAttr;
evFontColor, evBckColor :TColor;
evUnderline :Boolean;
TxtOffset :TRect;
begin

try

CharToCaret(SelStart, nLineBeg, nColBeg);
CharToCaret(SelStart+SelLength, nLineEnd, nColEnd);

with oCanvas do begin

px := rTxtRect.Left;
t := cLineText+' ';
SetLength(aCharsColor, Length(cLineText)+1);

for i := 0 to High(aCharsColor) do begin // Инициализируем массив цветов символов
aCharsColor[i].FontColor := Self.Font.Color;
aCharsColor[i].BckColor := Self.Color;
aCharsColor[i].Underline := False;
end; { for }

i := 0;

repeat // Проходим по всей строке и задаем для каждого символа его цвет и цвет его фона

cCurWord := NextWord(t);

if cCurWord <> '' then begin

if cCurWord = ' ' then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtSpace, evFontColor, evBckColor, evUnderline);

aCharsColor[i].FontColor := evFontColor;
aCharsColor[i].BckColor := evBckColor;
aCharsColor[i].Underline := evUnderline;
Inc(i);

end else
if IsSeparator(cCurWord[1]) then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtSeparator, evFontColor, evBckColor, evUnderline);

aCharsColor[i].FontColor := evFontColor;
aCharsColor[i].BckColor := evBckColor;
aCharsColor[i].Underline := evUnderline;
Inc(i);

end else
if IsNumber(cCurWord) then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtNumber, evFontColor, evBckColor, evUnderline);

for j := 1 to Length(cCurWord) do begin
aCharsColor[i+j-1].FontColor := evFontColor;
aCharsColor[i+j-1].BckColor := evBckColor;
aCharsColor[i+j-1].Underline := evUnderline;
end; { for }

Inc(i, Length(cCurWord));

end else begin // Задаем цвет остального текста

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtText, evFontColor, evBckColor, evUnderline);

for j:=1 to Length(cCurWord) do begin
aCharsColor[i+j-1].FontColor := evFontColor;
aCharsColor[i+j-1].BckColor := evBckColor;
aCharsColor[i+j-1].Underline := evUnderline;
end; { for }

Inc(i, Length(cCurWord));

end; { if }

end; { if }

until cCurWord = '';

if (Focused) or (FKeepSelOnLostFocus and not Focused) then begin // это если надо чтобы при потере фокуса исчезало выделение

if (nCurLine = nLineBeg) and (nColBeg <> nColEnd) and (nLineBeg = nLineEnd) then
for i := nColBeg+1 to nColEnd do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine > nLineBeg) and (nCurLine < nLineEnd) then
for i := 1 to Length(cLineText) do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine = nLineBeg) and (nLineBeg < nLineEnd) then
for i := nColBeg+1 to Length(cLineText) do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine = nLineEnd) and (nLineBeg < nLineEnd) then
for i := 1 to nColEnd do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

end; { if }

cCurWord := cLineText[1];
LastFont := aCharsColor[0].FontColor;
LastBck := aCharsColor[0].BckColor;
LastUnderline := aCharsColor[0].Underline;

if Length(cLineText) = 1 then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy));
end; { if }

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

end; { if }

for i := 2 to Length(cLineText) do begin

t := cLineText[i];

if (LastFont <> aCharsColor[i-1].FontColor) or
(LastBck <> aCharsColor[i-1].BckColor) or
(LastUnderline <> aCharsColor[i-1].Underline) then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy))
end; { if }

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

if aCharsColor[i-2].Underline then
DrawUnderline(oCanvas, px, rTxtRect.Top+rSize.cy, px+rSize.cx);

Inc(px, rSize.cx);

cCurWord := '';
LastBck := aCharsColor[i-1].BckColor;
LastFont := aCharsColor[i-1].FontColor;
LastUnderline := aCharsColor[i-1].Underline;

end; { if }

cCurWord := cCurWord+cLineText[i];

if px > rTxtRect.Right then Break;

if i = Length(cLineText) then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy))

end;

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

if aCharsColor[i-1].Underline then
DrawUnderline(oCanvas, px, rTxtRect.Top+rSize.cy, px+rSize.cx);

end; { if }

end; { for }

SendMessage(Self.Handle,EM_GETRECT,0,LPARAM(@TxtOffset));
Brush.Color := Self.Color;
FillRect(Rect(0, rTxtRect.Top, TxtOffset.Left, rTxtRect.Top+rSize.cy))

end; { with }


finally
SetLength(aCharsColor, 0);
end; { try }

end; { PaintLine }

procedure TColorEdit.WMPaint(var Message :TWMPaint);
var
PS :TPaintStruct;
psRect :TRect;
DC, slDC :HDC;
hbmNew :HBITMAP;
hbmOld :HBITMAP;
oCanvas,
sCanvas :TCanvas;
i :Integer;
rSize :TSize;
nMax :Integer;
nLineFirst :Integer;
nLineLast :Integer;
nLineCount :Integer;
cLine :String;

begin
// inherited;
if (FStartCaretPos.cx = 0) and (Text <> '') and (Alignment = taLeftJustify) then
FStartCaretPos := GetTextStart(0);

if NOT WordWrap then begin
slDC := GetDC(Self.Handle);
psRect := Self.ClientRect;
DC := CreateCompatibleDC(slDC);
hbmNew := CreateCompatibleBitmap(slDC, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);
sCanvas := TCanvas.Create;
sCanvas.Handle := DC;
sCanvas.Font := Self.Font;
sCanvas.Brush.Color := Self.Color;
sCanvas.FillRect(Self.ClientRect);
FLineCaretPos := GetTextStart(1);
cLine := TextFromLine(1); //Lines[i];
if cLine = '' then cLine :=' ';
rSize := sCanvas.TextExtent(cLine);
PaintLine(sCanvas, cLine, 1, Rect(FLineCaretPos.cx, FLineCaretPos.cy, Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy));
BitBlt(slDC, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
ReleaseDC(Self.Handle,slDC);
DeleteDC(slDC);
sCanvas.Free;
BeginPaint(Handle, PS);
EndPaint(Handle, PS);
end
else begin

BeginPaint(Handle, PS);

psRect := Self.ClientRect;
DC := CreateCompatibleDC(ps.hdc);
hbmNew := CreateCompatibleBitmap(PS.hdc, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);

oCanvas := TCanvas.Create;
try

oCanvas.Handle := DC;
oCanvas.Font := Self.Font;

with oCanvas do begin

if WordWrap then begin
nLineCount := SendMessage(Self.Handle, EM_GETLINECOUNT,0,0);
nMax := TopLine()+VisibleLines();
if nMax > nLineCount then nMax := nLineCount;
end
else
nMax := 0;

Brush.Color := Self.Color;
FillRect(Self.ClientRect);

rSize := TextExtent(' ');

if GetForegroundWindow() = Self.Parent.Handle then begin
nLineFirst := TopLine() + (PS.rcPaint.Top div rSize.cy);
nLineLast := TopLine() + (PS.rcPaint.Bottom div rSize.cy);
end else begin
nLineFirst := TopLine();
nLineLast := nMax;
end; { if }

for i := nLineFirst to nLineLast do begin

FLineCaretPos := GetTextStart(i);
cLine := TextFromLine(i); //Lines[i];
if cLine = '' then cLine :=' ';
rSize := TextExtent(cLine);

if FLineCaretPos.cy+rSize.cy <= psRect.Bottom then
PaintLine(oCanvas, cLine, i+1, Rect(FLineCaretPos.cx, FLineCaretPos.cy, Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy))

end; { for }

end; { with }

finally
BitBlt(PS.hdc, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
EndPaint(Handle, PS);
oCanvas.Free;
end; { try }
end;

end; { WMPaint }

procedure TColorEdit.WMPrintClient(var Message :TWMPaint);
var
PS :TPaintStruct;
psRect :TRect;
DC :HDC;
hbmNew :HBITMAP;
hbmOld :HBITMAP;
oCanvas :TCanvas;
i :Integer;
rSize :TSize;
nMax :Integer;
nLineFirst :Integer;
nLineLast :Integer;
cLine :String;

begin

BeginPaint(Handle, PS);

psRect := Self.ClientRect;
DC := CreateCompatibleDC(ps.hdc);
hbmNew := CreateCompatibleBitmap(PS.hdc, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);

oCanvas := TCanvas.Create;
try

oCanvas.Handle := DC;
oCanvas.Font := Self.Font;

with oCanvas do begin

if WordWrap then begin
nMax := TopLine()+VisibleLines();
if nMax > SendMessage(Handle, EM_GETLINECOUNT,0,0) then nMax := SendMessage(Handle, EM_GETLINECOUNT,0,0);
end
else
nMax := 0;

Brush.Color := Self.Color;
FillRect(Self.ClientRect);
rSize := TextExtent(' ');

if GetForegroundWindow=Self.Parent.Handle then begin
nLineFirst := TopLine() + (PS.rcPaint.Top div rSize.cy);
nLineLast := TopLine() + (PS.rcPaint.Bottom div rSize.cy);
end else begin
nLineFirst := TopLine();
nLineLast := nMax;
end; { if }

for i := nLineFirst to nLineLast do begin

FLineCaretPos := GetTextStart(i);
cLine := TextFromLine(i); //Lines[i];
if cLine = '' then cLine := ' ';
rSize := TextExtent(cLine);

if FLineCaretPos.cy+rSize.cy <= psRect.Bottom then
PaintLine(oCanvas, cLine, i+1, Rect(FLineCaretPos.cx, FLineCaretPos.cy,Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy));

end; { for }

end; { with }

finally
BitBlt(PS.hdc, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
EndPaint(Handle, PS);
oCanvas.Free;
end; { try }

end; { WMPrintClient }

procedure TColorEdit.DrawUnderline(oCanvas :TCanvas; nFromX, nFromY, nToX: Integer);
var
X, J, Y :Integer;

begin
Y:=0;
J:=1;
with oCanvas do
for X := nFromX to nToX do begin
oCanvas.Pixels[X, nFromY+Y] := clRed;
Y := Y+J;
if Y = 2 then J := -1;
if Y = 0 then J := 1;
end;
end; { DrawUnderline }

procedure Register;
begin

RegisterComponents('Samples', [TColorEdit]);

end; { Register }

end.
Автор: StalkerSoftware
Дата сообщения: 02.10.2009 12:57
ShIvADeSt

Цитата:
Метод называется анально-извращенный, но работает Мерцания не вижу, каретка движется правильно - вроде все в шоколаде.

Мерцания и пропадания каретки больше нет.

Я смотрю, что для победы над single-line edit ты фактически внутри WMPaint сделал еще один WMPaint по свойству WordWrap ...
А WMPrintClient так и не вызывается, думаю его можно вообще убрать.

1) В режиме (Alignment <> taLeftJustify) and (not WordWrap) при наборе текста, когда текст заполняет весь контрол, то текст исчезает (точнее говоря он весь уходит за левую границу контрола)

2) Общая ошибка для ColorEdit и ColorMemo. Копируем в буфер любой кусок текста. И вставляем его в ColorEdit или ColorMemo через стандартное контекстное меню контролов - все выделения пропадают. Но стоит нажать мышку или любую клавишу и все цветовое выделение восстанавливается назад. При вставке текста горячими клавишами такой ошибки не происходит.
Автор: ShIvADeSt
Дата сообщения: 02.10.2009 15:07
StalkerSoftware

Цитата:
Я смотрю, что для победы над single-line edit ты фактически внутри WMPaint сделал еще один WMPaint по свойству WordWrap ...
А WMPrintClient так и не вызывается, думаю его можно вообще убрать.

Убирай, просто при разработке какого контрола задействовали данное сообщение, не помню зачем - оставил тут тоже. Но у меня тоже ни разу на нем брейк не сработал, поэтому убирай. Я сделал не то что еще один WMPAINT - я просто через одно место реализовал отрисовку (вопреки MSDN), так как сам мелкософт поведение контрола реализовал через одно место.

Цитата:
1) В режиме (Alignment <> taLeftJustify) and (not WordWrap) при наборе текста, когда текст заполняет весь контрол, то текст исчезает (точнее говоря он весь уходит за левую границу контрола)

Судя повсему корни плывут из GetCaretPos (или как там оно называется, просто пишу из дома - проекта под рукой нет), там в коде для Мемо у меня было различные способы в зависимости от скролбаров - тут надо аналогично сделать, только в зависимости от вордврапа.

Цитата:
Копируем в буфер любой кусок текста. И вставляем его в ColorEdit или ColorMemo через стандартное контекстное меню контролов - все выделения пропадают.

Надо сделать обработчик WM_PASTE и перерисовку при нем. На работе буду - доделаю, приведу общий кусок. Потом там и там вставишь.
Автор: ShIvADeSt
Дата сообщения: 06.10.2009 05:54
StalkerSoftware
В общем посмотрел то что ты написал. Насчет выравнивания - мне интересно, какое должно быть поведение при выравнивании центр? У едита такого свойства нет, у Мемо в этом случае происходит перенос на новую строку. Так что это не баг - это неправильно выставленные свойства.

Цитата:
Общая ошибка для ColorEdit и ColorMemo. Копируем в буфер любой кусок текста. И вставляем его в ColorEdit или ColorMemo через стандартное контекстное меню контролов - все выделения пропадают. Но стоит нажать мышку или любую клавишу и все цветовое выделение восстанавливается назад. При вставке текста горячими клавишами такой ошибки не происходит.

Вообще не смог повторить. Выделяю текст, нажимаю ПКМ, делаю вставить - выделенный текст исчезает, выделений нет вообще. Проверил - в стандартном Едите поведение такое же.

Проверь как работает в стандартном контроле и сравни с переделанными.
Автор: StalkerSoftware
Дата сообщения: 06.10.2009 18:44
ShIvADeSt

Цитата:
Насчет выравнивания - мне интересно, какое должно быть поведение при выравнивании центр?
Это ты очень легко можешь увидеть и сам. Возьми ColorEdit, удали из него все, что относиться к функционалу Color, оставь только свойства WordWrap, WantReturns, Alignment и необходимы им обработчики (если хочешь я сам могу такое сделать и выложить этот Edit на своем сайте).
И ты увидишь, что текст вполне себе нормально скролируется и никуда не пропадает.


Цитата:
Вообще не смог повторить. Выделяю текст, нажимаю ПКМ, делаю вставить - выделенный текст исчезает, выделений нет вообще. Проверил - в стандартном Едите поведение такое же.
Эээ, ты меня видимо не так понял.
Копируешь любой текст в буфер обмена.
Запускаешь демку с ColorMemo. Тыкаешь мышкой в ColorMemo. Жмешь ПКМ, выбираешь в появившемся контекстном меню пункт "Вставить". Текст иp буфера вставляется в ColorMemo, но вся цветовая расцветка в нем пропадает.Если после этого в ColorMemo нажать любую клавишу или кнопку мыши, то цветовая расцветка опять появляется.
Автор: ShIvADeSt
Дата сообщения: 07.10.2009 01:23
StalkerSoftware

Цитата:
Текст иp буфера вставляется в ColorMemo, но вся цветовая расцветка в нем пропадает.

Не цветовое выделение, а синтаксическая подсветка У меня выделение четко ассоциируется с выделением мышью (курсором).

Цитата:
Это ты очень легко можешь увидеть и сам. Возьми ColorEdit, удали из него все, что относиться к функционалу Color, оставь только свойства WordWrap, WantReturns, Alignment и необходимы им обработчики (если хочешь я сам могу такое сделать и выложить этот Edit на своем сайте).
И ты увидишь, что текст вполне себе нормально скролируется и никуда не пропадает.

Сделай - я просто ставлю нормальный Едит и измененный. Выставляю одинаковые свойства и сравниваю поведение. Поэтому сделай сам как тебе надо, а я потом буду доделывать под нужное поведение. В общем как все сделаешь - так буду сразу все делать.
Автор: StalkerSoftware
Дата сообщения: 07.10.2009 14:39
ShIvADeSt

Цитата:
Не цветовое выделение, а синтаксическая подсветка У меня выделение четко ассоциируется с выделением мышью (курсором).
Согласен. Но главное, что ты эту ошибку увидел.


Цитата:
Сделай
Сделал, качай TAdvEdit. Это тот же ColorEdit, только без Color-функционала. Можешь в нем менять свойства Alignment и WordWrap и увидишь что он во всех случаях ведет (отрисовывает) себя нормально.
Автор: ShIvADeSt
Дата сообщения: 23.10.2009 07:01
StalkerSoftware

Цитата:
1) В режиме (Alignment <> taLeftJustify) and (not WordWrap) при наборе текста, когда текст заполняет весь контрол, то текст исчезает (точнее говоря он весь уходит за левую границу контрола)

Не удалось повторить. На всякий случай выкладываю полный код юнита.
[more]

Код:
{****************************************************}
{ }
{ ColorEdit v1.0 }
{ }
{ Copyright (c) 2004 by Gon Perez-Jimenez }
{ http://www.torry.net/authorsmore.php?id=3649 }
{ }
{ Copyright (c) 2009 by ShIvADeSt Moderator Ru-Board }
{ }
{ Copyright (c) 2009 by Stalker SoftWare }
{ }
{****************************************************}

unit ColorEdit;

interface

uses
Windows, Messages, Classes, Graphics, StdCtrls, Controls, SysUtils;


type
TWordType = (wtSeparator, wtSpace, wtNumber, wtText);
TCheckWordEvent = procedure(Sender :TObject; cWord :String; WordType :TWordType; var FontColor, BackgroundColor :TColor; var IsUnderline :Boolean) of object;

TColorEdit = class(TEdit)
private
{ Private declarations }
FStartCaretPos :TSize;
FLineCaretPos :TSize;
FFocusLost :Boolean;
FSeparators :TStrings;
FOnCheckWord :TCheckWordEvent;
FKeepSelOnLostFocus :Boolean;
FAlignment :TAlignment;
FWordWrap :Boolean;
FWantReturns :Boolean;
procedure SetRedraw(Handle :THandle; Flag :Boolean);
procedure CharToCaret(nCharPos :Integer; var nRow, nColumn :Integer);

function TextFromLine(nRow:integer):string;

procedure WMKillFocus(var Message :TWMKillFocus); message WM_KILLFOCUS;
procedure WMPaste(var Message :TWMPaste); message WM_PASTE;
procedure WMSetFocus(var Message :TWMSetFocus); message WM_SETFOCUS;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP;
procedure WMCHAR(var Message: TWMCHAR); message WM_CHAR;
procedure WMPrintClient(var Message: TWMPaint); message WM_PRINTCLIENT;
procedure WMERASEBKGND(var Message: TWMERASEBKGND); message WM_ERASEBKGND;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMMove(var Message: TWMMove); message WM_MOVE;
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;

procedure PaintLine(oCanvas :TCanvas; cLineText :String; nCurLine :Integer; rTxtRect :TRect);
procedure DrawUnderline(oCanvas :TCanvas; nFromX, nFromY, nToX :Integer);

procedure SetSeparators(const Value :TStrings);

function NextWord(var cStr :String) :String;

function GetTextStart(nRow :Integer) :TSize;

procedure SetAlignment(const Value: TAlignment);
procedure SetWordWrap(const Value: Boolean);
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
protected
{ Protected declarations }
procedure CreateParams(var Params :TCreateParams); override;

public
{ Public declarations }
procedure GotoXY(nCol, nLine :Integer);
function Line() :Integer;
function Col() :Integer;
function TopLine() :Integer;
function VisibleLines() :Integer;
function IsSeparator(cStr :Char) :Boolean;
function IsNumber(cStr :String) :Boolean;

published
{ Published declarations }
constructor Create(AOwner :TComponent); override;
destructor Destroy(); override;

property Separators :TStrings read FSeparators write SetSeparators;
property OnCheckWord :TCheckWordEvent read FOnCheckWord write FOnCheckWord;
property KeepSelOnLostFocus :Boolean read FKeepSelOnLostFocus write FKeepSelOnLostFocus default False;

property WordWrap :Boolean read FWordWrap write SetWordWrap default False;
property WantReturns :Boolean read FWantReturns write FWantReturns default False;
property Alignment :TAlignment read FAlignment write SetAlignment default taLeftJustify;

end; { TColorEdit }

procedure Register;

implementation


constructor TColorEdit.Create(AOwner: TComponent);
begin
inherited;
FSeparators := TStringList.Create;
FSeparators.Add('.');
FSeparators.Add(',');
FSeparators.Add('|');
FSeparators.Add(' ');
FSeparators.Add(';');
FSeparators.Add(':');
FSeparators.Add('"');
FSeparators.Add('''');
FSeparators.Add('^');
FSeparators.Add('+');
FSeparators.Add('-');
FSeparators.Add('*');
FSeparators.Add('/');
FSeparators.Add('\');
FSeparators.Add('`');
FSeparators.Add('~');
FSeparators.Add('[');
FSeparators.Add(']');
FSeparators.Add('(');
FSeparators.Add(')');
FSeparators.Add('{');
FSeparators.Add('}');
FSeparators.Add('?');
FSeparators.Add('!');
FSeparators.Add('%');
FSeparators.Add('=');
FSeparators.Add('<');
FSeparators.Add('>');

FFocusLost := False;
FKeepSelOnLostFocus := False;

FAlignment := taLeftJustify;
FWordWrap := False;
FWantReturns := False;

end; { Create }

destructor TColorEdit.Destroy;
begin
FSeparators.Free;
inherited;
end; { Destroy }

function TColorEdit.TextFromLine(nRow:integer):string;
var
EditText: array[0..4095] of Char;
L: Integer;
begin
Word((@EditText)^) := SizeOf(EditText);
L := SendMessage(Self.Handle, EM_GETLINE, nRow, Longint(@EditText));
if (EditText[L - 2] = #13) and (EditText[L - 1] = #10) then Dec(L, 2);
SetString(Result, EditText, L);
end;

procedure TColorEdit.SetSeparators(const Value :TStrings);
begin
FSeparators.Assign(Value);
end; { SetSeparators }

procedure TColorEdit.SetAlignment(const Value :TAlignment);
begin

if FAlignment <> Value then begin
FAlignment := Value;
RecreateWnd;
end; { if }

end; { SetAlignment }

procedure TColorEdit.SetWordWrap(const Value :Boolean);
begin

if Value <> FWordWrap then begin
FWordWrap := Value;
RecreateWnd;
end; { if }
end; { SetWordWrap }

procedure TColorEdit.WMGetDlgCode(var Message :TWMGetDlgCode);
begin

inherited;

if not FWantReturns then
Message.Result := (Message.Result and not DLGC_WANTALLKEYS);

end; { WMGetDlgCode }

procedure TColorEdit.CreateParams(var Params: TCreateParams);
const
Alignments: array[TAlignment] of Dword = (ES_LEFT, ES_RIGHT, ES_CENTER);
WordWraps: array[Boolean] of DWORD = (0, ES_AUTOHSCROLL);

begin

inherited CreateParams(Params);

if FWordWrap then
Params.Style := Params.Style and not WordWraps[FWordWrap] or ES_MULTILINE or
Alignments[FAlignment]
else
Params.Style := Params.Style or Alignments[FAlignment]

end; { CreateParams }

function TColorEdit.IsSeparator(cStr :Char) :Boolean;
begin
Result := (FSeparators.IndexOf(cStr) <> -1);
end; { IsSeparator }

function TColorEdit.NextWord(var cStr :String) :String;
begin

Result := '';

if cStr = '' then Exit;

if IsSeparator(cStr[1]) then begin
Result := Result+cStr[1];
Delete(cStr, 1, 1);
end else
while (cStr <> '') and (not IsSeparator(cStr[1])) do begin
Result := Result+cStr[1];
Delete(cStr, 1, 1);
end; { while }

end; { NextWord }

function TColorEdit.IsNumber(cStr :String) :Boolean;
var
i: Integer;

begin

Result := False;

for i:= 1 to Length(cStr) do
case cStr[i] of
'0'..'9':;
else
Exit;
end; { case }

Result := True;

end; { IsNumber }

function TColorEdit.VisibleLines() :Integer;
begin
Result := (Height div (Abs(Font.Height)+2));
end; { VisibleLines }

procedure TColorEdit.GotoXY(nCol, nLine :Integer);
begin

Dec(nLine);
SelStart := 0;
SelLength := 0;
SelStart := nCol+Perform(EM_LINEINDEX, nLine, 0);
SelLength :=0;

end; { GotoXY }

function TColorEdit.GetTextStart(nRow :Integer) :TSize;
var
ChrInd :Integer;
Res :LResult;
Caret : TPoint;
TxtRect:TRect;
begin

Result.cx := 0;
Result.cy := 0;

if Text = '' then Exit;

ChrInd := SendMessage(Handle, EM_LINEINDEX, nRow, 0);
Res := SendMessage(Handle, EM_POSFROMCHAR, ChrInd, 0);

if Res > 0 then begin
Result.cx := LoWord(Res);
if Result.cx>Self.ClientRect.Right then Result.cx:=Result.cx-65535-FStartCaretPos.cx;
Result.cy := HiWord(Res);
end; { if }
if NOT WordWrap then begin
Windows.GetCaretPos(Caret);
SendMessage(Handle,EM_GETRECT,0,LPARAM(@TxtRect));
Result.cy:= TxtRect.Top;
end;
end; { GetTextStart }

function TColorEdit.TopLine() :Integer;
begin
Result := SendMessage(Handle, EM_GETFIRSTVISIBLELINE, 0, 0);
end; { TopLine }

function TColorEdit.Line() :Integer;
begin
Result := SendMessage(Handle, EM_LINEFROMCHAR, SelStart, 0);
end; { Line }

function TColorEdit.Col() :Integer;
begin
Result := SelStart - SendMessage(Handle, EM_LINEINDEX, SendMessage(Handle, EM_LINEFROMCHAR, SelStart, 0), 0);
end; { Col }

procedure TColorEdit.SetRedraw(Handle :THandle; Flag :Boolean);
begin
SendMessage(Handle, WM_SETREDRAW, Ord(Flag), 0);
end; { SetRedraw }

procedure TColorEdit.WMVScroll(var Message :TWMVScroll);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMVScroll }

procedure TColorEdit.WMKillFocus(var Message :TWMKillFocus);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(GetDesktopWindow());
FFocusLost:=True;
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }
end; { WMKillFocus }

procedure TColorEdit.WMPaste(var Message :TWMPaste);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end;

procedure TColorEdit.WMSetFocus(var Message :TWMSetFocus);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(GetDesktopWindow());
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMSetFocus }

procedure TColorEdit.WMHScroll(var Message :TWMHScroll);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMHScroll }

procedure TColorEdit.WMSize(var Message :TWMSize);
begin
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
end; { WMSize }

procedure TColorEdit.WMEraseBKGND(var Message :TWMERASEBKGND);
begin
// не удалять данную пустую процедуру, иначе будут глюки с изображением
end; { WMEraseBKGND }

procedure TColorEdit.WMMove(var Message :TWMMove);
begin
Invalidate;
inherited;
end; { WMMove }

procedure TColorEdit.CharToCaret(nCharPos :Integer; var nRow, nColumn :Integer);
begin
nRow := SendMessage(Handle, EM_LINEFROMCHAR, nCharPos, 0)+1;
nColumn := nCharPos - SendMessage(Handle, EM_LINEINDEX, SendMessage(Handle, EM_LINEFROMCHAR, nCharPos, 0), 0);
end; { CharToCaret }

procedure TColorEdit.WMLButtonUp(var Message :TWMLButtonUp);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonUp }

procedure TColorEdit.WMLButtonDown(var Message :TWMLButtonDown);
var
Locked :Boolean;

begin

Locked := False;

if FFocusLost then begin
SetRedraw(Handle, False);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
SetRedraw(Handle, True);
end else
try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonDown }

procedure TColorEdit.WMLButtonDblClk(var Message: TWMLButtonDblClk);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMLButtonDblClk }

procedure TColorEdit.WMMouseMove(var Message :TWMMouseMove);
var
Locked :Boolean;

begin

Locked := False;

if (Message.Keys and MK_LBUTTON) = 0 then
inherited
else begin

try

RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);

if not FFocusLost then
Locked := LockWindowUpdate(Handle)
else
FFocusLost := False;

inherited;

finally
if (Locked) and (not FFocusLost) then LockWindowUpdate(0);
end; { try }

end; { if }

end; { WMMouseMove }

procedure TColorEdit.WMMouseWheel(var Message :TWMMouseWheel);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMMousewheel }

procedure TColorEdit.WMCHAR(var Message :TWMCHAR);
var
Locked :Boolean;

begin

Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMCHAR }

procedure TColorEdit.WMKeyDown(var Message :TWMKeyDown);
var
Locked :Boolean;

begin
Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }
end; { WMKeyDown }

procedure TColorEdit.WMKeyUp(var Message :TWMKeyUp);
var
Locked :Boolean;

begin
Locked := False;

try
Locked := LockWindowUpdate(Handle);
inherited;
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE);
finally
if Locked then LockWindowUpdate(0);
end; { try }

end; { WMKeyUp }

procedure TColorEdit.PaintLine(oCanvas :TCanvas; cLineText :String; nCurLine :Integer; rTxtRect :TRect);
const
HilightFont = clHighlightText;
HilightBack = clHighlight;

type
TxtAttr = record
FontColor, BckColor :TColor;
Underline :Boolean;
end; { TxtAttr }

var
i, j, px :Integer;
nLineBeg, nColBeg, nLineEnd, nColEnd :Integer;
LastFont, LastBck :TColor;
LastUnderline :Boolean;
rSize :TSize;
t, cCurWord :String;
aCharsColor :array of TxtAttr;
evFontColor, evBckColor :TColor;
evUnderline :Boolean;
TxtOffset :TRect;
begin

try

CharToCaret(SelStart, nLineBeg, nColBeg);
CharToCaret(SelStart+SelLength, nLineEnd, nColEnd);

with oCanvas do begin

px := rTxtRect.Left;
t := cLineText+' ';
SetLength(aCharsColor, Length(cLineText)+1);

for i := 0 to High(aCharsColor) do begin // Инициализируем массив цветов символов
aCharsColor[i].FontColor := Self.Font.Color;
aCharsColor[i].BckColor := Self.Color;
aCharsColor[i].Underline := False;
end; { for }

i := 0;

repeat // Проходим по всей строке и задаем для каждого символа его цвет и цвет его фона

cCurWord := NextWord(t);

if cCurWord <> '' then begin

if cCurWord = ' ' then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtSpace, evFontColor, evBckColor, evUnderline);

aCharsColor[i].FontColor := evFontColor;
aCharsColor[i].BckColor := evBckColor;
aCharsColor[i].Underline := evUnderline;
Inc(i);

end else
if IsSeparator(cCurWord[1]) then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtSeparator, evFontColor, evBckColor, evUnderline);

aCharsColor[i].FontColor := evFontColor;
aCharsColor[i].BckColor := evBckColor;
aCharsColor[i].Underline := evUnderline;
Inc(i);

end else
if IsNumber(cCurWord) then begin

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtNumber, evFontColor, evBckColor, evUnderline);

for j := 1 to Length(cCurWord) do begin
aCharsColor[i+j-1].FontColor := evFontColor;
aCharsColor[i+j-1].BckColor := evBckColor;
aCharsColor[i+j-1].Underline := evUnderline;
end; { for }

Inc(i, Length(cCurWord));

end else begin // Задаем цвет остального текста

evFontColor := Self.Font.Color;
evBckColor := Self.Color;
evUnderline := False;

if Assigned(FOnCheckWord) then
FOnCheckWord(Self, cCurWord, wtText, evFontColor, evBckColor, evUnderline);

for j:=1 to Length(cCurWord) do begin
aCharsColor[i+j-1].FontColor := evFontColor;
aCharsColor[i+j-1].BckColor := evBckColor;
aCharsColor[i+j-1].Underline := evUnderline;
end; { for }

Inc(i, Length(cCurWord));

end; { if }

end; { if }

until cCurWord = '';

if (Focused) or (FKeepSelOnLostFocus and not Focused) then begin // это если надо чтобы при потере фокуса исчезало выделение

if (nCurLine = nLineBeg) and (nColBeg <> nColEnd) and (nLineBeg = nLineEnd) then
for i := nColBeg+1 to nColEnd do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine > nLineBeg) and (nCurLine < nLineEnd) then
for i := 1 to Length(cLineText) do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine = nLineBeg) and (nLineBeg < nLineEnd) then
for i := nColBeg+1 to Length(cLineText) do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

if (nCurLine = nLineEnd) and (nLineBeg < nLineEnd) then
for i := 1 to nColEnd do begin
aCharsColor[i-1].FontColor := HilightFont;
aCharsColor[i-1].BckColor := HilightBack;
end; { for }

end; { if }

cCurWord := cLineText[1];
LastFont := aCharsColor[0].FontColor;
LastBck := aCharsColor[0].BckColor;
LastUnderline := aCharsColor[0].Underline;

if Length(cLineText) = 1 then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy));
end; { if }

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

end; { if }

for i := 2 to Length(cLineText) do begin

t := cLineText[i];

if (LastFont <> aCharsColor[i-1].FontColor) or
(LastBck <> aCharsColor[i-1].BckColor) or
(LastUnderline <> aCharsColor[i-1].Underline) then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy))
end; { if }

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

if aCharsColor[i-2].Underline then
DrawUnderline(oCanvas, px, rTxtRect.Top+rSize.cy, px+rSize.cx);

Inc(px, rSize.cx);

cCurWord := '';
LastBck := aCharsColor[i-1].BckColor;
LastFont := aCharsColor[i-1].FontColor;
LastUnderline := aCharsColor[i-1].Underline;

end; { if }

cCurWord := cCurWord+cLineText[i];

if px > rTxtRect.Right then Break;

if i = Length(cLineText) then begin

rSize := TextExtent(cCurWord);
SetBkMode(Handle, TRANSPARENT);

if LastBck <> Self.Color then begin
Brush.Color := LastBck;
FillRect(Rect(px, rTxtRect.Top, px+rSize.cx, rTxtRect.Top+rSize.cy))

end;

Font := Self.Font;
Font.Color := LastFont;
TextOut(px, rTxtRect.Top, cCurWord);

if aCharsColor[i-1].Underline then
DrawUnderline(oCanvas, px, rTxtRect.Top+rSize.cy, px+rSize.cx);

end; { if }

end; { for }

SendMessage(Self.Handle,EM_GETRECT,0,LPARAM(@TxtOffset));
Brush.Color := Self.Color;
FillRect(Rect(0, rTxtRect.Top, TxtOffset.Left, rTxtRect.Top+rSize.cy))

end; { with }


finally
SetLength(aCharsColor, 0);
end; { try }

end; { PaintLine }

procedure TColorEdit.WMPaint(var Message :TWMPaint);
var
PS :TPaintStruct;
psRect :TRect;
DC, slDC :HDC;
hbmNew :HBITMAP;
hbmOld :HBITMAP;
oCanvas,
sCanvas :TCanvas;
i :Integer;
rSize :TSize;
nMax :Integer;
nLineFirst :Integer;
nLineLast :Integer;
nLineCount :Integer;
cLine :String;

begin
// inherited;
if (FStartCaretPos.cx = 0) and (Text <> '') and (Alignment = taLeftJustify) then
FStartCaretPos := GetTextStart(0);

if NOT WordWrap then begin
slDC := GetDC(Self.Handle);
psRect := Self.ClientRect;
DC := CreateCompatibleDC(slDC);
hbmNew := CreateCompatibleBitmap(slDC, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);
sCanvas := TCanvas.Create;
sCanvas.Handle := DC;
sCanvas.Font := Self.Font;
sCanvas.Brush.Color := Self.Color;
sCanvas.FillRect(Self.ClientRect);
FLineCaretPos := GetTextStart(1);
cLine := TextFromLine(1); //Lines[i];
if cLine = '' then cLine :=' ';
rSize := sCanvas.TextExtent(cLine);
PaintLine(sCanvas, cLine, 1, Rect(FLineCaretPos.cx, FLineCaretPos.cy, Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy));
BitBlt(slDC, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
ReleaseDC(Self.Handle,slDC);
DeleteDC(slDC);
sCanvas.Free;
BeginPaint(Handle, PS);
EndPaint(Handle, PS);
end
else begin

BeginPaint(Handle, PS);

psRect := Self.ClientRect;
DC := CreateCompatibleDC(ps.hdc);
hbmNew := CreateCompatibleBitmap(PS.hdc, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);

oCanvas := TCanvas.Create;
try

oCanvas.Handle := DC;
oCanvas.Font := Self.Font;

with oCanvas do begin

if WordWrap then begin
nLineCount := SendMessage(Self.Handle, EM_GETLINECOUNT,0,0);
nMax := TopLine()+VisibleLines();
if nMax > nLineCount then nMax := nLineCount;
end
else
nMax := 0;

Brush.Color := Self.Color;
FillRect(Self.ClientRect);

rSize := TextExtent(' ');

if GetForegroundWindow() = Self.Parent.Handle then begin
nLineFirst := TopLine() + (PS.rcPaint.Top div rSize.cy);
nLineLast := TopLine() + (PS.rcPaint.Bottom div rSize.cy);
end else begin
nLineFirst := TopLine();
nLineLast := nMax;
end; { if }

for i := nLineFirst to nLineLast do begin

FLineCaretPos := GetTextStart(i);
cLine := TextFromLine(i); //Lines[i];
if cLine = '' then cLine :=' ';
rSize := TextExtent(cLine);

if FLineCaretPos.cy+rSize.cy <= psRect.Bottom then
PaintLine(oCanvas, cLine, i+1, Rect(FLineCaretPos.cx, FLineCaretPos.cy, Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy))

end; { for }

end; { with }

finally
BitBlt(PS.hdc, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
EndPaint(Handle, PS);
oCanvas.Free;
end; { try }
end;

end; { WMPaint }

procedure TColorEdit.WMPrintClient(var Message :TWMPaint);
var
PS :TPaintStruct;
psRect :TRect;
DC :HDC;
hbmNew :HBITMAP;
hbmOld :HBITMAP;
oCanvas :TCanvas;
i :Integer;
rSize :TSize;
nMax :Integer;
nLineFirst :Integer;
nLineLast :Integer;
cLine :String;

begin

BeginPaint(Handle, PS);

psRect := Self.ClientRect;
DC := CreateCompatibleDC(ps.hdc);
hbmNew := CreateCompatibleBitmap(PS.hdc, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top);
hbmOld := SelectObject(DC, hbmNew);

oCanvas := TCanvas.Create;
try

oCanvas.Handle := DC;
oCanvas.Font := Self.Font;

with oCanvas do begin

if WordWrap then begin
nMax := TopLine()+VisibleLines();
if nMax > SendMessage(Handle, EM_GETLINECOUNT,0,0) then nMax := SendMessage(Handle, EM_GETLINECOUNT,0,0);
end
else
nMax := 0;

Brush.Color := Self.Color;
FillRect(Self.ClientRect);
rSize := TextExtent(' ');

if GetForegroundWindow=Self.Parent.Handle then begin
nLineFirst := TopLine() + (PS.rcPaint.Top div rSize.cy);
nLineLast := TopLine() + (PS.rcPaint.Bottom div rSize.cy);
end else begin
nLineFirst := TopLine();
nLineLast := nMax;
end; { if }

for i := nLineFirst to nLineLast do begin

FLineCaretPos := GetTextStart(i);
cLine := TextFromLine(i); //Lines[i];
if cLine = '' then cLine := ' ';
rSize := TextExtent(cLine);

if FLineCaretPos.cy+rSize.cy <= psRect.Bottom then
PaintLine(oCanvas, cLine, i+1, Rect(FLineCaretPos.cx, FLineCaretPos.cy,Self.ClientRect.Right, FLineCaretPos.cy+rSize.cy));

end; { for }

end; { with }

finally
BitBlt(PS.hdc, psRect.Left, psRect.Top, psRect.Right - psRect.Left, psRect.Bottom - psRect.Top, dc, 0, 0, SRCCOPY);
SelectObject(DC, hbmOld);
DeleteObject(hbmNew);
DeleteDC(DC);
EndPaint(Handle, PS);
oCanvas.Free;
end; { try }

end; { WMPrintClient }

procedure TColorEdit.DrawUnderline(oCanvas :TCanvas; nFromX, nFromY, nToX: Integer);
var
X, J, Y :Integer;

begin
Y:=0;
J:=1;
with oCanvas do
for X := nFromX to nToX do begin
oCanvas.Pixels[X, nFromY+Y] := clRed;
Y := Y+J;
if Y = 2 then J := -1;
if Y = 0 then J := 1;
end;
end; { DrawUnderline }

procedure Register;
begin

RegisterComponents('Samples', [TColorEdit]);

end; { Register }

end.
Автор: StalkerSoftware
Дата сообщения: 23.10.2009 16:31
ShIvADeSt

Цитата:
Пофиксил, добавил обработчик WM_PASTE.

Проверил, все работает нормально.


Цитата:
Не удалось повторить. На всякий случай выкладываю полный код юнита.
Насчет первого пункта - попробуй с последними сорсами проверить, у меня все нормально - ставил 2 контрола рядом и проверял, одинаково ведут себя.

Странно. У меня эта проблема воспроизводится 100%-но.
На всякий случай сделал Демку этой ошибки. Внутри архива exe+код демки+последний ColorEdit.pas+AdvEdit.pas которые я в ней использую. Запустил (попробуй сначала мой exe, а потом уже собери сам).
Вверху AdvEdit, внизу ColorEdit.
В AdvEdit нажимаем 'w' и удерживаем ее, видим как текст разбегается (taCenter) в обе стороны, когда текст заполняет весь контрол он продолжает нормально скролироваться.
В ColorEdit нажимаем 'w' и удерживаем ее, видим как текст разбегается (taCenter) в обе стороны, когда текст заполняет весь контрол он исчезает.
Автор: ShIvADeSt
Дата сообщения: 27.10.2009 08:33
StalkerSoftware

Цитата:
Странно. У меня эта проблема воспроизводится 100%-но.

Прикол в том, что дома у меня эта проблема воспроизводится, но дома у меня редко бывает время покодить, а вот на работе повторить не получается. Трабла в этом куске

Код:
if Res > 0 then begin
Result.cx := LoWord(Res);
if Result.cx>Self.ClientRect.Right then Result.cx:=Result.cx-65535-FStartCaretPos.cx;
Result.cy := HiWord(Res);
end; { if }
Автор: StalkerSoftware
Дата сообщения: 27.10.2009 14:12
ShIvADeSt

Цитата:
Дома стоит WinXP SP3, на работе SP2 причем англ.
У меня на работе есть и XP SP3 (плюс все хот-фиксы безопасности) и XP SP2 (без хот-фиксов вообще), обе русские. Ошибка воспроизводится на обоих компах.
То есть получается, что дело либо в языке OS (что весьма странно), либо в каком то доп. софте, который на это влияет.


Цитата:
Когда дома будет время попробую это как нить обработать.
Хорошо, буду ждать.
Автор: MandrataPupa
Дата сообщения: 31.10.2010 21:15
Позвольте чайнику задать вопрос.
Я давно и безуспешно искал исходники какого-нибудь текстового редактора, умеющего раскрашивать текст. Вот нашёл, наконец, эту тему, установил у себя последнюю версию компонента ColorMemo (огромное спасибо разработчикам) и начал пробовать приспосабливать под свои нужды. А требуется мне, чтобы весь текст от ";" до конца строки считался бы комментарием и красился в соответствующий текст. Дык вот, загвоздка как раз в том, как внутри TForm1.ColorMemo1CheckWord отловить конец строки. Ну, или, хотя-бы, момент перехода на следующую строку.
Понимаю, что данный форум и, в том числе, эта тема не для подобных глупых вопросов. Но, если подскажите, буду очень благодарен. А если пошлёте - тоже не обижусь.
Автор: ShIvADeSt
Дата сообщения: 01.11.2010 03:22
Поиск #10 #13 либо обоих вместе, разные редакторы (программы) по разному метят конец строки, перевод на новую строку.

Страницы: 123456

Предыдущая тема: Delphi Инвертировать изображение на экране


Форум Ru-Board.club — поднят 15-09-2016 числа. Цель - сохранить наследие старого Ru-Board, истории становления российского интернета. Сделано для людей.