{ *************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * * * *************************************************************************** see for todo list: http://wiki.lazarus.freepascal.org/index.php/LazDoc } unit FPDocEditWindow; {$mode objfpc}{$H+} {off $define VerboseCodeHelp} interface uses // FCL Classes, SysUtils, // LazUtils Laz2_DOM, Laz2_XMLRead, LazStringUtils, LazTracer, // LCL LResources, StdCtrls, Buttons, ComCtrls, Controls, Dialogs, ExtCtrls, Forms, Graphics, LCLType, LCLProc, // Synedit SynEdit, SynHighlighterXML, SynEditFoldedView, SynEditWrappedView, // codetools FileProcs, CodeCache, CodeToolManager, CTXMLFixFragment, // IDEIntf IDEWindowIntf, ProjectIntf, LazIDEIntf, IDEHelpIntf, Menus, SrcEditorIntf, IDEDialogs, LazFileUtils, IDEImagesIntf, // IDE IDEOptionDefs, EnvironmentOpts, PackageSystem, LazarusIDEStrConsts, FPDocSelectInherited, FPDocSelectLink, CodeHelp; type TFPDocEditorFlag = ( fpdefReading, fpdefWriting, fpdefCodeCacheNeedsUpdate, fpdefChainNeedsUpdate, fpdefCaptionNeedsUpdate, fpdefValueControlsNeedsUpdate, fpdefInheritedControlsNeedsUpdate, fpdefTopicSettingUp, fpdefTopicNeedsUpdate, fpdefWasHidden ); TFPDocEditorFlags = set of TFPDocEditorFlag; { TFPDocEditor } TFPDocEditor = class(TForm) AddLinkToInheritedButton: TButton; BoldFormatButton: TSpeedButton; BrowseExampleButton: TButton; OpenXMLButton: TButton; ShortPanel: TPanel; DescrShortEdit: TEdit; SynXMLSyn1: TSynXMLSyn; TopicShort: TEdit; TopicDescrSynEdit: TSynEdit; Panel3: TPanel; TopicListBox: TListBox; NewTopicNameEdit: TEdit; NewTopicButton: TButton; CopyFromInheritedButton: TButton; CreateButton: TButton; DescrSynEdit: TSynEdit; DescrTabSheet: TTabSheet; ErrorsSynEdit: TSynEdit; ErrorsTabSheet: TTabSheet; ExampleEdit: TEdit; ExampleTabSheet: TTabSheet; InheritedShortEdit: TEdit; InheritedShortLabel: TLabel; InheritedTabSheet: TTabSheet; InsertCodeTagButton: TSpeedButton; InsertLinkSpeedButton: TSpeedButton; InsertParagraphSpeedButton: TSpeedButton; InsertRemarkButton: TSpeedButton; InsertVarTagButton: TSpeedButton; ItalicFormatButton: TSpeedButton; LeftBtnPanel: TPanel; LinkEdit: TEdit; LinkLabel: TLabel; Panel1: TPanel; Panel2: TPanel; SaveButton: TSpeedButton; SeeAlsoSynEdit: TSynEdit; MoveToInheritedButton: TButton; OpenDialog: TOpenDialog; PageControl: TPageControl; SeeAlsoTabSheet: TTabSheet; ShortEdit: TEdit; ShortLabel: TLabel; ShortTabSheet: TTabSheet; InsertPrintShortSpeedButton: TSpeedButton; InsertURLTagSpeedButton: TSpeedButton; TopicSheet: TTabSheet; UnderlineFormatButton: TSpeedButton; procedure AddLinkToInheritedButtonClick(Sender: TObject); procedure ApplicationIdle(Sender: TObject; var Done: Boolean); procedure BrowseExampleButtonClick(Sender: TObject); procedure CopyFromInheritedButtonClick(Sender: TObject); procedure CopyShortToDescrMenuItemClick(Sender: TObject); procedure CreateButtonClick(Sender: TObject); procedure DescrSynEditChange(Sender: TObject); procedure ErrorsSynEditChange(Sender: TObject); procedure ExampleEditChange(Sender: TObject); procedure FormatButtonClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormShow(Sender: TObject); procedure InsertLinkSpeedButtonClick(Sender: TObject); procedure LinkEditChange(Sender: TObject); procedure MoveToInheritedButtonClick(Sender: TObject); procedure NewTopicButtonClick(Sender: TObject); procedure OpenXMLButtonClick(Sender: TObject); procedure PageControlChange(Sender: TObject); procedure SaveButtonClick(Sender: TObject); procedure SeeAlsoSynEditChange(Sender: TObject); procedure ShortEditChange(Sender: TObject); procedure TopicControlEnter(Sender: TObject); procedure TopicDescrSynEditChange(Sender: TObject); procedure TopicListBoxClick(Sender: TObject); private FCaretXY: TPoint; FModified: Boolean; FFlags: TFPDocEditorFlags; fUpdateLock: Integer; fSourceFilename: string; fDocFile: TLazFPDocFile; fChain: TCodeHelpElementChain; FOldValues: TFPDocElementValues; FOldVisualValues: TFPDocElementValues; function GetDoc: TXMLdocument; function GetDocFile: TLazFPDocFile; function GetSourceFilename: string; function GetFirstElement: TDOMNode; function GetContextTitle(Element: TCodeHelpElement): string; function FindInheritedIndex: integer; procedure Save(CheckGUI: boolean = false); function GetGUIValues: TFPDocElementValues; procedure SetModified(const AValue: boolean); function WriteNode(Element: TCodeHelpElement; Values: TFPDocElementValues; Interactive: Boolean): Boolean; procedure UpdateCodeCache; procedure UpdateChain; procedure UpdateCaption; procedure UpdateValueControls; procedure UpdateInheritedControls; procedure OnFPDocChanging(Sender: TObject; FPDocFPFile: TLazFPDocFile); procedure OnFPDocChanged(Sender: TObject; FPDocFPFile: TLazFPDocFile); procedure LoadGUIValues(Element: TCodeHelpElement); procedure MoveToInherited(Element: TCodeHelpElement); function GetDefaultDocFile(CreateIfNotExists: Boolean = False): TLazFPDocFile; function ExtractIDFromLinkTag(const LinkTag: string; out ID, Title: string): boolean; function CreateElement(Element: TCodeHelpElement): Boolean; procedure UpdateButtons; function GetCurrentUnitName: string; function GetCurrentOwnerName: string; procedure JumpToError(Item : TFPDocItem; LineCol: TPoint); procedure OpenXML; function GUIModified: boolean; procedure DoEditorUpdate(Sender: TObject); procedure DoEditorMouseUp(Sender: TObject); private FFollowCursor: boolean; FIdleConnected: boolean; FLastTopicControl: TControl; FCurrentTopic: String; procedure SetFollowCursor(AValue: boolean); procedure SetIdleConnected(AValue: boolean); procedure UpdateTopicCombo; procedure ClearTopicControls; procedure UpdateTopic; protected procedure UpdateShowing; override; procedure Loaded; override; public procedure Reset; procedure InvalidateChain; procedure LoadIdentifierAt(const SrcFilename: string; const Caret: TPoint); procedure LoadIdentifierAtCursor; procedure BeginUpdate; procedure EndUpdate; procedure ClearEntry(DoSave: Boolean); property DocFile: TLazFPDocFile read GetDocFile; property Doc: TXMLdocument read GetDoc; property SourceFilename: string read GetSourceFilename; property CaretXY: TPoint read FCaretXY; property Modified: boolean read FModified write SetModified; property IdleConnected: boolean read FIdleConnected write SetIdleConnected; property FollowCursor: boolean read FFollowCursor write SetFollowCursor; end; var FPDocEditor: TFPDocEditor = nil; procedure DoShowFPDocEditor(State: TIWGetFormState = iwgfShowOnTop); implementation {$R *.lfm} {$R lazdoc.res} { TFPDocEditor } procedure DoShowFPDocEditor(State: TIWGetFormState); begin if FPDocEditor = Nil then IDEWindowCreators.CreateForm(FPDocEditor,TFPDocEditor, State=iwgfDisabled,LazarusIDE.OwningComponent) else if State=iwgfDisabled then FPDocEditor.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('DoShowFPDocEditor'){$ENDIF}; if State>=iwgfShow then IDEWindowCreators.ShowForm(FPDocEditor,State=iwgfShowOnTop); end; function TFPDocEditor.GetFirstElement: TDOMNode; var CurDocFile: TLazFPDocFile; begin Result:=nil; CurDocFile:=DocFile; if CurDocFile=nil then exit; Result:=CurDocFile.GetFirstElement; end; procedure TFPDocEditor.FormCreate(Sender: TObject); procedure UpdateSynEdit(ASynEd: TSynEdit); var fld: TSynEditFoldedView; begin fld := TSynEditFoldedView(ASynEd.TextViewsManager.SynTextViewByClass[TSynEditFoldedView]); if fld <> nil then fld.FoldProvider.Enabled := False; TLazSynEditLineWrapPlugin.Create(ASynEd); end; begin Caption := lisCodeHelpMainFormCaption; ShortTabSheet.Caption := lisCodeHelpShortTag; InheritedTabSheet.Caption := lisCodeHelpInherited; DescrTabSheet.Caption := lisCodeHelpDescrTag; ErrorsTabSheet.Caption := lisCodeHelpErrorsTag; SeeAlsoTabSheet.Caption := lisCodeHelpSeeAlsoTag; ExampleTabSheet.Caption := lisCodeHelpExampleTag; PageControl.PageIndex := 0; BoldFormatButton.Hint := lisCodeHelpHintBoldFormat; ItalicFormatButton.Hint := lisCodeHelpHintItalicFormat; UnderlineFormatButton.Hint := lisCodeHelpHintUnderlineFormat; InsertCodeTagButton.Hint := lisCodeHelpHintInsertCodeTag; InsertRemarkButton.Hint := lisCodeHelpHintRemarkTag; InsertVarTagButton.Hint := lisCodeHelpHintVarTag; InsertParagraphSpeedButton.Hint := lisCodeHelpInsertParagraphFormattingTag; InsertLinkSpeedButton.Hint := lisCodeHelpInsertALink; InsertPrintShortSpeedButton.Hint:=lisInsertPrintshortTag2; InsertURLTagSpeedButton.Hint:=lisInsertUrlTag; ShortLabel.Caption:=lisShort; LinkLabel.Caption:=lisLink; CreateButton.Caption := lisCodeHelpCreateButton; OpenXMLButton.Caption:=lisOpenXML; OpenXMLButton.Enabled:=false; SaveButton.Caption := ''; SaveButton.Enabled:=false; SaveButton.Hint:=lisSave; SaveButton.ShowHint:=true; BrowseExampleButton.Caption := lisCodeHelpBrowseExampleButton; MoveToInheritedButton.Caption:=lisLDMoveEntriesToInherited; CopyFromInheritedButton.Caption:=lisLDCopyFromInherited; AddLinkToInheritedButton.Caption:=lisLDAddLinkToInherited; Reset; CodeHelpBoss.AddHandlerOnChanging(@OnFPDocChanging); CodeHelpBoss.AddHandlerOnChanged(@OnFPDocChanged); Name := NonModalIDEWindowNames[nmiwFPDocEditor]; IDEImages.AssignImage(BoldFormatButton, 'formatbold'); IDEImages.AssignImage(UnderlineFormatButton, 'formatunderline'); IDEImages.AssignImage(ItalicFormatButton, 'formatitalic'); IDEImages.AssignImage(InsertVarTagButton, 'insertvartag'); IDEImages.AssignImage(InsertCodeTagButton, 'insertcodetag'); IDEImages.AssignImage(InsertRemarkButton, 'insertremark'); IDEImages.AssignImage(InsertURLTagSpeedButton, 'formatunderline'); IDEImages.AssignImage(SaveButton, 'laz_save'); SourceEditorManagerIntf.RegisterChangeEvent(semEditorActivate, @DoEditorUpdate); SourceEditorManagerIntf.RegisterChangeEvent(semEditorStatus, @DoEditorUpdate); SourceEditorManagerIntf.RegisterChangeEvent(semEditorMouseUp, @DoEditorMouseUp); UpdateSynEdit(TopicDescrSynEdit); UpdateSynEdit(DescrSynEdit); UpdateSynEdit(ErrorsSynEdit); UpdateSynEdit(SeeAlsoSynEdit); FollowCursor:=true; IdleConnected:=true; end; procedure TFPDocEditor.FormDestroy(Sender: TObject); begin IdleConnected:=false; Reset; FreeAndNil(fChain); if assigned(CodeHelpBoss) then CodeHelpBoss.RemoveAllHandlersOfObject(Self); Application.RemoveAllHandlersOfObject(Self); if SourceEditorManagerIntf<>nil then begin SourceEditorManagerIntf.UnRegisterChangeEvent(semEditorActivate, @DoEditorUpdate); SourceEditorManagerIntf.UnRegisterChangeEvent(semEditorStatus, @DoEditorUpdate); end; end; procedure TFPDocEditor.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if (Key=VK_S) and (Shift=[ssCtrl]) then begin Save(true); Key:=VK_UNKNOWN; end; end; procedure TFPDocEditor.FormShow(Sender: TObject); begin DoEditorUpdate(nil); end; procedure TFPDocEditor.FormatButtonClick(Sender: TObject); procedure InsertTag(const StartTag, EndTag: String); begin if PageControl.ActivePage = ShortTabSheet then begin ShortEdit.SelText := StartTag + ShortEdit.SelText + EndTag; DescrShortEdit.Text:=ShortEdit.Text; end else if PageControl.ActivePage = DescrTabSheet then DescrSynEdit.SelText := StartTag + DescrSynEdit.SelText + EndTag else if PageControl.ActivePage = ErrorsTabSheet then ErrorsSynEdit.SelText := StartTag + ErrorsSynEdit.SelText + EndTag else if PageControl.ActivePage = TopicSheet then begin if (FLastTopicControl = TopicShort) then TopicShort.SelText := StartTag + TopicShort.SelText + EndTag; if (FLastTopicControl = TopicDescrSynEdit) then TopicDescrSynEdit.SelText := StartTag + TopicDescrSynEdit.SelText + EndTag; end else exit; Modified:=true; end; begin case TSpeedButton(Sender).Tag of //bold 0: InsertTag('', ''); //italic 1: InsertTag('', ''); //underline 2: InsertTag('', ''); //code tag 3: InsertTag('

', '

'); //remark tag 4: InsertTag('

', '

'); //var tag 5: InsertTag('', ''); //paragraph tag 6: InsertTag('

', '

'); //printshort 7: if (fChain<>nil) and (fChain.Count>0) then InsertTag('',''); //url tag 8: InsertTag('', ''); end; end; procedure TFPDocEditor.InsertLinkSpeedButtonClick(Sender: TObject); var Link: string; LinkTitle: string; LinkSrc: String; begin if ShowFPDocLinkEditorDialog(fSourceFilename,DocFile,Link,LinkTitle)<>mrOk then exit; if Link='' then exit; LinkSrc:=''; end else begin LinkSrc:=LinkSrc+'>'+LinkTitle+''; end; if PageControl.ActivePage = ShortTabSheet then begin ShortEdit.SelText := LinkSrc; DescrShortEdit.Text := ShortEdit.Text; end; if PageControl.ActivePage = DescrTabSheet then DescrSynEdit.SelText := LinkSrc; if PageControl.ActivePage = SeeAlsoTabSheet then SeeAlsoSynEdit.SelText := LinkSrc; if PageControl.ActivePage = ErrorsTabSheet then ErrorsSynEdit.SelText := LinkSrc; if PageControl.ActivePage = TopicSheet then begin if (FLastTopicControl = TopicShort) then TopicShort.SelText := LinkSrc; if (FLastTopicControl = TopicDescrSynEdit) then TopicDescrSynEdit.SelText := LinkSrc; end; Modified:=true; end; procedure TFPDocEditor.LinkEditChange(Sender: TObject); begin if fpdefReading in FFlags then exit; if LinkEdit.Text<>FOldVisualValues[fpdiElementLink] then Modified:=true; end; procedure TFPDocEditor.ApplicationIdle(Sender: TObject; var Done: Boolean); var ActiveForm: TCustomForm; begin if (fUpdateLock>0) then begin DebugLn(['WARNING: TFPDocEditor.ApplicationIdle fUpdateLock>0']); exit; end; if not IsVisible then begin Include(FFlags,fpdefWasHidden); IdleConnected:=false; exit; end; ActiveForm:=Screen.ActiveCustomForm; if (ActiveForm<>nil) and (fsModal in ActiveForm.FormState) then exit; Done:=false; if fpdefCodeCacheNeedsUpdate in FFlags then UpdateCodeCache else if fpdefChainNeedsUpdate in FFlags then UpdateChain else if fpdefCaptionNeedsUpdate in FFlags then UpdateCaption else if fpdefValueControlsNeedsUpdate in FFlags then UpdateValueControls else if fpdefInheritedControlsNeedsUpdate in FFlags then UpdateInheritedControls else if fpdefTopicNeedsUpdate in FFlags then UpdateTopicCombo else begin //debugln(['TFPDocEditor.ApplicationIdle updated']); Done:=true; IdleConnected:=false; end; end; procedure TFPDocEditor.MoveToInheritedButtonClick(Sender: TObject); var i: Integer; Element: TCodeHelpElement; Candidates: TFPList; FPDocSelectInheritedDlg: TFPDocSelectInheritedDlg; ShortDescr: String; begin if fChain=nil then exit; Candidates:=nil; FPDocSelectInheritedDlg:=nil; try // find all entries till the first inherited entry with a description for i:=1 to fChain.Count-1 do begin Element:=fChain[i]; if Candidates=nil then Candidates:=TFPList.Create; Candidates.Add(Element); if (Element.ElementNode<>nil) and (Element.FPDocFile.GetValueFromNode(Element.ElementNode,fpdiShort)<>'') then break; end; // choose one entry if (Candidates=nil) or (Candidates.Count=0) then exit; if Candidates.Count=1 then begin // there is only one candidate Element:=TCodeHelpElement(Candidates[0]); if (Element.ElementNode<>nil) then begin ShortDescr:=Element.FPDocFile.GetValueFromNode(Element.ElementNode,fpdiShort); if ShortDescr<>'' then begin // the inherited entry already contains a description. // ask if it should be really replaced if IDEQuestionDialog(lisCodeHelpConfirmreplace, GetContextTitle(Element)+' already contains the help:'+LineEnding+ShortDescr, mtConfirmation, [mrYes, lisReplace, mrCancel]) <> mrYes then exit; end; end; end else begin // there is more than one candidate // => ask which one to replace FPDocSelectInheritedDlg:=TFPDocSelectInheritedDlg.Create(nil); FPDocSelectInheritedDlg.InheritedComboBox.Items.Clear; for i:=0 to Candidates.Count-1 do begin Element:=TCodeHelpElement(Candidates[i]); FPDocSelectInheritedDlg.InheritedComboBox.Items.Add( GetContextTitle(Element)); end; if FPDocSelectInheritedDlg.ShowModal<>mrOk then exit; i:=FPDocSelectInheritedDlg.InheritedComboBox.ItemIndex; if i<0 then exit; Element:=TCodeHelpElement(Candidates[i]); end; // move the content of the current entry to the inherited entry MoveToInherited(Element); finally FPDocSelectInheritedDlg.Free; Candidates.Free; end; end; procedure TFPDocEditor.NewTopicButtonClick(Sender: TObject); var Dfile: TLazFPDocFile; begin if NewTopicNameEdit.Text = '' then exit; Dfile := GetDefaultDocFile(True); if not assigned(DFile) then exit; if DFile.GetModuleTopic(NewTopicNameEdit.Text) = nil then begin DFile.CreateModuleTopic(NewTopicNameEdit.Text); CodeHelpBoss.SaveFPDocFile(DFile); end; UpdateTopicCombo; TopicListBox.ItemIndex := TopicListBox.Items.IndexOf(NewTopicNameEdit.Text); TopicListBoxClick(Sender); end; procedure TFPDocEditor.OpenXMLButtonClick(Sender: TObject); begin OpenXML; end; procedure TFPDocEditor.PageControlChange(Sender: TObject); begin UpdateButtons; end; procedure TFPDocEditor.SaveButtonClick(Sender: TObject); begin Save; UpdateValueControls; end; procedure TFPDocEditor.SeeAlsoSynEditChange(Sender: TObject); begin if fpdefReading in FFlags then exit; if SeeAlsoSynEdit.Text<>FOldVisualValues[fpdiSeeAlso] then Modified:=true; end; procedure TFPDocEditor.ShortEditChange(Sender: TObject); // called by ShortEdit and DescrShortEdit var NewShort: String; begin if fpdefReading in FFlags then exit; //debugln(['TFPDocEditor.ShortEditChange ',DbgSName(Sender)]); if Sender=DescrShortEdit then NewShort:=DescrShortEdit.Text else NewShort:=ShortEdit.Text; if NewShort<>FOldVisualValues[fpdiShort] then Modified:=true; // copy to the other edit if Sender=DescrShortEdit then ShortEdit.Text:=NewShort else DescrShortEdit.Text:=NewShort; end; procedure TFPDocEditor.TopicControlEnter(Sender: TObject); begin FLastTopicControl := TControl(Sender); end; procedure TFPDocEditor.TopicDescrSynEditChange(Sender: TObject); begin if fpdefReading in FFlags then exit; if fpdefTopicSettingUp in FFlags then exit; Modified := True; end; procedure TFPDocEditor.TopicListBoxClick(Sender: TObject); begin if fpdefTopicSettingUp in FFlags then exit; if (FCurrentTopic <> '') and Modified then Save; UpdateTopic; end; function TFPDocEditor.GetContextTitle(Element: TCodeHelpElement): string; // get codetools path. for example: TButton.Align begin Result:=''; if Element=nil then exit; Result:=Element.ElementName; end; function TFPDocEditor.GetDoc: TXMLdocument; begin if DocFile<>nil then Result:=DocFile.Doc else Result:=nil; end; procedure TFPDocEditor.ClearTopicControls; var OldSettingUp: boolean; begin OldSettingUp:=fpdefTopicSettingUp in FFlags; Include(FFlags, fpdefTopicSettingUp); try TopicShort.Clear; TopicDescrSynEdit.Clear; TopicShort.Enabled := False; TopicDescrSynEdit.Enabled := False; finally if not OldSettingUp then Exclude(FFlags, fpdefTopicSettingUp); end; end; function TFPDocEditor.GetDocFile: TLazFPDocFile; begin Result:=nil; if fChain<>nil then Result:=fChain.DocFile else Result:=fDocFile; end; function TFPDocEditor.GetSourceFilename: string; begin Result:=fSourceFilename; end; procedure TFPDocEditor.UpdateCaption; var strCaption: String; Filename: String; begin if fUpdateLock>0 then begin Include(FFlags,fpdefCaptionNeedsUpdate); exit; end; Exclude(FFlags,fpdefCaptionNeedsUpdate); {$IFDEF VerboseCodeHelp} DebugLn(['TFPDocEditForm.UpdateCaption START']); {$ENDIF} strCaption := lisCodeHelpMainFormCaption + ' - '; if (fChain <> nil) and (fChain.Count>0) then strCaption := strCaption + GetContextTitle(fChain[0]) + ' - ' else strCaption := strCaption + lisCodeHelpNoTagCaption + ' - '; if DocFile<>nil then begin Filename:=DocFile.Filename; if (LazarusIDE.ActiveProject<>nil) then Filename:=LazarusIDE.ActiveProject.GetShortFilename(Filename,true); Caption := strCaption + Filename; end else Caption := strCaption + lisCodeHelpNoTagCaption; {$IFDEF VerboseCodeHelp} DebugLn(['TFPDocEditor.UpdateCaption ',Caption]); {$ENDIF} end; procedure TFPDocEditor.UpdateValueControls; var Element: TCodeHelpElement; begin if fUpdateLock>0 then begin Include(FFlags,fpdefValueControlsNeedsUpdate); exit; end; Exclude(FFlags,fpdefValueControlsNeedsUpdate); {$IFDEF VerboseCodeHelp} DebugLn(['TFPDocEditForm.UpdateValueControls START']); {$ENDIF} Element:=nil; if (fChain<>nil) and (fChain.Count>0) then Element:=fChain[0]; LoadGUIValues(Element); SaveButton.Enabled:=FModified; end; procedure TFPDocEditor.UpdateInheritedControls; var i: LongInt; Element: TCodeHelpElement; ShortDescr: String; begin if fUpdateLock>0 then begin Include(FFlags,fpdefInheritedControlsNeedsUpdate); exit; end; Exclude(FFlags,fpdefInheritedControlsNeedsUpdate); {$IFDEF VerboseCodeHelp} DebugLn(['TFPDocEditForm.UpdateInheritedControls START']); {$ENDIF} i:=FindInheritedIndex; if i<0 then begin InheritedShortEdit.Text:=''; InheritedShortEdit.Enabled:=false; InheritedShortLabel.Caption:=lisCodeHelpnoinheriteddescriptionfound; end else begin Element:=fChain[i]; ShortDescr:=Element.FPDocFile.GetValueFromNode(Element.ElementNode,fpdiShort); InheritedShortEdit.Text:=ShortDescr; InheritedShortEdit.Enabled:=true; InheritedShortLabel.Caption:=lisCodeHelpShortdescriptionOf+' ' +GetContextTitle(Element); end; MoveToInheritedButton.Enabled:=(fChain<>nil) and (fChain.Count>1) and (ShortEdit.Text<>''); CopyFromInheritedButton.Enabled:=(i>=0); AddLinkToInheritedButton.Enabled:=(i>=0); end; procedure TFPDocEditor.UpdateChain; var Code: TCodeBuffer; LDResult: TCodeHelpParseResult; NewChain: TCodeHelpElementChain; CacheWasUsed: Boolean; begin fDocFile:=nil; FreeAndNil(fChain); if fUpdateLock>0 then begin Include(FFlags,fpdefChainNeedsUpdate); exit; end; Exclude(FFlags,fpdefChainNeedsUpdate); if (fSourceFilename='') or (CaretXY.X<1) or (CaretXY.Y<1) then exit; {$IFDEF VerboseCodeHelp} DebugLn(['TFPDocEditForm.UpdateChain START ',fSourceFilename,' ',dbgs(CaretXY)]); {$ENDIF} NewChain:=nil; try // fetch pascal source Code:=CodeToolBoss.LoadFile(fSourceFilename,true,false); if Code=nil then begin DebugLn(['TFPDocEditForm.UpdateChain failed loading ',fSourceFilename]); exit; end; // start getting the fpdoc element chain LDResult:=CodeHelpBoss.GetElementChain(Code,CaretXY.X,CaretXY.Y,true, NewChain,CacheWasUsed); case LDResult of chprParsing: begin Include(FFlags,fpdefChainNeedsUpdate); DebugLn(['TFPDocEditForm.UpdateChain ToDo: still parsing CodeHelpBoss.GetElementChain for ',fSourceFilename,' ',dbgs(CaretXY)]); exit; end; chprFailed: begin {$IFDEF VerboseFPDocFails} DebugLn(['TFPDocEditForm.UpdateChain failed CodeHelpBoss.GetElementChain for ',fSourceFilename,' ',dbgs(CaretXY)]); {$ENDIF} exit; end; else {$IFDEF VerboseCodeHelp} NewChain.WriteDebugReport; {$ENDIF} fChain:=NewChain; fDocFile:=fChain.DocFile; NewChain:=nil; end; finally NewChain.Free; end; if (fDocFile=nil) then begin // load default docfile, needed to show syntax errors in xml and for topics fDocFile:=GetDefaultDocFile; end; OpenXMLButton.Enabled:=fDocFile<>nil; if fDocFile<>nil then OpenXMLButton.Hint:=fDocFile.Filename else OpenXMLButton.Hint:=''; end; procedure TFPDocEditor.OnFPDocChanging(Sender: TObject; FPDocFPFile: TLazFPDocFile); begin if fpdefWriting in FFlags then exit; if (fChain<>nil) and (fChain.IndexOfFile(FPDocFPFile)>=0) then InvalidateChain else if (fDocFile<>nil) and (fDocFile=FPDocFPFile) then Include(FFlags,fpdefTopicNeedsUpdate); end; procedure TFPDocEditor.OnFPDocChanged(Sender: TObject; FPDocFPFile: TLazFPDocFile); begin if fpdefWriting in FFlags then exit; if FPDocFPFile=nil then exit; // maybe eventually update the editor end; procedure TFPDocEditor.LoadGUIValues(Element: TCodeHelpElement); var EnabledState: Boolean; OldModified: Boolean; begin if fpdefReading in FFlags then exit; OldModified:=FModified; Include(FFlags,fpdefReading); try EnabledState := (Element<>nil) and (Element.ElementNode<>nil); //CreateButton.Enabled := (Element<>nil) and (Element.ElementNode=nil) // and (Element.ElementName<>''); if EnabledState then begin FOldValues:=Element.FPDocFile.GetValuesFromNode(Element.ElementNode); FOldVisualValues[fpdiShort]:=ReplaceLineEndings(FOldValues[fpdiShort],''); FOldVisualValues[fpdiElementLink]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiElementLink]); FOldVisualValues[fpdiDescription]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiDescription]); FOldVisualValues[fpdiErrors]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiErrors]); FOldVisualValues[fpdiSeeAlso]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiSeeAlso]); FOldVisualValues[fpdiExample]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiExample]); //DebugLn(['TFPDocEditor.LoadGUIValues Short="',dbgstr(FOldValues[fpdiShort]),'"']); end else begin FOldVisualValues[fpdiShort]:=''; FOldVisualValues[fpdiElementLink]:=''; FOldVisualValues[fpdiDescription]:=''; FOldVisualValues[fpdiErrors]:=''; FOldVisualValues[fpdiSeeAlso]:=''; FOldVisualValues[fpdiExample]:=''; end; ShortEdit.Text := FOldVisualValues[fpdiShort]; DescrShortEdit.Text := ShortEdit.Text; //debugln(['TFPDocEditor.LoadGUIValues "',ShortEdit.Text,'" "',FOldVisualValues[fpdiShort],'"']); LinkEdit.Text := FOldVisualValues[fpdiElementLink]; DescrSynEdit.Lines.Text := FOldVisualValues[fpdiDescription]; //debugln(['TFPDocEditor.LoadGUIValues DescrMemo="',dbgstr(DescrSynEdit.Lines.Text),'" Descr="',dbgstr(FOldVisualValues[fpdiDescription]),'"']); SeeAlsoSynEdit.Text := FOldVisualValues[fpdiSeeAlso]; ErrorsSynEdit.Lines.Text := FOldVisualValues[fpdiErrors]; ExampleEdit.Text := FOldVisualValues[fpdiExample]; ShortEdit.Enabled := EnabledState; DescrShortEdit.Enabled := ShortEdit.Enabled; LinkEdit.Enabled := EnabledState; DescrSynEdit.Enabled := EnabledState; SeeAlsoSynEdit.Enabled := EnabledState; ErrorsSynEdit.Enabled := EnabledState; ExampleEdit.Enabled := EnabledState; BrowseExampleButton.Enabled := EnabledState; FModified:=OldModified; SaveButton.Enabled:=false; finally Exclude(FFlags,fpdefReading); end; end; procedure TFPDocEditor.MoveToInherited(Element: TCodeHelpElement); var Values: TFPDocElementValues; begin Values:=GetGUIValues; WriteNode(Element,Values,true); end; function TFPDocEditor.ExtractIDFromLinkTag(const LinkTag: string; out ID, Title: string ): boolean; // extract id and title from example: // // TCanvas var StartPos: Integer; EndPos: LongInt; begin Result:=false; ID:=''; Title:=''; StartPos:=length(''>') do inc(StartPos); if LinkTag[StartPos-1]='\' then begin // no title end else begin // has title inc(StartPos); EndPos:=StartPos; while (EndPos<=length(LinkTag)) and (LinkTag[EndPos]<>'<') do inc(EndPos); Title:=copy(LinkTag,StartPos,EndPos-StartPos); end; exit; end; inc(EndPos); end; end; function TFPDocEditor.CreateElement(Element: TCodeHelpElement): Boolean; var NewElement: TCodeHelpElement; begin //DebugLn(['TFPDocEditForm.CreateElement ']); if (Element=nil) or (Element.ElementName='') then exit(false); NewElement:=nil; Include(FFlags,fpdefWriting); try Result:=CodeHelpBoss.CreateElement(Element.CodeXYPos.Code, Element.CodeXYPos.X,Element.CodeXYPos.Y,NewElement); finally Exclude(FFlags,fpdefWriting); NewElement.Free; end; Reset; InvalidateChain; end; procedure TFPDocEditor.UpdateButtons; var HasEdit: Boolean; begin HasEdit:=(PageControl.ActivePage = ShortTabSheet) or (PageControl.ActivePage = DescrTabSheet) or (PageControl.ActivePage = SeeAlsoTabSheet) or (PageControl.ActivePage = ErrorsTabSheet) or (PageControl.ActivePage = TopicSheet); BoldFormatButton.Enabled:=HasEdit; ItalicFormatButton.Enabled:=HasEdit; UnderlineFormatButton.Enabled:=HasEdit; InsertCodeTagButton.Enabled:=HasEdit; InsertLinkSpeedButton.Enabled:=HasEdit; InsertParagraphSpeedButton.Enabled:=HasEdit; InsertRemarkButton.Enabled:=HasEdit; InsertVarTagButton.Enabled:=HasEdit; end; function TFPDocEditor.GetCurrentUnitName: string; begin if (fChain<>nil) and (fChain.Count>0) then Result:=fChain[0].ElementUnitName else Result:=''; end; function TFPDocEditor.GetCurrentOwnerName: string; begin if (fChain<>nil) and (fChain.Count>0) then Result:=fChain[0].ElementOwnerName else Result:=''; end; procedure TFPDocEditor.JumpToError(Item: TFPDocItem; LineCol: TPoint); begin case Item of fpdiShort: PageControl.ActivePage:=ShortTabSheet; fpdiElementLink: PageControl.ActivePage:=InheritedTabSheet; fpdiDescription: begin PageControl.ActivePage:=DescrTabSheet; DescrSynEdit.CaretXY:=LineCol; end; fpdiErrors: PageControl.ActivePage:=ErrorsTabSheet; fpdiSeeAlso: PageControl.ActivePage:=SeeAlsoTabSheet; fpdiExample: PageControl.ActivePage:=ExampleTabSheet; end; end; procedure TFPDocEditor.OpenXML; var CurDocFile: TLazFPDocFile; begin CurDocFile:=DocFile; if CurDocFile=nil then exit; if FileExistsUTF8(CurDocFile.Filename) then begin LazarusIDE.DoOpenEditorFile(CurDocFile.Filename,-1,-1, [ofOnlyIfExists,ofRegularFile,ofUseCache]); end; end; function TFPDocEditor.GUIModified: boolean; begin if fpdefReading in FFlags then exit(false); Result:=(ShortEdit.Text<>FOldVisualValues[fpdiShort]) or (LinkEdit.Text<>FOldVisualValues[fpdiElementLink]) or (DescrSynEdit.Text<>FOldVisualValues[fpdiDescription]) or (SeeAlsoSynEdit.Text<>FOldVisualValues[fpdiSeeAlso]) or (ErrorsSynEdit.Text<>FOldVisualValues[fpdiErrors]) or (ExampleEdit.Text<>FOldVisualValues[fpdiExample]); if Result then begin if (ShortEdit.Text<>FOldVisualValues[fpdiShort]) then debugln(['TFPDocEditor.GUIModified Short ',dbgstr(ShortEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiShort])]); if (LinkEdit.Text<>FOldVisualValues[fpdiElementLink]) then debugln(['TFPDocEditor.GUIModified link ',dbgstr(LinkEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiElementLink])]); if (DescrSynEdit.Text<>FOldVisualValues[fpdiDescription]) then debugln(['TFPDocEditor.GUIModified Descr ',dbgstr(DescrSynEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiDescription])]); if (SeeAlsoSynEdit.Text<>FOldVisualValues[fpdiSeeAlso]) then debugln(['TFPDocEditor.GUIModified SeeAlso ',dbgstr(SeeAlsoSynEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiSeeAlso])]); if (ErrorsSynEdit.Text<>FOldVisualValues[fpdiErrors]) then debugln(['TFPDocEditor.GUIModified Errors ',dbgstr(ErrorsSynEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiErrors])]); if (ExampleEdit.Text<>FOldVisualValues[fpdiExample]) then debugln(['TFPDocEditor.GUIModified Example ',dbgstr(ExampleEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiExample])]); end; end; procedure TFPDocEditor.DoEditorUpdate(Sender: TObject); begin if GetCaptureControl <> nil then // If SynEdit has Capture the user may be selecting by Mouse. https://bugs.freepascal.org/view.php?id=37150 exit; if FollowCursor then LoadIdentifierAtCursor; end; procedure TFPDocEditor.DoEditorMouseUp(Sender: TObject); begin if FollowCursor then LoadIdentifierAtCursor; end; procedure TFPDocEditor.UpdateTopicCombo; var cnt, i: LongInt; DFile: TLazFPDocFile; Topics: TStringList; begin Exclude(FFlags,fpdefTopicNeedsUpdate); Topics:=TStringList.Create; Include(FFlags,fpdefTopicSettingUp); try Dfile := DocFile; if DFile<>nil then begin cnt := DFile.GetModuleTopicCount; for i := 0 to cnt - 1 do Topics.Add(DFile.GetModuleTopicName(i)); end; TopicListBox.Items.Assign(Topics); TopicListBox.ItemIndex:=TopicListBox.Items.IndexOf(FCurrentTopic); UpdateTopic; finally Exclude(FFlags,fpdefTopicSettingUp); Topics.Free; end; end; procedure TFPDocEditor.SetIdleConnected(AValue: boolean); begin if FIdleConnected=AValue then Exit; FIdleConnected:=AValue; if IdleConnected then Application.AddOnIdleHandler(@ApplicationIdle) else Application.RemoveOnIdleHandler(@ApplicationIdle); end; procedure TFPDocEditor.SetFollowCursor(AValue: boolean); begin if FFollowCursor=AValue then Exit; FFollowCursor:=AValue; if FollowCursor then LoadIdentifierAtCursor; end; function TFPDocEditor.GetDefaultDocFile(CreateIfNotExists: Boolean): TLazFPDocFile; var CacheWasUsed : Boolean; AnOwner: TObject; FPDocFileName: String; begin Result := nil; if (not CreateIfNotExists) and (fDocFile<>nil) then exit(fDocFile); FPDocFileName := CodeHelpBoss.GetFPDocFilenameForSource(SourceFilename, true, CacheWasUsed, AnOwner, CreateIfNotExists); if (FPDocFileName = '') or (CodeHelpBoss.LoadFPDocFile(FPDocFileName, [chofUpdateFromDisk], Result, CacheWasUsed) <> chprSuccess) then Result := nil; end; procedure TFPDocEditor.Reset; var i: TFPDocItem; begin FreeAndNil(fChain); if fpdefReading in FFlags then exit; Include(FFlags,fpdefReading); try // clear all element editors/viewers ShortEdit.Clear; DescrShortEdit.Clear; LinkEdit.Clear; DescrSynEdit.Clear; SeeAlsoSynEdit.Clear; ErrorsSynEdit.Clear; ExampleEdit.Clear; ClearTopicControls; for i:=Low(TFPDocItem) to high(TFPDocItem) do FOldVisualValues[i]:=''; Modified := False; //CreateButton.Enabled:=false; OpenXMLButton.Enabled:=false; finally Exclude(FFlags,fpdefReading); end; end; procedure TFPDocEditor.InvalidateChain; begin FreeAndNil(fChain); FFlags:=FFlags+[fpdefCodeCacheNeedsUpdate, fpdefChainNeedsUpdate,fpdefCaptionNeedsUpdate, fpdefValueControlsNeedsUpdate,fpdefInheritedControlsNeedsUpdate]; IdleConnected:=true; end; procedure TFPDocEditor.LoadIdentifierAt(const SrcFilename: string; const Caret: TPoint); var NewSrcFilename: String; begin //debugln(['TFPDocEditor.LoadIdentifierAt START ',SrcFilename,' ',dbgs(Caret)]); // save the current changes to documentation Save(IsVisible); NewSrcFilename:=TrimAndExpandFilename(SrcFilename); if (NewSrcFilename=SourceFilename) and (CompareCaret(Caret,CaretXY)=0) and (fChain<>nil) and fChain.IsValid and (not LazarusIDE.NeedSaveSourceEditorChangesToCodeCache(nil)) then exit; FCaretXY:=Caret; fSourceFilename:=NewSrcFilename; Reset; Include(FFlags,fpdefTopicNeedsUpdate); InvalidateChain; end; procedure TFPDocEditor.LoadIdentifierAtCursor; var SrcEdit: TSourceEditorInterface; begin if SourceEditorManagerIntf=nil then exit; if csDestroying in ComponentState then exit; if FFlags*[fpdefReading,fpdefWriting]<>[] then exit; SrcEdit:=SourceEditorManagerIntf.ActiveEditor; if SrcEdit=nil then Reset else LoadIdentifierAt(SrcEdit.FileName,SrcEdit.CursorTextXY); end; procedure TFPDocEditor.BeginUpdate; begin inc(fUpdateLock); end; procedure TFPDocEditor.EndUpdate; begin dec(fUpdateLock); if fUpdateLock<0 then RaiseGDBException(''); if fUpdateLock=0 then begin if fpdefCaptionNeedsUpdate in FFlags then UpdateCaption; end; end; procedure TFPDocEditor.ClearEntry(DoSave: Boolean); begin Modified:=true; ShortEdit.Text:=''; DescrShortEdit.Text:=ShortEdit.Text; DescrSynEdit.Text:=''; SeeAlsoSynEdit.Text:=''; ErrorsSynEdit.Text:=''; ExampleEdit.Text:=''; if DoSave then Save; end; procedure TFPDocEditor.Save(CheckGUI: boolean); var Values: TFPDocElementValues; TopicDocFile: TLazFPDocFile; Node: TDOMNode; Child: TDOMNode; TopicChanged: Boolean; begin //DebugLn(['TFPDocEditor.Save FModified=',FModified]); if fpdefReading in FFlags then exit; if (not FModified) and ((not CheckGUI) or (not GUIModified)) then begin SaveButton.Enabled:=false; Exit; // nothing changed => exit end; //DebugLn(['TFPDocEditor.Save FModified=',FModified,' CheckGUI=',CheckGUI,' GUIModified=',GUIModified]); FModified:=false; SaveButton.Enabled:=false; TopicChanged:=false; TopicDocFile:=DocFile; if FCurrentTopic <> '' then begin if fDocFile=nil then fDocFile := GetDefaultDocFile(True); TopicDocFile:=DocFile; if TopicDocFile <> nil then begin Node := TopicDocFile.GetModuleTopic(FCurrentTopic); if Node <> nil then begin Child := Node.FindNode('short'); if (Child = nil) or (TopicDocFile.GetChildValuesAsString(Child)<>TopicShort.Text) then begin TopicDocFile.SetChildValue(Node, 'short', TopicShort.Text); TopicChanged:=true; end; Child := Node.FindNode('descr'); if (Child = nil) or (TopicDocFile.GetChildValuesAsString(Child)<>TopicDescrSynEdit.Text) then begin TopicDocFile.SetChildValue(Node, 'descr', TopicDescrSynEdit.Text); TopicChanged:=true; end; end; end; end; if (fChain=nil) or (fChain.Count=0) then begin if IsVisible then DebugLn(['TFPDocEditor.Save failed: no chain']); end else if not fChain.IsValid then begin if IsVisible then DebugLn(['TFPDocEditor.Save failed: chain not valid']); end else if (fChain[0].FPDocFile <> nil) then begin Values:=GetGUIValues; if WriteNode(fChain[0],Values,true) then begin // write succeeded if fChain.DocFile=TopicDocFile then TopicChanged:=false; end else begin DebugLn(['TFPDocEditor.Save WriteNode FAILED']); end; end; if TopicChanged then begin Include(FFlags,fpdefWriting); try CodeHelpBoss.SaveFPDocFile(TopicDocFile); finally Exclude(FFlags,fpdefWriting); end; end; end; function TFPDocEditor.GetGUIValues: TFPDocElementValues; var i: TFPDocItem; begin Result[fpdiShort]:=ShortEdit.Text; Result[fpdiDescription]:=DescrSynEdit.Text; Result[fpdiErrors]:=ErrorsSynEdit.Text; Result[fpdiSeeAlso]:=SeeAlsoSynEdit.Text; Result[fpdiExample]:=ExampleEdit.Text; Result[fpdiElementLink]:=LinkEdit.Text; for i:=Low(TFPDocItem) to High(TFPDocItem) do if Trim(Result[i])='' then Result[i]:=''; end; procedure TFPDocEditor.SetModified(const AValue: boolean); begin if FModified=AValue then exit; FModified:=AValue; SaveButton.Enabled:=FModified; //debugln(['TFPDocEditor.SetModified New=',FModified]); end; procedure TFPDocEditor.UpdateTopic; var Child: TDOMNode; Node: TDOMNode; DFile: TLazFPDocFile; begin FCurrentTopic := ''; try if TopicListBox.ItemIndex < 0 then exit; Dfile := GetDefaultDocFile(True); if DFile = nil then exit; FCurrentTopic := TopicListBox.Items[TopicListBox.ItemIndex]; Node := DFile.GetModuleTopic(FCurrentTopic); if Node = nil then exit; Include(FFlags, fpdefTopicSettingUp); try Child := Node.FindNode('short'); if Child <> nil then TopicShort.Text := DFile.GetChildValuesAsString(Child); Child := Node.FindNode('descr'); if Child <> nil then TopicDescrSynEdit.Text := DFile.GetChildValuesAsString(Child); TopicShort.Enabled := True; TopicDescrSynEdit.Enabled := True; if TopicShort.IsVisible then TopicShort.SetFocus; finally Exclude(FFlags, fpdefTopicSettingUp); end; finally if FCurrentTopic='' then ClearTopicControls; end; end; procedure TFPDocEditor.UpdateShowing; begin inherited UpdateShowing; if IsVisible and (fpdefWasHidden in FFlags) then begin Exclude(FFlags,fpdefWasHidden); LoadIdentifierAtCursor; end; end; procedure TFPDocEditor.Loaded; begin inherited Loaded; DescrSynEdit.ControlStyle:=DescrSynEdit.ControlStyle+[]; end; function TFPDocEditor.WriteNode(Element: TCodeHelpElement; Values: TFPDocElementValues; Interactive: Boolean): Boolean; var TopNode: TDOMNode; CurDocFile: TLazFPDocFile; CurDoc: TXMLDocument; function Check(Test: boolean; const Msg: string): Boolean; var CurName: String; begin Result:=Test; if not Test then exit; DebugLn(['TFPDocEditor.WriteNode ERROR ',Msg]); if Interactive then begin; if Element.FPDocFile<>nil then CurName:=Element.FPDocFile.Filename else CurName:=Element.ElementName; IDEMessageDialog(lisCodeToolsDefsWriteError, Format(lisFPDocErrorWriting, [CurName, LineEnding, Msg]), mtError, [mbCancel]); end; end; function SetValue(Item: TFPDocItem): boolean; var NewValue: String; begin Result:=false; NewValue:=Values[Item]; try FixFPDocFragment(NewValue, Item in [fpdiShort,fpdiDescription,fpdiErrors,fpdiSeeAlso], true); CurDocFile.SetChildValue(TopNode,FPDocItemNames[Item],NewValue); Result:=true; except on E: EXMLReadError do begin DebugLn(['SetValue ',dbgs(E.LineCol),' Name=',FPDocItemNames[Item]]); JumpToError(Item,E.LineCol); IDEMessageDialog(lisFPDocFPDocSyntaxError, Format(lisFPDocThereIsASyntaxErrorInTheFpdocElement, [FPDocItemNames [Item], LineEnding+LineEnding, E.Message]), mtError, [mbOk], ''); end; end; end; begin Result:=false; if fpdefWriting in FFlags then begin DebugLn(['TFPDocEditForm.WriteNode inconsistency detected: recursive write']); exit; end; if Check(Element=nil,'Element=nil') then exit; CurDocFile:=Element.FPDocFile; if Check(CurDocFile=nil,'Element.FPDocFile=nil') then begin // no fpdoc file found DebugLn(['TFPDocEditForm.WriteNode TODO: implement creating new fpdoc file']); exit; end; CurDoc:=CurDocFile.Doc; if Check(CurDoc=nil,'Element.FPDocFile.Doc=nil') then exit; if Check(not Element.ElementNodeValid,'not Element.ElementNodeValid') then exit; TopNode:=Element.ElementNode; if Check(TopNode=nil,'TopNode=nil') then begin // no old node found Check(false,'no old node found. TODO: implement creating a new.'); Exit; end; Include(FFlags,fpdefWriting); CurDocFile.BeginUpdate; try if SetValue(fpdiShort) and SetValue(fpdiElementLink) and SetValue(fpdiDescription) and SetValue(fpdiErrors) and SetValue(fpdiSeeAlso) and SetValue(fpdiExample) then ; finally CurDocFile.EndUpdate; fChain.MakeValid; Exclude(FFlags,fpdefWriting); end; if CodeHelpBoss.SaveFPDocFile(CurDocFile)<>mrOk then begin DebugLn(['TFPDocEditForm.WriteNode failed writing ',CurDocFile.Filename]); exit; end; Result:=true; end; procedure TFPDocEditor.UpdateCodeCache; begin if fUpdateLock>0 then begin Include(FFlags,fpdefCodeCacheNeedsUpdate); exit; end; Exclude(FFlags,fpdefCodeCacheNeedsUpdate); LazarusIDE.SaveSourceEditorChangesToCodeCache(nil); end; procedure TFPDocEditor.ErrorsSynEditChange(Sender: TObject); begin if fpdefReading in FFlags then exit; if ErrorsSynEdit.Text<>FOldVisualValues[fpdiErrors] then Modified:=true; end; procedure TFPDocEditor.ExampleEditChange(Sender: TObject); begin if fpdefReading in FFlags then exit; if ExampleEdit.Text<>FOldVisualValues[fpdiExample] then Modified:=true; end; function TFPDocEditor.FindInheritedIndex: integer; // returns Index in chain of an overriden Element with a short description // returns -1 if not found var Element: TCodeHelpElement; begin if (fChain<>nil) then begin Result:=1; while (Resultnil) and (Element.FPDocFile.GetValueFromNode(Element.ElementNode,fpdiShort)<>'') then exit; inc(Result); end; end; Result:=-1; end; procedure TFPDocEditor.AddLinkToInheritedButtonClick(Sender: TObject); var i: LongInt; Element: TCodeHelpElement; Link: String; begin i:=FindInheritedIndex; if i<0 then exit; //DebugLn(['TFPDocEditor.AddLinkToInheritedButtonClick ']); Element:=fChain[i]; Link:=Element.ElementName; if Element.ElementUnitName<>'' then begin Link:=Element.ElementUnitName+'.'+Link; if Element.ElementFPDocPackageName<>'' then Link:='#'+Element.ElementFPDocPackageName+'.'+Link; end; if Link<>LinkEdit.Text then begin LinkEdit.Text:=Link; Modified:=true; end; end; procedure TFPDocEditor.BrowseExampleButtonClick(Sender: TObject); begin if Doc=nil then exit; InitIDEFileDialog(OpenDialog); OpenDialog.Title:=lisChooseAnExampleFile; OpenDialog.Filter:=dlgFilterPascalFile+'|*.pas;*.pp;*.p|'+dlgFilterAll+'|'+FileMask; OpenDialog.InitialDir:=ExtractFilePath(DocFile.Filename); if OpenDialog.Execute then begin ExampleEdit.Text := ExtractRelativepath( ExtractFilePath(DocFile.Filename), GetForcedPathDelims(OpenDialog.FileName)); if ExampleEdit.Text<>FOldVisualValues[fpdiExample] then Modified:=true; end; StoreIDEFileDialog(OpenDialog); end; procedure TFPDocEditor.CopyFromInheritedButtonClick(Sender: TObject); var i: LongInt; begin i:=FindInheritedIndex; if i<0 then exit; //DebugLn(['TFPDocEditForm.CopyFromInheritedButtonClick ']); if ShortEdit.Text<>'' then begin if IDEQuestionDialog('Confirm replace', GetContextTitle(fChain[0])+' already contains the help:'+LineEnding+ShortEdit.Text, mtConfirmation, [mrYes,'Replace', mrCancel]) <> mrYes then exit; end; LoadGUIValues(fChain[i]); Modified:=true; end; procedure TFPDocEditor.CopyShortToDescrMenuItemClick(Sender: TObject); begin DescrSynEdit.Append(ShortEdit.Text); Modified:=true; end; procedure TFPDocEditor.CreateButtonClick(Sender: TObject); begin if ((fChain=nil) or (fChain.Count=0)) or (TCodeHelpElement(fChain[0]).ElementName='') then begin IDEMessageDialog('Invalid Declaration','Please place the editor caret on an identifier. If this is a new unit, please save the file first.', mtError,[mbOK]); exit; end; CreateElement(fChain[0]); end; procedure TFPDocEditor.DescrSynEditChange(Sender: TObject); begin if fpdefReading in FFlags then exit; if DescrSynEdit.Text<>FOldVisualValues[fpdiDescription] then Modified:=true; end; end.