Page 1 of 1
[Example] Smart indent
Posted: Tue Sep 25, 2007 10:13 am
by Sergey Tkachenko
Smart indent, programming style
When user presses Enter key, this code adds space characters at the beginning of new paragraph (as many spaces as at the beginning of the current paragraph).
Code: Select all
// Returns count of space characters at the beginning of the current paragraph
function GetLeadingSpacesCount(rve: TCustomRichViewEdit): Integer;
var StartItemNo, ItemNo, i: Integer;
s: String;
begin
rve := rve.TopLevelEditor;
ItemNo := rve.CurItemNo;
while not rve.IsParaStart(ItemNo) do
dec(ItemNo);
Result := 0;
StartItemNo := ItemNo;
while ItemNo<rve.ItemCount do begin
if (ItemNo>StartItemNo) and rve.IsParaStart(ItemNo) then
exit;
if rve.GetItemStyle(ItemNo)<0 then
exit;
s := rve.GetItemText(ItemNo);
for i := 1 to Length(s) do
if s[i]=' ' then
inc(Result)
else
exit;
inc(ItemNo);
end;
end;
// Returns string consisting of Count space characters
function GetSpaces(Count: Integer): String;
var i: Integer;
begin
SetLength(Result, Count);
for i := 1 to Count do
Result[i] := ' ';
end;
// OnKeyDown, indenting
procedure TForm1.RichViewEdit1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key=VK_RETURN then begin
RichViewEdit1.InsertText(#13+GetSpaces(GetLeadingSpacesCount(RichViewEdit1)));
Key := 0;
end;
end;
Possibility to improve: when pressing Enter in the middle of paragraph, calculate how many space characters are to the right of the caret (so they will be at the beginning of the new paragraph) and take them into account.
2008-Dec-11: Updated for compatibility with TRichView 11 and Delphi 2009
Posted: Tue Oct 23, 2007 8:43 am
by Sergey Tkachenko
Smart indent/unindent, MS Word style
This code changes FirstIndent and LeftIndent of paragraph, if Tab/Backspace is pressed when the caret is at the beginning of paragraph. First, it changes FirstIndent. Next, if FirstIndent is already changed, it changes LeftIndent.
This code changes indents only if the caret is at the beginning of non-empty paragraph.
When the caret is at the beginning of a bulleted/numbered paragraph, Tab increases the list level.
This code uses its own procedures for OnParaStyleConversion event. Of course, you can integrate this code in existing event handler instead. But in this way, it's easy to integrate this code in existing application, even if it uses RichViewActions.
Code: Select all
// Procedure for OnParaStyleConversion event.
// Changes paragraph's FirstIndent to UserData
procedure TForm3.ChangeFirstIndentConversion(Sender: TCustomRichViewEdit;
StyleNo, UserData: Integer; AppliedToText: Boolean;
var NewStyleNo: Integer);
var ParaStyle: TParaInfo;
begin
ParaStyle := TParaInfo.Create(nil);
ParaStyle.Assign(Sender.Style.ParaStyles[StyleNo]);
ParaStyle.FirstIndent := UserData;
ParaStyle.Standard := False;
NewStyleNo := Sender.Style.ParaStyles.FindSuchStyle(StyleNo, ParaStyle,
RVAllParaInfoProperties);
if NewStyleNo<0 then begin
Sender.Style.ParaStyles.Add.Assign(ParaStyle);
NewStyleNo := Sender.Style.ParaStyles.Count-1;
end;
ParaStyle.Free;
end;
// Procedure for OnParaStyleConversion event.
// Changes paragraph's LeftIndent to UserData
// If UserData=0, resets FirstIndent as well.
procedure TForm3.ChangeLeftIndentConversion(Sender: TCustomRichViewEdit;
StyleNo, UserData: Integer; AppliedToText: Boolean;
var NewStyleNo: Integer);
var ParaStyle: TParaInfo;
begin
ParaStyle := TParaInfo.Create(nil);
ParaStyle.Assign(Sender.Style.ParaStyles[StyleNo]);
ParaStyle.LeftIndent := UserData;
if ParaStyle.LeftIndent=0 then
ParaStyle.FirstIndent := 0;
ParaStyle.Standard := False;
NewStyleNo := Sender.Style.ParaStyles.FindSuchStyle(StyleNo, ParaStyle,
RVAllParaInfoProperties);
if NewStyleNo<0 then begin
Sender.Style.ParaStyles.Add.Assign(ParaStyle);
NewStyleNo := Sender.Style.ParaStyles.Count-1;
end;
ParaStyle.Free;
end;
function TForm3.ChangeIndent(rve: TCustomRichViewEdit;
Step, Max: Integer): Boolean;
var OldParaStyleConversion: TRVStyleConversionEvent;
FirstIndent, LeftIndent: Integer;
ListNo, ListLevel, StartFrom: Integer;
Reset: Boolean;
begin
Result := False;
OldParaStyleConversion := rve.OnParaStyleConversion;
try
rve := rve.TopLevelEditor;
if rve.SelectionExists then
exit;
if (rve.OffsetInCurItem<=rve.GetOffsBeforeItem(rve.CurItemNo)) and
(rve.CurItemNo>0) and (rve.GetItemStyle(rve.CurItemNo-1)=rvsListMarker) then begin
// changing list level
rve.GetListMarkerInfo(rve.CurItemNo, ListNo, ListLevel, StartFrom, Reset);
if (ListNo>=0) and (ListNo<rve.Style.ListStyles.Count) then
if (Step>0) and (ListLevel+1<rve.Style.ListStyles[ListNo].Levels.Count) then begin
rve.ChangeListLevels(+1);
Result := True;
end
else if (Step<0) and (ListLevel>0) then begin
rve.ChangeListLevels(-1);
Result := True;
end;
exit;
end;
if (rve.OffsetInCurItem>rve.GetOffsBeforeItem(rve.CurItemNo)) or
not rve.IsParaStart(rve.CurItemNo) then
exit; // not at the beginning of paragraph
if (rve.OffsetInCurItem>=rve.GetOffsAfterItem(rve.CurItemNo)) and
((rve.CurItemNo=rve.ItemCount-1) or rve.IsParaStart(rve.CurItemNo)) then
exit; // empty paragraph, exiting
FirstIndent := rve.Style.ParaStyles[rve.GetItemPara(rve.CurItemNo)].FirstIndent;
LeftIndent := rve.Style.ParaStyles[rve.GetItemPara(rve.CurItemNo)].LeftIndent;
if Step>0 then begin
if FirstIndent=0 then begin
rve.OnParaStyleConversion := ChangeFirstIndentConversion;
rve.ApplyParaStyleConversion(Step);
Result := True;
end
else begin
inc(LeftIndent, Step);
if LeftIndent>Max then
LeftIndent := Max;
if LeftIndent>rve.Style.ParaStyles[rve.GetItemPara(rve.CurItemNo)].LeftIndent then begin
rve.OnParaStyleConversion := ChangeLeftIndentConversion;
rve.ApplyParaStyleConversion(LeftIndent);
Result := True;
end;
end
end
else begin
if FirstIndent>0 then begin
rve.OnParaStyleConversion := ChangeFirstIndentConversion;
rve.ApplyParaStyleConversion(0);
Result := True;
end
else begin
inc(LeftIndent, Step);
if LeftIndent<0 then
LeftIndent := 0;
if (LeftIndent<rve.Style.ParaStyles[rve.GetItemPara(rve.CurItemNo)].LeftIndent) or
(FirstIndent<>0) then begin
rve.OnParaStyleConversion := ChangeLeftIndentConversion;
rve.ApplyParaStyleConversion(LeftIndent);
Result := True;
end;
end;
end;
finally
rve.OnParaStyleConversion := OldParaStyleConversion;
end;
end;
// OnKeyDown event
procedure TForm3.RichViewEdit1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (Key=VK_BACK) and ChangeIndent(TCustomRichViewEdit(Sender), -48, 480) then
Key := 0;
end;
// OnKeyPress event
procedure TForm3.RichViewEdit1KeyPress(Sender: TObject; var Key: Char);
begin
if (Key=#9) and ChangeIndent(TCustomRichViewEdit(Sender), +48, 480) then
Key := #0;
end;
Upd: 2011-Jun-26: changing list levels on Tab
Upd: 2015-Jan-14: changing list levels on Backspace
Posted: Wed Jul 04, 2012 2:23 pm
by jonjon
Sergey,
I've added this code and it seems to be working fine however I'm missing the SHIFT-TAB feature, available in Word, to un-indent.
Can it be easily added to your sample ?
Thanks.
Posted: Thu Jul 05, 2012 11:47 am
by Sergey Tkachenko
This code unindents on BACKSPACE.
To add SHIFT+TAB, change
Code: Select all
// OnKeyPress event
procedure TForm3.RichViewEdit1KeyPress(Sender: TObject; var Key: Char);
var step: Integer;
begin
if Key=#9 then begin
step := 48;
if GetAsyncKeyState(VK_SHIFT)and$8000<>0 then
step := -step;
if ChangeIndent(TCustomRichViewEdit(Sender), step, 480) then
Key := #0;
end;
end;
Posted: Thu Jul 05, 2012 12:17 pm
by jonjon
Thanks Sergey but I can't get it to work on 13.10: TAB is working fine, Shift-Tab/Delete isn't. It looks like rve.IsParaStart is always returning false.
Posted: Fri Jul 13, 2012 8:49 am
by Sergey Tkachenko
Sorry, I cannot reproduce this problem. I added TForm3.ChangeFirstIndentConversion and the code
Code: Select all
procedure TForm3.RichViewEdit1KeyPress(Sender: TObject; var Key: Char);
var step: Integer;
begin
if Key=#9 then begin
step := 48;
if GetAsyncKeyState(VK_SHIFT)and$8000<>0 then
step := -step;
if ChangeIndent(TCustomRichViewEdit(Sender), step, 480) then
Key := #0;
end;
end;
in the ActionTest project, and it works on Tab and Shift+Tab as expected.
Note that this procedure works only in non-empty paragraphs, like in MS Word.
Posted: Tue Jan 13, 2015 11:47 pm
by Rael Bauer
Sergey Tkachenko wrote:Sorry, I cannot reproduce this problem. I added TForm3.ChangeFirstIndentConversion and the code
Code: Select all
procedure TForm3.RichViewEdit1KeyPress(Sender: TObject; var Key: Char);
var step: Integer;
begin
if Key=#9 then begin
step := 48;
if GetAsyncKeyState(VK_SHIFT)and$8000<>0 then
step := -step;
if ChangeIndent(TCustomRichViewEdit(Sender), step, 480) then
Key := #0;
end;
end;
in the ActionTest project, and it works on Tab and Shift+Tab as expected.
If this is to work for Shift+Tab (i.e. un-indent) then shouldn't ChangeIndent function include some code like:
It seems the top part of the function is not handling both cases?
Posted: Wed Jan 14, 2015 10:35 am
by Sergey Tkachenko
You are right, I corrected it.