RVLinear.pas
上传用户:daoqigc
上传日期:2021-04-20
资源大小:2795k
文件大小:17k
源码类别:

RichEdit

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {       RichView                                        }
  4. {       A set of procedures implementing RichEdit-like  }
  5. {       selection (SelStart and SelLength)              }
  6. {                                                       }
  7. {       Copyright (c) Sergey Tkachenko                  }
  8. {       svt@trichview.com                               }
  9. {       http://www.trichview.com                        }
  10. {                                                       }
  11. {*******************************************************}
  12. {
  13.   v1.6:
  14.   new: GetTextRange returns text in the specified range. The returned text
  15.     has exactly RangeLength number of characters, this function is completely
  16.     compatible with other functions in this unit.
  17.     Nontext items (except for tables and tabs) are saved as RVNonTextCharacter).
  18.   v1.5:
  19.   new: RVGetSelectionEx and RVSetSelectionEx stores the selection in the record
  20.     TRVSelection. These functions can store even multicell selection
  21.   v1.4:
  22.   fix: RVGetSelection works with TRichView
  23.   v1.3:
  24.   new: RVCharsPerLineBreak - number of characters per line break
  25.   v1.1:
  26.   chg: linear position is counted from 0, like in RichEdit.
  27.     If you need to count it from 1 (for compatibility reasons), remove the
  28.     dot from the define below.
  29. }
  30. {.$DEFINE RVLIN_STARTFROM1}
  31. {$I RV_Defs.inc}
  32. unit RVLinear;
  33. interface
  34. uses RichView, {$IFNDEF RVDONOTUSERVF}RVEdit, RVTable, {$ENDIF}
  35.   CRVData, CRVFData, RVUni, RVItem;
  36. {$IFNDEF RVDONOTUSERVF}
  37. function RVGetLinearCaretPos(rve: TCustomRichViewEdit): Integer;
  38. procedure RVSetLinearCaretPos(rve: TCustomRichViewEdit; LinearPos: Integer);
  39. {$ENDIF}
  40. procedure RVGetSelection(rv: TCustomRichView; var SelStart, SelLength: Integer);
  41. procedure RVSetSelection(rv: TCustomRichView; SelStart, SelLength: Integer);
  42. function RVGetTextRange(rv: TCustomRichView; RangeStart, RangeLength: Integer): String;
  43. function RVGetTextLength(rv: TCustomRichView): Integer;
  44. const RVCharsPerLineBreak: Integer = 1;
  45.       RVNonTextCharacter: Char = ' ';
  46. {$IFNDEF RVDONOTUSERVF}
  47. type
  48.   TRVSelection = record
  49.     SelStart, SelLength: Integer;
  50.     MultiCell: Boolean;
  51.     StartRow, StartCol, RowOffs, ColOffs: Integer;
  52.   end;
  53. procedure RVGetSelectionEx(rv: TCustomRichView; var Selection: TRVSelection);
  54. procedure RVSetSelectionEx(rv: TCustomRichView; const Selection: TRVSelection);
  55. {$ENDIF}
  56. implementation
  57. uses RVStyle;
  58. function GetAbstractCharCountInItem(item: TCustomRVItemInfo; const text: String): Integer; forward;
  59. function GetAbstractCharCountInRVData(RVData: TCustomRVData): Integer;
  60. var i: Integer;
  61. begin
  62.   Result := 0;
  63.   for i := 0 to RVData.Items.Count-1 do
  64.     inc(Result, GetAbstractCharCountInItem(RVData.GetItem(i), RVData.Items[i]));
  65. end;
  66. {------------------------------------------------------------------------------}
  67. function GetAbstractCharCountInItem(item: TCustomRVItemInfo;
  68.   const text: String): Integer;
  69. var StoreSub: TRVStoreSubRVData;
  70.     SubRVData: TCustomRVData;
  71. begin
  72.   if not item.SameAsPrev then
  73.     Result := RVCharsPerLineBreak
  74.   else
  75.     Result := 0;
  76.   if item.StyleNo>=0 then begin
  77.     inc(Result, RVU_Length(text, item.ItemOptions));
  78.     exit;
  79.   end;
  80.   SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdFirst));
  81.   if SubRVData<>nil then begin
  82.     repeat
  83.       inc(Result, GetAbstractCharCountInRVData(SubRVData.GetRVData));
  84.       SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdNext));
  85.     until SubRVData=nil;
  86.     StoreSub.Free;
  87.     inc(Result, RVCharsPerLineBreak);
  88.     end
  89.   else
  90.     inc(Result);
  91. end;
  92. {------------------------------------------------------------------------------}
  93. function RichViewToLinear(rv: TCustomRichView; CurRVData, RVData: TCustomRVData;
  94.   ItemNo, ItemOffs: Integer; var LinearPos: Integer): Boolean;
  95. var i, SubLinPos: Integer;
  96.     StoreSub: TRVStoreSubRVData;
  97.     SubRVData: TCustomRVData;
  98.     item: TCustomRVItemInfo;
  99. begin
  100.   Result := False;
  101.   LinearPos := 0;
  102.   if CurRVData=RVData then begin
  103.     for i := 0 to ItemNo-1 do
  104.       inc(LinearPos, GetAbstractCharCountInItem(CurRVData.GetItem(i), CurRVData.Items[i]));
  105.     if CurRVData.GetItemStyle(ItemNo)>=0 then begin
  106.       if CurRVData.IsFromNewLine(ItemNo) then
  107.         inc(LinearPos, RVCharsPerLineBreak);
  108.       inc(LinearPos, ItemOffs-1)
  109.       end
  110.     else if ItemOffs>0 then
  111.       inc(LinearPos, GetAbstractCharCountInItem(CurRVData.GetItem(ItemNo), CurRVData.Items[ItemNo]))
  112.     else if CurRVData.IsFromNewLine(ItemNo) then
  113.       inc(LinearPos, RVCharsPerLineBreak);
  114.     Result := True;
  115.     end
  116.   else begin
  117.     for i := 0 to CurRVData.Items.Count-1 do begin
  118.       item := CurRVData.GetItem(i);
  119.       SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdFirst));
  120.       if SubRVData<>nil then begin
  121.         if not item.SameAsPrev then
  122.           inc(LinearPos, RVCharsPerLineBreak);
  123.          repeat
  124.            Result := RichViewToLinear(rv, SubRVData.GetRVData, RVData, ItemNo, ItemOffs, SubLinPos);
  125.            inc(LinearPos, SubLinPos);
  126.            if Result then
  127.              break;
  128.            SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdNext));
  129.          until SubRVData=nil;
  130.          StoreSub.Free;
  131.          if Result then
  132.            exit;
  133.          inc(LinearPos, RVCharsPerLineBreak);
  134.         end
  135.       else
  136.         inc(LinearPos, GetAbstractCharCountInItem(item, CurRVData.Items[i]));
  137.     end;
  138.   end;
  139. end;
  140. {------------------------------------------------------------------------------}
  141. function LinearToRichView(rv: TCustomRichView; CurRVData: TCustomRVData;
  142.   var LinearPos: Integer; var RVData: TCustomRVData;
  143.   var ItemNo, ItemOffs: Integer): Boolean;
  144. var i, SubLinPos: Integer;
  145.     StoreSub: TRVStoreSubRVData;
  146.     SubRVData: TCustomRVData;
  147.     item: TCustomRVItemInfo;
  148. begin
  149.   Result := False;
  150.   for i := 0 to CurRVData.Items.Count-1 do begin
  151.     item := CurRVData.GetItem(i);
  152.     SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdFirst));
  153.     if SubRVData<>nil then begin
  154.       if (i>0) and not item.SameAsPrev then
  155.         dec(LinearPos, RVCharsPerLineBreak);
  156.       if LinearPos=0 then begin
  157.         RVData := CurRVData;
  158.         ItemNo := i;
  159.         ItemOffs := 0;
  160.         Result := True;
  161.         StoreSub.Free;
  162.         exit;
  163.       end;
  164.       repeat
  165.         dec(LinearPos, RVCharsPerLineBreak);
  166.         Result := LinearToRichView(rv, SubRVData.GetRVData, LinearPos, RVData, ItemNo, ItemOffs);
  167.         if Result then
  168.           break;
  169.         SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdNext));
  170.       until SubRVData=nil;
  171.       StoreSub.Free;
  172.       if Result then
  173.         exit;
  174.       dec(LinearPos, RVCharsPerLineBreak);
  175.       if LinearPos=0 then begin
  176.         RVData := CurRVData;
  177.         ItemNo := i;
  178.         ItemOffs := 1;
  179.         Result := True;
  180.         exit;
  181.       end;
  182.       end
  183.     else begin
  184.       SubLinPos := GetAbstractCharCountInItem(item, CurRVData.Items[i]);
  185.       if i=0 then
  186.         dec(SubLinPos, RVCharsPerLineBreak);
  187.       if (SubLinPos>=LinearPos) then begin
  188.         if (i>0) and not item.SameAsPrev then
  189.           dec(LinearPos, RVCharsPerLineBreak);
  190.         RVData := CurRVData;
  191.         ItemNo := i;
  192.         ItemOffs := LinearPos;
  193.         if item.StyleNo>=0 then
  194.           inc(ItemOffs);
  195.         if ItemOffs<RVData.GetOffsBeforeItem(ItemNo) then
  196.           ItemOffs := RVData.GetOffsBeforeItem(ItemNo);
  197.         if ItemOffs>RVData.GetOffsAfterItem(ItemNo) then
  198.           ItemOffs := RVData.GetOffsAfterItem(ItemNo);
  199.         Result := True;
  200.         exit;
  201.       end;
  202.       dec(LinearPos, SubLinPos);
  203.     end;
  204.   end;
  205. end;
  206. {------------------------------------------------------------------------------}
  207. {$IFNDEF RVDONOTUSERVF}
  208. function RVGetLinearCaretPos(rve: TCustomRichViewEdit): Integer;
  209. var tle: TCustomRichViewEdit;
  210. begin
  211.   tle := rve;
  212.   while tle.InplaceEditor<>nil do
  213.     tle := TCustomRichViewEdit(tle.InplaceEditor);
  214.   if tle.CurItemNo<0 then
  215.     Result := 0
  216.   else begin
  217.     RichViewToLinear(rve, rve.RVData, tle.RVData, tle.CurItemNo,
  218.       tle.OffsetInCurItem, Result);
  219.     dec(Result, RVCharsPerLineBreak);
  220.   end;
  221.   {$IFDEF RVLIN_STARTFROM1}
  222.   inc(Result);
  223.   {$ENDIF}
  224. end;
  225. {------------------------------------------------------------------------------}
  226. procedure RVSetLinearCaretPos(rve: TCustomRichViewEdit; LinearPos: Integer);
  227. var RVData: TCustomRVData;
  228.     ItemNo, ItemOffs: Integer;
  229. begin
  230.   {$IFDEF RVLIN_STARTFROM1}
  231.   dec(LinearPos);
  232.   {$ENDIF}
  233.   if LinearToRichView(rve, rve.RVData, LinearPos, RVData, ItemNo, ItemOffs) then begin
  234.     RVData := RVData.Edit;
  235.     TCustomRVFormattedData(RVData).SetSelectionBounds(ItemNo, ItemOffs,
  236.       ItemNo, ItemOffs);
  237.     TCustomRVFormattedData(RVData).Invalidate;
  238.   end;
  239. end;
  240. {$ENDIF}
  241. {------------------------------------------------------------------------------}
  242. procedure RVGetSelection(rv: TCustomRichView; var SelStart, SelLength: Integer);
  243. var ItemNo1, ItemNo2, ItemOffs1, ItemOffs2: Integer;
  244.     RVData: TCustomRVFormattedData;
  245. begin
  246.   RVData := rv.RVData;
  247.   while RVData.GetChosenRVData<>nil do
  248.     RVData := TCustomRVFormattedData(RVData.GetChosenRVData);
  249.   RVData.GetSelectionBoundsEx(ItemNo1, ItemOffs1, ItemNo2, ItemOffs2, False);
  250.   if ItemNo1<0 then begin
  251.     {$IFDEF RVLIN_STARTFROM1}
  252.     SelStart := 1;
  253.     {$ELSE}
  254.     SelStart := 0;
  255.     {$ENDIF}
  256.     SelLength := 0;
  257.     end
  258.   else begin
  259.     RichViewToLinear(rv, rv.RVData, RVData, ItemNo1, ItemOffs1, SelStart);
  260.     RichViewToLinear(rv, rv.RVData, RVData, ItemNo2, ItemOffs2, SelLength);
  261.     dec(SelStart, RVCharsPerLineBreak);
  262.     dec(SelLength, RVCharsPerLineBreak);
  263.     SelLength := SelLength-SelStart;
  264.     {$IFDEF RVLIN_STARTFROM1}
  265.     inc(SelStart);
  266.     {$ENDIF}
  267.   end;
  268. end;
  269. {------------------------------------------------------------------------------}
  270. procedure RVSetSelection(rv: TCustomRichView; SelStart, SelLength: Integer);
  271. var ItemNo1, ItemNo2, ItemOffs1, ItemOffs2: Integer;
  272.   RVData1, RVData2: TCustomRVData;
  273. begin
  274.   {$IFDEF RVLIN_STARTFROM1}
  275.   dec(SelStart);
  276.   {$ENDIF}
  277.   inc(SelLength, SelStart);
  278.   if LinearToRichView(rv, rv.RVData, SelStart, RVData1, ItemNo1, ItemOffs1) and
  279.      LinearToRichView(rv, rv.RVData, SelLength, RVData2, ItemNo2, ItemOffs2) and
  280.      (RVData1=RVData2)
  281.   then begin
  282.     RVData1 := RVData1.Edit;
  283.     TCustomRVFormattedData(RVData1).SetSelectionBounds(ItemNo1, ItemOffs1,
  284.       ItemNo2, ItemOffs2);
  285.     TCustomRVFormattedData(RVData1).Invalidate;
  286.   end;
  287. end;
  288. {------------------------------------------------------------------------------}
  289. {$IFNDEF RVDONOTUSERVF}
  290. procedure RVGetSelectionEx(rv: TCustomRichView; var Selection: TRVSelection);
  291. var RVData: TCustomRVFormattedData;
  292. begin
  293.   RVData := rv.RVData;
  294.   while RVData.GetChosenRVData<>nil do
  295.     RVData := TCustomRVFormattedData(RVData.GetChosenRVData);
  296.   Selection.MultiCell := (RVData.PartialSelectedItem<>nil) and
  297.     (RVData.PartialSelectedItem is TRVTableItemInfo);
  298.   if Selection.MultiCell then
  299.     with Selection do begin
  300.       TRVTableItemInfo(RVData.PartialSelectedItem).GetSelectionBounds(StartRow,
  301.         StartCol, RowOffs, ColOffs);
  302.       RichViewToLinear(rv, rv.RVData, RVData,
  303.         TRVTableItemInfo(RVData.PartialSelectedItem).GetMyItemNo, 0, SelStart);
  304.       SelLength := 0;
  305.       dec(SelStart, RVCharsPerLineBreak);
  306.       {$IFDEF RVLIN_STARTFROM1}
  307.       inc(SelStart);
  308.       {$ENDIF}
  309.     end
  310.   else
  311.     RVGetSelection(rv, Selection.SelStart, Selection.SelLength);
  312. end;
  313. {------------------------------------------------------------------------------}
  314. procedure RVSetSelectionEx(rv: TCustomRichView; const Selection: TRVSelection);
  315. var RVData: TCustomRVData;
  316.     SelStart, ItemNo, ItemOffs: Integer;
  317. begin
  318.   if Selection.MultiCell then begin
  319.     SelStart := Selection.SelStart;
  320.     {$IFDEF RVLIN_STARTFROM1}
  321.     dec(SelStart);
  322.     {$ENDIF}
  323.     if LinearToRichView(rv, rv.RVData, SelStart, RVData, ItemNo, ItemOffs) then begin
  324.       RVData := RVData.Edit;
  325.       if RVData.GetItemStyle(ItemNo)=rvsTable then
  326.         with Selection do
  327.           TRVTableItemInfo(RVData.GetItem(ItemNo)).Select(StartRow, StartCol,
  328.             RowOffs, ColOffs);
  329.     end;
  330.     end
  331.   else
  332.     RVSetSelection(rv, Selection.SelStart, Selection.SelLength);
  333. end;
  334. {$ENDIF}
  335. {------------------------------------------------------------------------------}
  336. function GetTextRange_(rv: TCustomRichView; CurRVData: TCustomRVData;
  337.   var LinearPos: Integer; RangeLength: Integer; var s: String;
  338.   var CollectingText: Boolean): Boolean;
  339. var i, ItemLen, Offs: Integer;
  340.     StoreSub: TRVStoreSubRVData;
  341.     SubRVData: TCustomRVData;
  342.     item: TCustomRVItemInfo;
  343.     LineBreak: String;
  344.     function CheckExit: Boolean;
  345.     begin
  346.       if LinearPos<=0 then begin
  347.         if CollectingText then begin
  348.           Result := True;
  349.           exit;
  350.         end;
  351.         CollectingText := True;
  352.         if RangeLength>0 then
  353.           LinearPos := RangeLength
  354.         else
  355.           LinearPos := MaxInt;
  356.       end;
  357.       Result := False;      
  358.     end;
  359. begin
  360.   Result := False;
  361.   if RVCharsPerLineBreak=1 then
  362.     LineBreak := #13
  363.   else
  364.     LineBreak := #13#10;
  365.   for i := 0 to CurRVData.Items.Count-1 do begin
  366.     item := CurRVData.GetItem(i);
  367.     SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdFirst));
  368.     if SubRVData<>nil then begin
  369.       if (i>0) and not item.SameAsPrev then begin
  370.         dec(LinearPos, RVCharsPerLineBreak);
  371.         if CollectingText then
  372.           s := s+LineBreak;
  373.       end;
  374.       if CheckExit then begin
  375.         Result := True;
  376.         StoreSub.Free;
  377.         exit;
  378.       end;
  379.       repeat
  380.         dec(LinearPos, RVCharsPerLineBreak);
  381.         if CollectingText then
  382.           s := s+LineBreak;
  383.         Result := GetTextRange_(rv, SubRVData.GetRVData, LinearPos, RangeLength,
  384.           s, CollectingText);
  385.         if Result then
  386.           break;
  387.         SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdNext));
  388.       until SubRVData=nil;
  389.       StoreSub.Free;
  390.       if Result then
  391.         exit;
  392.       dec(LinearPos, RVCharsPerLineBreak);
  393.       if CollectingText then
  394.         s := s+LineBreak;
  395.       if CheckExit then begin
  396.         Result := True;
  397.         exit;
  398.       end;
  399.       end
  400.     else begin
  401.       if CheckExit then begin
  402.         Result := True;
  403.         exit;
  404.       end;
  405.       if (i>0) and not item.SameAsPrev then begin
  406.         dec(LinearPos, RVCharsPerLineBreak);
  407.         if CollectingText then
  408.           s := s+LineBreak;
  409.         if CheckExit then begin
  410.           Result := True;
  411.           exit;
  412.         end;
  413.       end;
  414.       if item.StyleNo<0 then begin
  415.         dec(LinearPos);
  416.         if CollectingText then
  417.           if item.StyleNo=rvsTab then
  418.             s := s+#9
  419.           else
  420.             s := s+RVNonTextCharacter;
  421.         if CheckExit then begin
  422.           Result := True;
  423.           exit;
  424.         end;
  425.         end
  426.       else begin
  427.         ItemLen := CurRVData.ItemLength(i);
  428.         Offs    := 1;
  429.         if ItemLen>=LinearPos then begin
  430.           if CollectingText then
  431.             s := s+Copy(CurRVData.GetItemTextA(i), Offs, LinearPos);
  432.           dec(ItemLen, LinearPos);
  433.           inc(Offs, LinearPos);
  434.           LinearPos := 0;
  435.           if CheckExit then begin
  436.             Result := True;
  437.             exit;
  438.           end;
  439.           if ItemLen>=LinearPos then begin
  440.             if CollectingText then
  441.               s := s+Copy(CurRVData.GetItemTextA(i), Offs, LinearPos);
  442.             LinearPos := 0;
  443.             if CheckExit then begin
  444.               Result := True;
  445.               exit;
  446.             end;
  447.             end
  448.           else begin
  449.             if CollectingText then
  450.               s := s+Copy(CurRVData.GetItemTextA(i), Offs, ItemLen);
  451.             dec(LinearPos, ItemLen);
  452.           end;
  453.           end
  454.         else begin
  455.           if CollectingText then
  456.             s := s+Copy(CurRVData.GetItemTextA(i), Offs, ItemLen);
  457.           dec(LinearPos, ItemLen);
  458.         end;
  459.       end;
  460.     end;
  461.   end;
  462. end;
  463. {------------------------------------------------------------------------------}
  464. function RVGetTextRange(rv: TCustomRichView; RangeStart, RangeLength: Integer): String;
  465. var f: Boolean;
  466. begin
  467.   f := False;
  468.   Result := '';
  469.   if RangeLength=0 then
  470.     exit;
  471.   f := False;
  472.   GetTextRange_(rv, rv.RVData, RangeStart, RangeLength, Result, f);
  473. end;
  474. {------------------------------------------------------------------------------}
  475. function RVGetTextLength(rv: TCustomRichView): Integer;
  476. begin
  477.   Result := 0;
  478.   RichViewToLinear(rv, rv.RVData, rv.RVData, rv.ItemCount-1,
  479.     rv.GetOffsAfterItem(rv.ItemCount-1), Result);
  480.   if Result>0 then
  481.     dec(Result);
  482. end;
  483. end.