Page 1 of 2

[Example] Modification of "Search and Mark" demo

Posted: Tue Sep 06, 2005 5:18 pm
by Sergey Tkachenko
"Search & Mark" - the last subdemo of the demo located in Demos\Delphi\Multidemo\.
It searches for the given word in text and marks all its occurences by applying the specified text style:

Code: Select all

rve.SetSelectionBounds(0,rve.GetOffsBeforeItem(0), 0, rve.GetOffsBeforeItem(0));
while rve.SearchText(txt.Text, [rvseoDown]) do
  rve.ApplyTextStyle(sncomMarked);
It's good if you have only a fixed set of styles (like in this demo: two styles - for normal text and for marked text). But what if you need to mark text in arbitrary document? ApplyTextStyle ignores all attributes of the selected text, so it may have different font size and name after applying. The solution is using ApplyStyleConversion instead of ApplyTextStyle:

Code: Select all

rve.SetSelectionBounds(0,rve.GetOffsBeforeItem(0), 0, rve.GetOffsBeforeItem(0));
while rve.SearchText(txt.Text, [rvseoDown]) do
  rve.ApplyStyleConversion(0);

// making the selected text white-on-green,
// other font attributes are not changed
procedure TfrmDemo7.rveStyleConversion(Sender: TCustomRichViewEdit;
  StyleNo, UserData: Integer; AppliedToText: Boolean;
  var NewStyleNo: Integer);
var FontStyle: TFontInfo;
begin
  FontStyle := TFontInfo.Create(nil);
  FontStyle.Assign(Sender.Style.TextStyles[StyleNo]);
  FontStyle.BackColor := clGreen;
  FontStyle.Color     := clWhite;
  NewStyleNo := Sender.Style.TextStyles.FindSuchStyle(StyleNo, FontStyle,
    RVAllFontInfoProperties);
  if NewStyleNo<0 then begin
    NewStyleNo := Sender.Style.TextStyles.Count;
    Sender.Style.TextStyles.Add;
    Sender.Style.TextStyles[NewStyleNo].Assign(FontStyle);
    Sender.Style.TextStyles[NewStyleNo].Standard := False;
  end;
end;

Posted: Tue Feb 28, 2006 8:40 pm
by Sergey Tkachenko
And a new version of code marking all occurences of the given string in text.
It is:
1) very fast
2) works even in TRichView.
So this is an ideal function for applications which need to display multiple searching results.

2008-Dec-11: this code is removed because it obsolete. Use MarkSearch.pas from RichViewActions, see below.

This procedure cannot be undone. If you call it for TRichViewEdit, call RichViewEdit.ClearUndo.

Posted: Mon May 29, 2006 6:35 pm
by Pieter E.
Is there a C++ example available of this last post?

Thanks.

Posted: Sat Jun 03, 2006 9:20 am
by Sergey Tkachenko
You can create a pascal unit with this function and include it in C++Builder project.

Posted: Thu Jun 15, 2006 12:50 pm
by Sergey Tkachenko
Code of the last example is modified (LastPos implementation is changed)

Posted: Tue Nov 21, 2006 10:11 am
by syntetic
I'm not familiar Delphi. Please, explain me how to make it work with unicode strings, WideString in C++Builder?

Posted: Wed Nov 22, 2006 5:39 am
by mamouri
syntetic,

For unicode version of this function check here: http://www.trichview.com/forums/viewtopic.php?t=1383

In C++ Builder you can make a pas unit file and include it in your project. There is no need to convert it to C++ Builder

Thank you

Posted: Thu Nov 23, 2006 1:47 pm
by Sergey Tkachenko
The link is deleted, because newer version is included in RichViewActions

This unit contains new functions for fast substring highlighting:

Code: Select all

{
  The functions mark all occurences of s in RVData with (Color, BackColor).
  If RVData is nil (or omitted), rv.RVData is used.
  Both functions search both in Unicode and ANSI text items.
  Options:
    WholeWords (words are defined as characters between two characters from
      rv.Delimiters)
    IgnoreCase (for Unicode text, works only on WinNT-based systems
      (Win2000, WinXP...))
  Return value:
    number of marks. If it is positive, call rv.Format.
    If you call this function for TRichViewEdit, call ClearUndo.
}


function MarkSubStringA(const s: String;
  Color, BackColor: TColor; IgnoreCase, WholeWords: Boolean; rv: TCustomRichView;
  RVData: TCustomRVData=nil): Integer;
