{ *************************************************************************** * * * 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, FindDeclarationTool, CodeTree, // IDEIntf IDEWindowIntf, LazIDEIntf, Menus, SrcEditorIntf, IDEDialogs, LazFileUtils, IDEImagesIntf, IDEHelpIntf, // IdeUtils IdeUtilsPkgStrConsts, // IDE IDEOptionDefs, EnvironmentOpts, LazarusIDEStrConsts, FPDocSelectInherited, FPDocSelectLink, CodeHelp; type TFPDocEditorFlag = ( fpdefReading, fpdefWriting, fpdefCodeCacheNeedsUpdate, fpdefChainNeedsUpdate, fpdefCaptionNeedsUpdate, fpdefButtonsNeedUpdate, 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 OnCustomButtonClick(Sender: TObject); 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 FCustomSpeedButtons: array of TSpeedButton; 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, 'inserturltag'); IDEImages.AssignImage(InsertLinkSpeedButton, 'insertlink'); IDEImages.AssignImage(InsertParagraphSpeedButton, 'insertparagraph'); IDEImages.AssignImage(InsertPrintShortSpeedButton, 'insertprintshort'); 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 [fpdefButtonsNeedUpdate,fpdefCaptionNeedsUpdate]*FFlags<>[] then begin if fpdefButtonsNeedUpdate in FFlags then UpdateButtons; if fpdefCaptionNeedsUpdate in FFlags then UpdateCaption; end 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; i: Integer; Btn: TSpeedButton; Cnt: SizeInt; begin if fUpdateLock>0 then begin Include(FFlags,fpdefButtonsNeedUpdate); exit; end; Exclude(FFlags,fpdefButtonsNeedUpdate); HasEdit:=(fChain<>nil) and (fChain.Count>0) and (TCodeHelpElement(fChain[0]).ElementName>''); if HasEdit then HasEdit:=(PageControl.ActivePage = ShortTabSheet) or (PageControl.ActivePage = DescrTabSheet) or (PageControl.ActivePage = SeeAlsoTabSheet) or (PageControl.ActivePage = ErrorsTabSheet) or (PageControl.ActivePage = TopicSheet); //debugln(['TFPDocEditor.UpdateButtons Caption=',Caption,' Chain=',fChain<>nil,' Page=',DbgSName(PageControl.ActivePage),' HasEdit=',HasEdit]); BoldFormatButton.Enabled:=HasEdit; ItalicFormatButton.Enabled:=HasEdit; UnderlineFormatButton.Enabled:=HasEdit; InsertCodeTagButton.Enabled:=HasEdit; InsertLinkSpeedButton.Enabled:=HasEdit; InsertParagraphSpeedButton.Enabled:=HasEdit; InsertRemarkButton.Enabled:=HasEdit; InsertVarTagButton.Enabled:=HasEdit; InsertPrintShortSpeedButton.Enabled:=HasEdit; InsertURLTagSpeedButton.Enabled:=HasEdit; Cnt:=length(LazarusHelp.FPDocEditorTextBtnHandlers); for i:=0 to Cnt-1 do begin //debugln(['TFPDocEditor.UpdateButtons ',i,' ',LazarusHelp.FPDocEditorTextBtnHandlers[i].Caption]); if i''; Btn.Enabled:=HasEdit; end; for i:=length(FCustomSpeedButtons)-1 downto Cnt do FCustomSpeedButtons[i].Free; SetLength(FCustomSpeedButtons,Cnt); //debugln(['TFPDocEditor.UpdateButtons END']); 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,fpdefButtonsNeedUpdate, 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 LazTracer.RaiseGDBException(''); if fUpdateLock=0 then begin if fpdefButtonsNeedUpdate in FFlags then UpdateButtons; 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; UpdateButtons; 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.OnCustomButtonClick(Sender: TObject); var Btn: TSpeedButton; OnExec: TFPDocEditorTxtBtnClick; Params: TFPDocEditorTxtBtnParams; Element: TCodeHelpElement; Tool: TFindDeclarationTool; Node: TCodeTreeNode; Caret: TCodeXYPosition; begin Btn:=TSpeedButton(Sender); if Btn.Tag>=length(LazarusHelp.FPDocEditorTextBtnHandlers) then exit; OnExec:=LazarusHelp.FPDocEditorTextBtnHandlers[Btn.Tag].OnExecute; if not Assigned(OnExec) then exit; UpdateCodeCache; UpdateChain; if (fChain=nil) or (fChain.Count=0) then exit; Params:=Default(TFPDocEditorTxtBtnParams); Element:=fChain[0]; Tool:=Element.CodeContext.Tool; Node:=Element.CodeContext.Node; Params.CodeTool:=Tool; Params.CodeNode:=Node; if (Tool<>nil) and (Node<>nil) then begin Element.CodeContext.Tool.CleanPosToCaret(Node.StartPos,Caret); Params.CodeBuf:=Caret.Code; Params.Filename:=Caret.Code.Filename; Params.Line:=Caret.Y; Params.Col:=Caret.X; end; if PageControl.ActivePage = ShortTabSheet then begin Params.Part:=fpdepShortDesc; Params.Selection:=ShortEdit.SelText; end else if PageControl.ActivePage = DescrTabSheet then begin Params.Part:=fpdepDescription; Params.Selection:=DescrSynEdit.SelText; end else if PageControl.ActivePage = ErrorsTabSheet then begin Params.Part:=fpdepErrors; Params.Selection:=ErrorsSynEdit.SelText; end else if PageControl.ActivePage = TopicSheet then begin if (FLastTopicControl = TopicShort) then begin Params.Part:=fpdepTopicShort; Params.Selection:=TopicShort.SelText; end else if (FLastTopicControl = TopicDescrSynEdit) then begin Params.Part:=fpdepTopicDesc; Params.Selection:=TopicDescrSynEdit.SelText; end else exit; end else exit; OnExec(Params); if not Params.Success then exit; // insert text case Params.Part of fpdepShortDesc: begin ShortEdit.SelText := Params.Selection; DescrShortEdit.Text:=ShortEdit.Text; end; fpdepDescription: DescrSynEdit.SelText := Params.Selection; fpdepErrors: ErrorsSynEdit.SelText := Params.Selection; fpdepTopicShort: TopicShort.SelText := Params.Selection; fpdepTopicDesc: TopicDescrSynEdit.SelText := Params.Selection; end; Modified:=true; 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(lisConfirmReplace, Format(lisAlreadyContainsTheHe, [GetContextTitle(fChain[0]), ShortEdit.Text]), mtConfirmation, [mrYes, lisReplace, 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(lisInvalidDeclaration, lisPleasePlaceTheEditorCaretOnAnIdentifierIfThisIsANe, 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.