[Examples] Count of characters and words

Demos, code samples. Only questions related to the existing topics are allowed here.
jonjon
Posts: 276
Joined: Sat Aug 27, 2005 4:19 pm

Post by jonjon » Wed Nov 24, 2010 4:27 pm

Are the character, paragraph and line count functions tread-safe ? Thanks in advance.

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

Post by Sergey Tkachenko » Wed Nov 24, 2010 5:25 pm

Interesting question.
Characters, paragraphs - yes.
As for word enumeration, as I can see, it is safe too, with one possible problem. It uses TRVWordEnumerator class that was primarily created for spell checking. When called for TRichViewEdit, it prevents its form from closing by assigning its OnCloseQuery event and restoring it after its work.
If several word enumerators will work at the same time in threads, it may happen that the original OnCloseQuery will not be restored.

jonjon
Posts: 276
Joined: Sat Aug 27, 2005 4:19 pm

Post by jonjon » Thu Nov 25, 2010 9:44 am

Thank you Sergey.

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

Post by Sergey Tkachenko » Mon Apr 11, 2011 4:39 pm

Calculating paragraph counts in the way like MS Word: empty paragraphs are not taken into account.
Paragraphs are considered empty if they do not contain any of:
- non-empty text items (at least one non-space character);
- bullet or numbering (text);
- non-empty labelitem (label/number/footnote/endnote)
As you can see, paragraph containing only pictures, spaces and tabs are considered empty.
For tables, the count of paragraphs equals to sum of counts of paragraphs in all its cells. The table itself is not counted as a paragraph.

Code: Select all

function GetParaCount(RVData: TCustomRVData): Integer;
var i, r, c, StyleNo: Integer;
    table: TRVTableItemInfo;
    EmptyPara: Boolean;
    ListNo, ListLevel, StartFrom: Integer;
    Reset: Boolean;
begin
  Result := 0;
  EmptyPara := True;
  for i := 0 to RVData.ItemCount-1 do begin
    if RVData.IsParaStart(i) then begin
      if not EmptyPara then
        inc(Result);
      EmptyPara := True;
    end;
    StyleNo := RVData.GetItemStyle(i);
    if StyleNo=rvsListMarker then begin
      RVData.GetListMarkerInfo(i,ListNo, ListLevel, StartFrom, Reset);
      if (ListNo>=0) and (ListNo<RVData.GetRVStyle.ListStyles.Count) and
         (ListLevel>=0) and (ListLevel<RVData.GetRVStyle.ListStyles[ListNo].Levels.Count) and
         RVData.GetRVStyle.ListStyles[ListNo].Levels[ListLevel].UsesFont then
        EmptyPara := False
      end
    else if RVData.GetItem(i) is TRVLabelItemInfo then begin
      if Trim(TRVLabelItemInfo(RVData.GetItem(i)).Text)<>'' then
      end
    else if StyleNo>=0 then begin
      if Trim(RVData.GetItemText(i))<>'' then
        EmptyPara := False;
      end
    else if StyleNo=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
            inc(Result, GetParaCount(table.Cells[r,c].GetRVData));
    end;
  end;
  if not EmptyPara then
    inc(Result);
end;
Use:

Code: Select all

  lblParaCount.Caption := IntToStr(GetParaCount(RichViewEdit1.RVData));

Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin » Tue Jun 07, 2011 6:36 am

Ok, everything works fine here, i can count words/characters/paragraphs but still have some quiestions:

1. When counting document lines globally (including tables) how can I check cursor position globally too? Because RVE.GetCurrentLineCol(line,col); returns only local line number.

2. Using GetLineCount(RVE.RVData); from your example inside RVE.OnCaretMove generates errors. How can I avoid this checking cursor position when it moves?

Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin » Thu Jun 16, 2011 7:39 am

Any suggestions?

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

Post by Sergey Tkachenko » Thu Jun 16, 2011 4:22 pm

May be you can try to use a PostMessage from OnCaretMove to calculate line/col outside of this event?

Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin » Fri Jun 17, 2011 11:17 pm

No idea :roll:

Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin » Thu Jun 23, 2011 1:03 am

So how can I get cursor/current line globally?
Your example shows how to count lines including tables but when you get cursor inside the table GetCurrentLineCol(line,col) will give you local in-cell line number.

Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin » Sat Jul 02, 2011 1:24 am

I want to know global current line number.
How can I check if we got cursor in the current line in this routine:

Code: Select all

...
if RVData.GetItemStyle(RVData.DrawItems[i].ItemNo)=rvsTable then begin
  table := TRVTableItemInfo(RVData.GetItem(RVData.DrawItems[i].ItemNo));
  for r := 0 to table.RowCount-1 do
    for c := 0 to table.ColCount-1 do
      if table.Cells[r,c]<>nil then
        inc(Result, GetLineCount(TCustomRVFormattedData(table.Cells[r,c].GetRVData)));
...

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

Post by Sergey Tkachenko » Sun Jul 03, 2011 12:46 pm

Code: Select all

function GetCurrentLine(rve: TCustomRichViewEdit): Integer;
var
  CurRVData: TCustomRVFormattedData;
  CurDItemNo: Integer;
  CurDOffs: Integer;

  function DoGetCurrentLine(RVData: TCustomRVFormattedData; var LineNo: Integer): Boolean;
  var i,r,c: Integer;
     table: TRVTableItemInfo;
  begin
    Result := False;
    for i := 0 to RVData.DrawItems.Count-1 do begin
      if RVData.DrawItems[i].FromNewLine then
        inc(LineNo);
      Result := (RVData=CurRVData) and (i=CurDItemNo);
      if Result then
        exit;
      if RVData.GetItemStyle(RVData.DrawItems[i].ItemNo)=rvsTable then begin
        table := TRVTableItemInfo(RVData.GetItem(RVData.DrawItems[i].ItemNo));
        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 := DoGetCurrentLine(TCustomRVFormattedData(table.Cells[r,c].GetRVData), LineNo);
              if Result then
                exit;
            end;
      end;
    end;
  end;

begin
  CurRVData := rve.TopLevelEditor.RVData;
  CurRVData.Item2DrawItem(rve.TopLevelEditor.CurItemNo,
    rve.TopLevelEditor.OffsetInCurItem, CurDItemNo, CurDOffs);
  Result := 0;
  DoGetCurrentLine(rve.RVData, Result);
end;
In this procedure, lines are counted in RichViewEdit's way: a table itself is treated as a line (the next line after the table is the first line in the first cell).

Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin » Mon Jul 04, 2011 2:45 am

But the table may contain several lines and in your first example it counts them.
So I can have just one table with 100 lines and get 100 lines result in your line count example. And this is what I need. Just want to know cursor line number according all my 100 lines (now I'm getting 100 lines total and current line is some 3rd line in a table cell, but must include all previous lines)

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

Post by Sergey Tkachenko » Tue Jul 05, 2011 7:59 am

Yes, this GetCurrentLine calculates the current line using the same method as GetLineCount.
These functions are compatible.

Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin » Tue Jul 05, 2011 8:36 am

Ok, thanks. I didn't see it's another function.
I'm going to join GetCurrentLine and GetLineCount in one function to not do same job twice.

Petko
Posts: 166
Joined: Tue Sep 06, 2005 12:42 pm

Post by Petko » Thu Jan 26, 2012 3:36 pm

Sergey, how can we get the same statistics (characters, words and lines) for the selected text instead for the whole document (for paragraphs there is already as I see)?

Post Reply