[Examples] Count of characters and words

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

Post by jonjon »

Are the character, paragraph and line count functions tread-safe ? Thanks in advance.
Sergey Tkachenko
Site Admin
Posts: 17564
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

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: 467
Joined: Sat Aug 27, 2005 4:19 pm

Post by jonjon »

Thank you Sergey.
Sergey Tkachenko
Site Admin
Posts: 17564
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

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 »

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 »

Any suggestions?
Sergey Tkachenko
Site Admin
Posts: 17564
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

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 »

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

Post by Marsianin »

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 »

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: 17564
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

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 »

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: 17564
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

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 »

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

Post by Petko »

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