Implement "Track changes" feature
Posted: Fri Feb 08, 2008 2:26 pm
For a multi-user software, I would like to have a "Track Changes" features, as in Word.
I tried to implement this in a very dirty way: every change is shown with an alternative style, colored.
It doesn't show deleted words or characters...
How can I implement this feature, without changing the TRichView core, or is this great feature in your to-do list?
Here is what I've done so far:
I tried to implement this in a very dirty way: every change is shown with an alternative style, colored.
It doesn't show deleted words or characters...
How can I implement this feature, without changing the TRichView core, or is this great feature in your to-do list?
Here is what I've done so far:
First I create a new Component with some new methods:Then the implementation part, with dirty StyleNo changes:Code: Select all
protected StyleModif: array[0..31] of byte; // just 256 boolean values ModifItem: TCustomRVItemInfo; ModifItemOffset: integer; public // assign to a RichViewEdit for basic track changes: procedure ModifItemAction(Sender: TCustomRichView; ItemAction: TRVItemAction; Item: TCustomRVItemInfo; var Text: String; RVData: TCustomRVData); procedure ModifCaretMove(Sender: TObject); // track changes text styles: GetBit(@StyleModif,StyleNo)=true, BackColor=clYellow function StyleModifToNormal(StyleNo: integer): integer; function StyleNormalToModif(StyleNo: integer): integer; // Tag=integer(NewStr('Comment')) (must have rvoTagsArePChars in Options)
The StyleModif just check whatever the Style is a "modifying" Style or not with this common and fast procedures:Code: Select all
procedure TIct4RichViewEdit.ModifCaretMove(Sender: TObject); var s,s2,s3: string; m: TRVTextItemInfo; i, e,b, o, L: integer; rv: TCustomRichViewEdit absolute Sender; begin if (ModifItem=nil) or not (Sender is TCustomRichViewEdit) or not (ModifItem is TRVTextItemInfo) then exit; while (rv.InplaceEditor<>nil) and (rv.InplaceEditor is TCustomRichViewEdit) do rv := TCustomRichViewEdit(rv.InplaceEditor); m := TRVTextItemInfo(ModifItem); ModifItem := nil; i := rv.GetItemNo(m); if i<0 then exit; s := rv.GetItemTextA(i); L := length(s); e := ModifItemOffset; b := e; while (e<=L) and (s[e]<>' ') do inc(e); // word modified in s[b+1..e-1] // while (e<=L) and (s[e]=' ') do inc(e); / includes spaces after while (b>0) and (s[b]<>' ') do dec(b); if e=b then exit; dec(e); s2 := copy(s,b+1,e-b); s3 := copy(s,e+1,maxInt); o := rv.GetOffsBeforeItem(i); inc(b,o); inc(e,o); inc(o,ModifItemOffset-b+1); // ModifItemOffset will change with ApplyTextStyle if rv.UndoAction=rvutDelete then begin dec(o,2); if o<1 then o := 1; end; rv.SetSelectionBounds(i,b,i,e); rv.ApplyTextStyle( StyleNormalToModif(m.StyleNo)); i := rv.CurItemNo; rv.SetSelectionBounds(i, o, i, o); rv.Invalidate; end; procedure TIct4RichViewEdit.ModifItemAction(Sender: TCustomRichView; ItemAction: TRVItemAction; Item: TCustomRVItemInfo; var Text: String; RVData: TCustomRVData); begin case ItemAction of rviaTextModifying: begin if (ModifItem=Item) or (Item.StyleNo<0) or GetBit(@StyleModif,Item.StyleNo) or not Sender.InheritsFrom(TCustomRichViewEdit) then exit; ModifItem := Item; ModifItemOffset := TCustomRichViewEdit(Sender).OffsetInCurItem; end; end; end; function TIct4RichViewEdit.StyleModifToNormal(StyleNo: integer): integer; var FontSource: TFontInfo; begin FontSource := Style.TextStyles[StyleNo]; if FontSource.BackColor<>clYellow then result := StyleNo else result := FontSource.BaseStyleNo; end; function TIct4RichViewEdit.StyleNormalToModif(StyleNo: integer): integer; var FontSource, FontInfo: TFontInfo; begin FontSource := Style.TextStyles[StyleNo]; if FontSource.BackColor=clYellow then begin result := StyleNo; exit; end; FontInfo := TFontInfo.Create(nil); try FontInfo.Assign(FontSource); FontInfo.BackColor := clYellow; FontInfo.BaseStyleNo := StyleNo; result := Style.TextStyles.FindSuchStyle(StyleNo,FontInfo,RVAllFontInfoProperties); if result=-1 then begin with Style.TextStyles.Add do begin Assign(FontInfo); Standard := false; end; result := Style.TextStyles.Count-1; SetBit(@StyleModif,result); end; finally FontInfo.Free; end; end;
Code: Select all
function GetBit(p: pointer; aIndex: integer): boolean; asm bt [p],aindex // eax = self = bits sbb eax,eax and eax,1 end; procedure SetBit(p: pointer; aIndex: integer); asm bts [p],aIndex end;