function MarkSubStringW(const s: WideString;
  Color, BackColor: TColor; IgnoreCase, WholeWords: Boolean; rv: TCustomRichView;
  RVData: TCustomRVData=nil): Integer;
To use in C++Builder, just include this unit in your project.

The following improvements were made comparing to the old code:
- supporting WholeWords and IgnoreCase options;
- for text in RichView, no conversion from Unicode to ANSI (or vice versa) is performed, so no characters are lost;
- not only String, but also WideString substring parameter.

Posted: Wed May 02, 2007 6:16 pm
by Sergey Tkachenko
Updated. New (marked) items retain tag of the original text item.

Note: this unit is included in RichViewActions. If you use RichViewActions, copy this unit over the RichViewActions' one.

Posted: Thu Jul 07, 2011 12:45 pm
by Petko
Sergey, how can we scroll to the first marked string?

Posted: Sun Jul 10, 2011 10:15 am
by Sergey Tkachenko
I am afraid it's not possible with the current implementation, because you cannot distinguish the original text of these colors and the marked string. But it is possible to scroll to the first text of these colors.
Do you want only to scroll, or move the caret to this string?

Posted: Sat Jul 23, 2011 12:52 pm
by Petko
It is OK for me, because I use unique color for the search results.

Yes, I want to move the caret there..

Posted: Sat Sep 17, 2011 9:40 am
by Sergey Tkachenko
Sorry for a so long delay.
If you are still interested, this is the code for scrolling to the first occurrence of the text having the specified colors:

Code: Select all

function FindColoredText(RVData: TCustomRVData; ForeColor, BackColor: TColor;
  var ResRVData: TCustomRVData; var ResItemNo: Integer): Boolean;
var i,r,c: Integer;
    Table: TRVTableItemInfo;
    TextStyle: TFontInfo;
begin
  Result := False;
  for i := 0 to RVData.ItemCount-1 do
    if RVData.GetItemStyle(i)>=0 then begin
      TextStyle := RVData.GetRVStyle.TextStyles[RVData.GetItemStyle(i)];
      Result := (TextStyle.Color=ForeColor) and (TextStyle.BackColor=BackColor);
      if Result then begin
        ResRVData := RVData;
        ResItemNo := i;
        exit;
      end;
      end
    else if RVData.GetItemStyle(i)=rvsTable then begin
      Table := TRVTableItemInfo(RVData.GetItem(i));
      for r := 0 to table.RowCount-1 do
        for c := 0 to table.ColCount-1 do
          if Table.Cells[r,c]<>nil then begin
            Result := FindColoredText(table.Cells[r,c].GetRVData,
              ForeColor, BackColor, ResRVData, ResItemNo);
            if Result then
              exit;
          end;
    end;
end;

function ScrollToColoredText(rv: TCustomRichView;
  ForeColor, BackColor: TColor): Boolean;
var X0, Y0, X, Y, ItemNo: Integer;
    RVData: TCustomRVData;
begin
  Result := FindColoredText(rv.RVData, ForeColor, BackColor, RVData, ItemNo);
  if not Result then
    exit;
  TCustomRVFormattedData(RVData).GetOriginEx(X0, Y0);
  TCustomRVFormattedData(RVData).GetItemCoords(ItemNo, X, Y);
  rv.ScrollTo(Y0+Y);
end;

Posted: Sat Sep 17, 2011 10:02 am
by Sergey Tkachenko
The implementation of ScrollToColoredText above does not take a cell rotation (new feature, available for registered users) into account.
The following implementation does it:

Code: Select all

function ScrollToColoredText(rv: TCustomRichView;
  ForeColor, BackColor: TColor): Boolean;
var ItemNo: Integer;
    RVData: TCustomRVData;
    R: TRect;
begin
  Result := FindColoredText(rv.RVData, ForeColor, BackColor, RVData, ItemNo);
  if not Result then
    exit;
  rv.GetItemCoordsEx(TCustomRVFormattedData(RVData), ItemNo, 0, True, R);
  rv.ScrollTo(R.Top);
end;
GetItemCoordsEx is a new method, it is not available yet, it will be included in the next update.

Posted: Wed Sep 21, 2011 8:06 pm
by Petko
Yes, Sergey, I am still interested. Thank you very much!