lazarus/ide/fpdoceditwindow.pas

1615 lines
49 KiB
ObjectPascal

{
***************************************************************************
* *
* 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 <http://www.gnu.org/copyleft/gpl.html>. 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, LazIDEIntf, Menus,
SrcEditorIntf, IDEDialogs, LazFileUtils, IDEImagesIntf,
// IDE
IDEOptionDefs, EnvironmentOpts, 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('<b>', '</b>');
//italic
1:
InsertTag('<i>', '</i>');
//underline
2:
InsertTag('<u>', '</u>');
//code tag
3:
InsertTag('<p><code>', '</code></p>');
//remark tag
4:
InsertTag('<p><remark>', '</remark></p>');
//var tag
5:
InsertTag('<var>', '</var>');
//paragraph tag
6:
InsertTag('<p>', '</p>');
//printshort
7:
if (fChain<>nil) and (fChain.Count>0) then
InsertTag('<printshort id="'+fChain[0].ElementName+'"/>','');
//url tag
8:
InsertTag('<url href="">', '</url>');
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:='<link id="'+Link+'"';
if LinkTitle='' then begin
LinkSrc:=LinkSrc+'/>';
end else begin
LinkSrc:=LinkSrc+'>'+LinkTitle+'</link>';
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:
// <link id="TCustomControl"/>
// <link id="#lcl.Graphics.TCanvas">TCanvas</link>
var
StartPos: Integer;
EndPos: LongInt;
begin
Result:=false;
ID:='';
Title:='';
StartPos:=length('<link id="')+1;
if copy(LinkTag,1,StartPos-1)<>'<link id="' then
exit;
EndPos:=StartPos;
while (EndPos<=length(LinkTag)) do begin
if LinkTag[EndPos]='"' then begin
ID:=copy(LinkTag,StartPos,EndPos-StartPos);
Title:='';
Result:=true;
// extract title
StartPos:=EndPos;
while (StartPos<=length(LinkTag)) and (LinkTag[StartPos]<>'>') 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 (Result<fChain.Count) do begin
Element:=fChain[Result];
if (Element.ElementNode<>nil)
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.