[Example] Modification of "Search and Mark" demo

Demos, code samples. Only questions related to the existing topics are allowed here.
Sergey Tkachenko
Site Admin
Posts: 17602
Joined: Sat Aug 27, 2005 10:28 am
Contact:

[Example] Modification of "Search and Mark" demo

Post 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;
Sergey Tkachenko
Site Admin
Posts: 17602
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post 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.
Last edited by Sergey Tkachenko on Thu Dec 11, 2008 3:14 pm, edited 3 times in total.
Pieter E.
Posts: 835
Joined: Sun Aug 28, 2005 9:34 am

Post by Pieter E. »

Is there a C++ example available of this last post?

Thanks.
Sergey Tkachenko
Site Admin
Posts: 17602
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

You can create a pascal unit with this function and include it in C++Builder project.
Sergey Tkachenko
Site Admin
Posts: 17602
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

Code of the last example is modified (LastPos implementation is changed)
syntetic
Posts: 1
Joined: Mon Nov 20, 2006 7:43 pm

Post by syntetic »

I'm not familiar Delphi. Please, explain me how to make it work with unicode strings, WideString in C++Builder?
mamouri
Posts: 63
Joined: Sat Aug 19, 2006 5:06 am

Post 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
Sergey Tkachenko
Site Admin
Posts: 17602
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post 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.
Last edited by Sergey Tkachenko on Tue Jul 01, 2008 2:02 pm, edited 1 time in total.
Sergey Tkachenko
Site Admin
Posts: 17602
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post 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.
Petko
Posts: 174
Joined: Tue Sep 06, 2005 12:42 pm

Post by Petko »

Sergey, how can we scroll to the first marked string?
Sergey Tkachenko
Site Admin
Posts: 17602
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post 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?
Petko
Posts: 174
Joined: Tue Sep 06, 2005 12:42 pm

Post by Petko »

It is OK for me, because I use unique color for the search results.

Yes, I want to move the caret there..
Sergey Tkachenko
Site Admin
Posts: 17602
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post 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;
Sergey Tkachenko
Site Admin
Posts: 17602
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post 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.
Petko
Posts: 174
Joined: Tue Sep 06, 2005 12:42 pm

Post by Petko »

Yes, Sergey, I am still interested. Thank you very much!
Post Reply