1 {
2 ***************************************************************************
3 * *
4 * This source is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This code is distributed in the hope that it will be useful, but *
10 * WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * General Public License for more details. *
13 * *
14 * A copy of the GNU General Public License is available on the World *
15 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
16 * obtain it by writing to the Free Software Foundation, *
17 * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
18 * *
19 ***************************************************************************
20
21 see for todo list: http://wiki.lazarus.freepascal.org/index.php/LazDoc
22 }
23
24 unit FPDocEditWindow;
25
26 {$mode objfpc}{$H+}
27
28 {off $define VerboseCodeHelp}
29
30 interface
31
32 uses
33 // FCL
34 Classes, SysUtils,
35 // LazUtils
36 Laz2_DOM, Laz2_XMLRead, LazStringUtils, LazTracer,
37 // LCL
38 LResources, StdCtrls, Buttons, ComCtrls, Controls, Dialogs,
39 ExtCtrls, Forms, Graphics, LCLType, LCLProc,
40 // Synedit
41 SynEdit, SynHighlighterXML, SynEditFoldedView, SynEditWrappedView,
42 // codetools
43 FileProcs, CodeCache, CodeToolManager, CTXMLFixFragment,
44 // IDEIntf
45 IDEWindowIntf, ProjectIntf, LazIDEIntf, IDEHelpIntf, Menus,
46 SrcEditorIntf, IDEDialogs, LazFileUtils, IDEImagesIntf,
47 // IDE
48 IDEOptionDefs, EnvironmentOpts, PackageSystem, LazarusIDEStrConsts,
49 FPDocSelectInherited, FPDocSelectLink, CodeHelp;
50
51 type
52 TFPDocEditorFlag = (
53 fpdefReading,
54 fpdefWriting,
55 fpdefCodeCacheNeedsUpdate,
56 fpdefChainNeedsUpdate,
57 fpdefCaptionNeedsUpdate,
58 fpdefValueControlsNeedsUpdate,
59 fpdefInheritedControlsNeedsUpdate,
60 fpdefTopicSettingUp,
61 fpdefTopicNeedsUpdate,
62 fpdefWasHidden
63 );
64 TFPDocEditorFlags = set of TFPDocEditorFlag;
65
66 { TFPDocEditor }
67
68 TFPDocEditor = class(TForm)
69 AddLinkToInheritedButton: TButton;
70 BoldFormatButton: TSpeedButton;
71 BrowseExampleButton: TButton;
72 OpenXMLButton: TButton;
73 ShortPanel: TPanel;
74 DescrShortEdit: TEdit;
75 SynXMLSyn1: TSynXMLSyn;
76 TopicShort: TEdit;
77 TopicDescrSynEdit: TSynEdit;
78 Panel3: TPanel;
79 TopicListBox: TListBox;
80 NewTopicNameEdit: TEdit;
81 NewTopicButton: TButton;
82 CopyFromInheritedButton: TButton;
83 CreateButton: TButton;
84 DescrSynEdit: TSynEdit;
85 DescrTabSheet: TTabSheet;
86 ErrorsSynEdit: TSynEdit;
87 ErrorsTabSheet: TTabSheet;
88 ExampleEdit: TEdit;
89 ExampleTabSheet: TTabSheet;
90 InheritedShortEdit: TEdit;
91 InheritedShortLabel: TLabel;
92 InheritedTabSheet: TTabSheet;
93 InsertCodeTagButton: TSpeedButton;
94 InsertLinkSpeedButton: TSpeedButton;
95 InsertParagraphSpeedButton: TSpeedButton;
96 InsertRemarkButton: TSpeedButton;
97 InsertVarTagButton: TSpeedButton;
98 ItalicFormatButton: TSpeedButton;
99 LeftBtnPanel: TPanel;
100 LinkEdit: TEdit;
101 LinkLabel: TLabel;
102 Panel1: TPanel;
103 Panel2: TPanel;
104 SaveButton: TSpeedButton;
105 SeeAlsoSynEdit: TSynEdit;
106 MoveToInheritedButton: TButton;
107 OpenDialog: TOpenDialog;
108 PageControl: TPageControl;
109 SeeAlsoTabSheet: TTabSheet;
110 ShortEdit: TEdit;
111 ShortLabel: TLabel;
112 ShortTabSheet: TTabSheet;
113 InsertPrintShortSpeedButton: TSpeedButton;
114 InsertURLTagSpeedButton: TSpeedButton;
115 TopicSheet: TTabSheet;
116 UnderlineFormatButton: TSpeedButton;
117 procedure AddLinkToInheritedButtonClick(Sender: TObject);
118 procedure ApplicationIdle(Sender: TObject; var Done: Boolean);
119 procedure BrowseExampleButtonClick(Sender: TObject);
120 procedure CopyFromInheritedButtonClick(Sender: TObject);
121 procedure CopyShortToDescrMenuItemClick(Sender: TObject);
122 procedure CreateButtonClick(Sender: TObject);
123 procedure DescrSynEditChange(Sender: TObject);
124 procedure ErrorsSynEditChange(Sender: TObject);
125 procedure ExampleEditChange(Sender: TObject);
126 procedure FormatButtonClick(Sender: TObject);
127 procedure FormCreate(Sender: TObject);
128 procedure FormDestroy(Sender: TObject);
129 procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
130 procedure FormShow(Sender: TObject);
131 procedure InsertLinkSpeedButtonClick(Sender: TObject);
132 procedure LinkEditChange(Sender: TObject);
133 procedure MoveToInheritedButtonClick(Sender: TObject);
134 procedure NewTopicButtonClick(Sender: TObject);
135 procedure OpenXMLButtonClick(Sender: TObject);
136 procedure PageControlChange(Sender: TObject);
137 procedure SaveButtonClick(Sender: TObject);
138 procedure SeeAlsoSynEditChange(Sender: TObject);
139 procedure ShortEditChange(Sender: TObject);
140 procedure TopicControlEnter(Sender: TObject);
141 procedure TopicDescrSynEditChange(Sender: TObject);
142 procedure TopicListBoxClick(Sender: TObject);
143 private
144 FCaretXY: TPoint;
145 FModified: Boolean;
146 FFlags: TFPDocEditorFlags;
147 fUpdateLock: Integer;
148 fSourceFilename: string;
149 fDocFile: TLazFPDocFile;
150 fChain: TCodeHelpElementChain;
151 FOldValues: TFPDocElementValues;
152 FOldVisualValues: TFPDocElementValues;
GetDocnull153 function GetDoc: TXMLdocument;
GetDocFilenull154 function GetDocFile: TLazFPDocFile;
GetSourceFilenamenull155 function GetSourceFilename: string;
GetFirstElementnull156 function GetFirstElement: TDOMNode;
157
GetContextTitlenull158 function GetContextTitle(Element: TCodeHelpElement): string;
159
FindInheritedIndexnull160 function FindInheritedIndex: integer;
161 procedure Save(CheckGUI: boolean = false);
GetGUIValuesnull162 function GetGUIValues: TFPDocElementValues;
163 procedure SetModified(const AValue: boolean);
WriteNodenull164 function WriteNode(Element: TCodeHelpElement; Values: TFPDocElementValues;
165 Interactive: Boolean): Boolean;
166 procedure UpdateCodeCache;
167 procedure UpdateChain;
168 procedure UpdateCaption;
169 procedure UpdateValueControls;
170 procedure UpdateInheritedControls;
171 procedure OnFPDocChanging(Sender: TObject; FPDocFPFile: TLazFPDocFile);
172 procedure OnFPDocChanged(Sender: TObject; FPDocFPFile: TLazFPDocFile);
173 procedure LoadGUIValues(Element: TCodeHelpElement);
174 procedure MoveToInherited(Element: TCodeHelpElement);
GetDefaultDocFilenull175 function GetDefaultDocFile(CreateIfNotExists: Boolean = False): TLazFPDocFile;
ExtractIDFromLinkTagnull176 function ExtractIDFromLinkTag(const LinkTag: string; out ID, Title: string): boolean;
CreateElementnull177 function CreateElement(Element: TCodeHelpElement): Boolean;
178 procedure UpdateButtons;
GetCurrentUnitNamenull179 function GetCurrentUnitName: string;
GetCurrentOwnerNamenull180 function GetCurrentOwnerName: string;
181 procedure JumpToError(Item : TFPDocItem; LineCol: TPoint);
182 procedure OpenXML;
GUIModifiednull183 function GUIModified: boolean;
184 procedure DoEditorUpdate(Sender: TObject);
185 procedure DoEditorMouseUp(Sender: TObject);
186 private
187 FFollowCursor: boolean;
188 FIdleConnected: boolean;
189 FLastTopicControl: TControl;
190 FCurrentTopic: String;
191 procedure SetFollowCursor(AValue: boolean);
192 procedure SetIdleConnected(AValue: boolean);
193 procedure UpdateTopicCombo;
194 procedure ClearTopicControls;
195 procedure UpdateTopic;
196 protected
197 procedure UpdateShowing; override;
198 procedure Loaded; override;
199 public
200 procedure Reset;
201 procedure InvalidateChain;
202 procedure LoadIdentifierAt(const SrcFilename: string; const Caret: TPoint);
203 procedure LoadIdentifierAtCursor;
204 procedure BeginUpdate;
205 procedure EndUpdate;
206 procedure ClearEntry(DoSave: Boolean);
207 property DocFile: TLazFPDocFile read GetDocFile;
208 property Doc: TXMLdocument read GetDoc;
209 property SourceFilename: string read GetSourceFilename;
210 property CaretXY: TPoint read FCaretXY;
211 property Modified: boolean read FModified write SetModified;
212 property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
213 property FollowCursor: boolean read FFollowCursor write SetFollowCursor;
214 end;
215
216 var
217 FPDocEditor: TFPDocEditor = nil;
218
219 procedure DoShowFPDocEditor(State: TIWGetFormState = iwgfShowOnTop);
220
221 implementation
222
223 {$R *.lfm}
224 {$R lazdoc.res}
225
226 { TFPDocEditor }
227
228 procedure DoShowFPDocEditor(State: TIWGetFormState);
229 begin
230 if FPDocEditor = Nil then
231 IDEWindowCreators.CreateForm(FPDocEditor,TFPDocEditor,
232 State=iwgfDisabled,LazarusIDE.OwningComponent)
233 else if State=iwgfDisabled then
234 FPDocEditor.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('DoShowFPDocEditor'){$ENDIF};
235
236 if State>=iwgfShow then
237 IDEWindowCreators.ShowForm(FPDocEditor,State=iwgfShowOnTop);
238 end;
239
GetFirstElementnull240 function TFPDocEditor.GetFirstElement: TDOMNode;
241 var
242 CurDocFile: TLazFPDocFile;
243 begin
244 Result:=nil;
245 CurDocFile:=DocFile;
246 if CurDocFile=nil then exit;
247 Result:=CurDocFile.GetFirstElement;
248 end;
249
250 procedure TFPDocEditor.FormCreate(Sender: TObject);
251 procedure UpdateSynEdit(ASynEd: TSynEdit);
252 var
253 fld: TSynEditFoldedView;
254 begin
255 fld := TSynEditFoldedView(ASynEd.TextViewsManager.SynTextViewByClass[TSynEditFoldedView]);
256 if fld <> nil then
257 fld.FoldProvider.Enabled := False;
258 TLazSynEditLineWrapPlugin.Create(ASynEd);
259 end;
260 begin
261 Caption := lisCodeHelpMainFormCaption;
262
263 ShortTabSheet.Caption := lisCodeHelpShortTag;
264 InheritedTabSheet.Caption := lisCodeHelpInherited;
265 DescrTabSheet.Caption := lisCodeHelpDescrTag;
266 ErrorsTabSheet.Caption := lisCodeHelpErrorsTag;
267 SeeAlsoTabSheet.Caption := lisCodeHelpSeeAlsoTag;
268 ExampleTabSheet.Caption := lisCodeHelpExampleTag;
269
270 PageControl.PageIndex := 0;
271
272 BoldFormatButton.Hint := lisCodeHelpHintBoldFormat;
273 ItalicFormatButton.Hint := lisCodeHelpHintItalicFormat;
274 UnderlineFormatButton.Hint := lisCodeHelpHintUnderlineFormat;
275 InsertCodeTagButton.Hint := lisCodeHelpHintInsertCodeTag;
276 InsertRemarkButton.Hint := lisCodeHelpHintRemarkTag;
277 InsertVarTagButton.Hint := lisCodeHelpHintVarTag;
278 InsertParagraphSpeedButton.Hint := lisCodeHelpInsertParagraphFormattingTag;
279 InsertLinkSpeedButton.Hint := lisCodeHelpInsertALink;
280 InsertPrintShortSpeedButton.Hint:=lisInsertPrintshortTag2;
281 InsertURLTagSpeedButton.Hint:=lisInsertUrlTag;
282
283 ShortLabel.Caption:=lisShort;
284 LinkLabel.Caption:=lisLink;
285 CreateButton.Caption := lisCodeHelpCreateButton;
286 OpenXMLButton.Caption:=lisOpenXML;
287 OpenXMLButton.Enabled:=false;
288 SaveButton.Caption := '';
289 SaveButton.Enabled:=false;
290 SaveButton.Hint:=lisSave;
291 SaveButton.ShowHint:=true;
292
293 BrowseExampleButton.Caption := lisCodeHelpBrowseExampleButton;
294
295 MoveToInheritedButton.Caption:=lisLDMoveEntriesToInherited;
296 CopyFromInheritedButton.Caption:=lisLDCopyFromInherited;
297 AddLinkToInheritedButton.Caption:=lisLDAddLinkToInherited;
298
299 Reset;
300
301 CodeHelpBoss.AddHandlerOnChanging(@OnFPDocChanging);
302 CodeHelpBoss.AddHandlerOnChanged(@OnFPDocChanged);
303
304 Name := NonModalIDEWindowNames[nmiwFPDocEditor];
305
306 IDEImages.AssignImage(BoldFormatButton, 'formatbold');
307 IDEImages.AssignImage(UnderlineFormatButton, 'formatunderline');
308 IDEImages.AssignImage(ItalicFormatButton, 'formatitalic');
309 IDEImages.AssignImage(InsertVarTagButton, 'insertvartag');
310 IDEImages.AssignImage(InsertCodeTagButton, 'insertcodetag');
311 IDEImages.AssignImage(InsertRemarkButton, 'insertremark');
312 IDEImages.AssignImage(InsertURLTagSpeedButton, 'formatunderline');
313 IDEImages.AssignImage(SaveButton, 'laz_save');
314
315 SourceEditorManagerIntf.RegisterChangeEvent(semEditorActivate, @DoEditorUpdate);
316 SourceEditorManagerIntf.RegisterChangeEvent(semEditorStatus, @DoEditorUpdate);
317 SourceEditorManagerIntf.RegisterChangeEvent(semEditorMouseUp, @DoEditorMouseUp);
318
319 UpdateSynEdit(TopicDescrSynEdit);
320 UpdateSynEdit(DescrSynEdit);
321 UpdateSynEdit(ErrorsSynEdit);
322 UpdateSynEdit(SeeAlsoSynEdit);
323
324 FollowCursor:=true;
325 IdleConnected:=true;
326 end;
327
328 procedure TFPDocEditor.FormDestroy(Sender: TObject);
329 begin
330 IdleConnected:=false;
331 Reset;
332 FreeAndNil(fChain);
333 if assigned(CodeHelpBoss) then
334 CodeHelpBoss.RemoveAllHandlersOfObject(Self);
335 Application.RemoveAllHandlersOfObject(Self);
336 if SourceEditorManagerIntf<>nil then begin
337 SourceEditorManagerIntf.UnRegisterChangeEvent(semEditorActivate, @DoEditorUpdate);
338 SourceEditorManagerIntf.UnRegisterChangeEvent(semEditorStatus, @DoEditorUpdate);
339 end;
340 end;
341
342 procedure TFPDocEditor.FormKeyDown(Sender: TObject; var Key: Word;
343 Shift: TShiftState);
344 begin
345 if (Key=VK_S) and (Shift=[ssCtrl]) then begin
346 Save(true);
347 Key:=VK_UNKNOWN;
348 end;
349 end;
350
351 procedure TFPDocEditor.FormShow(Sender: TObject);
352 begin
353 DoEditorUpdate(nil);
354 end;
355
356 procedure TFPDocEditor.FormatButtonClick(Sender: TObject);
357
358 procedure InsertTag(const StartTag, EndTag: String);
359 begin
360 if PageControl.ActivePage = ShortTabSheet then begin
361 ShortEdit.SelText := StartTag + ShortEdit.SelText + EndTag;
362 DescrShortEdit.Text:=ShortEdit.Text;
363 end else if PageControl.ActivePage = DescrTabSheet then
364 DescrSynEdit.SelText := StartTag + DescrSynEdit.SelText + EndTag
365 else if PageControl.ActivePage = ErrorsTabSheet then
366 ErrorsSynEdit.SelText := StartTag + ErrorsSynEdit.SelText + EndTag
367 else if PageControl.ActivePage = TopicSheet then begin
368 if (FLastTopicControl = TopicShort) then
369 TopicShort.SelText := StartTag + TopicShort.SelText + EndTag;
370 if (FLastTopicControl = TopicDescrSynEdit) then
371 TopicDescrSynEdit.SelText := StartTag + TopicDescrSynEdit.SelText + EndTag;
372 end
373 else
374 exit;
375 Modified:=true;
376 end;
377
378 begin
379 case TSpeedButton(Sender).Tag of
380 //bold
381 0:
382 InsertTag('<b>', '</b>');
383 //italic
384 1:
385 InsertTag('<i>', '</i>');
386 //underline
387 2:
388 InsertTag('<u>', '</u>');
389 //code tag
390 3:
391 InsertTag('<p><code>', '</code></p>');
392 //remark tag
393 4:
394 InsertTag('<p><remark>', '</remark></p>');
395 //var tag
396 5:
397 InsertTag('<var>', '</var>');
398 //paragraph tag
399 6:
400 InsertTag('<p>', '</p>');
401 //printshort
402 7:
403 if (fChain<>nil) and (fChain.Count>0) then
404 InsertTag('<printshort id="'+fChain[0].ElementName+'"/>','');
405 //url tag
406 8:
407 InsertTag('<url href="">', '</url>');
408 end;
409 end;
410
411 procedure TFPDocEditor.InsertLinkSpeedButtonClick(Sender: TObject);
412 var
413 Link: string;
414 LinkTitle: string;
415 LinkSrc: String;
416 begin
417 if ShowFPDocLinkEditorDialog(fSourceFilename,DocFile,Link,LinkTitle)<>mrOk then exit;
418 if Link='' then exit;
419 LinkSrc:='<link id="'+Link+'"';
420 if LinkTitle='' then begin
421 LinkSrc:=LinkSrc+'/>';
422 end else begin
423 LinkSrc:=LinkSrc+'>'+LinkTitle+'</link>';
424 end;
425 if PageControl.ActivePage = ShortTabSheet then begin
426 ShortEdit.SelText := LinkSrc;
427 DescrShortEdit.Text := ShortEdit.Text;
428 end;
429 if PageControl.ActivePage = DescrTabSheet then
430 DescrSynEdit.SelText := LinkSrc;
431 if PageControl.ActivePage = SeeAlsoTabSheet then
432 SeeAlsoSynEdit.SelText := LinkSrc;
433 if PageControl.ActivePage = ErrorsTabSheet then
434 ErrorsSynEdit.SelText := LinkSrc;
435 if PageControl.ActivePage = TopicSheet then begin
436 if (FLastTopicControl = TopicShort) then
437 TopicShort.SelText := LinkSrc;
438 if (FLastTopicControl = TopicDescrSynEdit) then
439 TopicDescrSynEdit.SelText := LinkSrc;
440 end;
441
442 Modified:=true;
443 end;
444
445 procedure TFPDocEditor.LinkEditChange(Sender: TObject);
446 begin
447 if fpdefReading in FFlags then exit;
448 if LinkEdit.Text<>FOldVisualValues[fpdiElementLink] then
449 Modified:=true;
450 end;
451
452 procedure TFPDocEditor.ApplicationIdle(Sender: TObject; var Done: Boolean);
453 var
454 ActiveForm: TCustomForm;
455 begin
456 if (fUpdateLock>0) then
457 begin
458 DebugLn(['WARNING: TFPDocEditor.ApplicationIdle fUpdateLock>0']);
459 exit;
460 end;
461 if not IsVisible then begin
462 Include(FFlags,fpdefWasHidden);
463 IdleConnected:=false;
464 exit;
465 end;
466 ActiveForm:=Screen.ActiveCustomForm;
467 if (ActiveForm<>nil) and (fsModal in ActiveForm.FormState) then exit;
468 Done:=false;
469 if fpdefCodeCacheNeedsUpdate in FFlags then
470 UpdateCodeCache
471 else if fpdefChainNeedsUpdate in FFlags then
472 UpdateChain
473 else if fpdefCaptionNeedsUpdate in FFlags then
474 UpdateCaption
475 else if fpdefValueControlsNeedsUpdate in FFlags then
476 UpdateValueControls
477 else if fpdefInheritedControlsNeedsUpdate in FFlags then
478 UpdateInheritedControls
479 else if fpdefTopicNeedsUpdate in FFlags then
480 UpdateTopicCombo
481 else begin
482 //debugln(['TFPDocEditor.ApplicationIdle updated']);
483 Done:=true;
484 IdleConnected:=false;
485 end;
486 end;
487
488 procedure TFPDocEditor.MoveToInheritedButtonClick(Sender: TObject);
489 var
490 i: Integer;
491 Element: TCodeHelpElement;
492 Candidates: TFPList;
493 FPDocSelectInheritedDlg: TFPDocSelectInheritedDlg;
494 ShortDescr: String;
495 begin
496 if fChain=nil then exit;
497 Candidates:=nil;
498 FPDocSelectInheritedDlg:=nil;
499 try
500 // find all entries till the first inherited entry with a description
501 for i:=1 to fChain.Count-1 do begin
502 Element:=fChain[i];
503 if Candidates=nil then
504 Candidates:=TFPList.Create;
505 Candidates.Add(Element);
506 if (Element.ElementNode<>nil)
507 and (Element.FPDocFile.GetValueFromNode(Element.ElementNode,fpdiShort)<>'')
508 then
509 break;
510 end;
511
512 // choose one entry
513 if (Candidates=nil) or (Candidates.Count=0) then exit;
514 if Candidates.Count=1 then begin
515 // there is only one candidate
516 Element:=TCodeHelpElement(Candidates[0]);
517 if (Element.ElementNode<>nil) then begin
518 ShortDescr:=Element.FPDocFile.GetValueFromNode(Element.ElementNode,fpdiShort);
519 if ShortDescr<>'' then begin
520 // the inherited entry already contains a description.
521 // ask if it should be really replaced
522 if IDEQuestionDialog(lisCodeHelpConfirmreplace,
523 GetContextTitle(Element)+' already contains the help:'+LineEnding+ShortDescr,
524 mtConfirmation, [mrYes, lisReplace,
525 mrCancel]) <> mrYes then exit;
526 end;
527 end;
528 end else begin
529 // there is more than one candidate
530 // => ask which one to replace
531 FPDocSelectInheritedDlg:=TFPDocSelectInheritedDlg.Create(nil);
532 FPDocSelectInheritedDlg.InheritedComboBox.Items.Clear;
533 for i:=0 to Candidates.Count-1 do begin
534 Element:=TCodeHelpElement(Candidates[i]);
535 FPDocSelectInheritedDlg.InheritedComboBox.Items.Add(
536 GetContextTitle(Element));
537 end;
538 if FPDocSelectInheritedDlg.ShowModal<>mrOk then exit;
539 i:=FPDocSelectInheritedDlg.InheritedComboBox.ItemIndex;
540 if i<0 then exit;
541 Element:=TCodeHelpElement(Candidates[i]);
542 end;
543
544 // move the content of the current entry to the inherited entry
545 MoveToInherited(Element);
546 finally
547 FPDocSelectInheritedDlg.Free;
548 Candidates.Free;
549 end;
550 end;
551
552 procedure TFPDocEditor.NewTopicButtonClick(Sender: TObject);
553 var
554 Dfile: TLazFPDocFile;
555 begin
556 if NewTopicNameEdit.Text = '' then exit;
557 Dfile := GetDefaultDocFile(True);
558 if not assigned(DFile) then exit;
559 if DFile.GetModuleTopic(NewTopicNameEdit.Text) = nil then begin
560 DFile.CreateModuleTopic(NewTopicNameEdit.Text);
561 CodeHelpBoss.SaveFPDocFile(DFile);
562 end;
563 UpdateTopicCombo;
564 TopicListBox.ItemIndex := TopicListBox.Items.IndexOf(NewTopicNameEdit.Text);
565 TopicListBoxClick(Sender);
566 end;
567
568 procedure TFPDocEditor.OpenXMLButtonClick(Sender: TObject);
569 begin
570 OpenXML;
571 end;
572
573 procedure TFPDocEditor.PageControlChange(Sender: TObject);
574 begin
575 UpdateButtons;
576 end;
577
578 procedure TFPDocEditor.SaveButtonClick(Sender: TObject);
579 begin
580 Save;
581 UpdateValueControls;
582 end;
583
584 procedure TFPDocEditor.SeeAlsoSynEditChange(Sender: TObject);
585 begin
586 if fpdefReading in FFlags then exit;
587 if SeeAlsoSynEdit.Text<>FOldVisualValues[fpdiSeeAlso] then
588 Modified:=true;
589 end;
590
591 procedure TFPDocEditor.ShortEditChange(Sender: TObject);
592 // called by ShortEdit and DescrShortEdit
593 var
594 NewShort: String;
595 begin
596 if fpdefReading in FFlags then exit;
597 //debugln(['TFPDocEditor.ShortEditChange ',DbgSName(Sender)]);
598 if Sender=DescrShortEdit then
599 NewShort:=DescrShortEdit.Text
600 else
601 NewShort:=ShortEdit.Text;
602 if NewShort<>FOldVisualValues[fpdiShort] then
603 Modified:=true;
604 // copy to the other edit
605 if Sender=DescrShortEdit then
606 ShortEdit.Text:=NewShort
607 else
608 DescrShortEdit.Text:=NewShort;
609 end;
610
611 procedure TFPDocEditor.TopicControlEnter(Sender: TObject);
612 begin
613 FLastTopicControl := TControl(Sender);
614 end;
615
616 procedure TFPDocEditor.TopicDescrSynEditChange(Sender: TObject);
617 begin
618 if fpdefReading in FFlags then exit;
619 if fpdefTopicSettingUp in FFlags then exit;
620 Modified := True;
621 end;
622
623 procedure TFPDocEditor.TopicListBoxClick(Sender: TObject);
624 begin
625 if fpdefTopicSettingUp in FFlags then exit;
626 if (FCurrentTopic <> '') and Modified then
627 Save;
628 UpdateTopic;
629 end;
630
GetContextTitlenull631 function TFPDocEditor.GetContextTitle(Element: TCodeHelpElement): string;
632 // get codetools path. for example: TButton.Align
633 begin
634 Result:='';
635 if Element=nil then exit;
636 Result:=Element.ElementName;
637 end;
638
TFPDocEditor.GetDocnull639 function TFPDocEditor.GetDoc: TXMLdocument;
640 begin
641 if DocFile<>nil then
642 Result:=DocFile.Doc
643 else
644 Result:=nil;
645 end;
646
647 procedure TFPDocEditor.ClearTopicControls;
648 var
649 OldSettingUp: boolean;
650 begin
651 OldSettingUp:=fpdefTopicSettingUp in FFlags;
652 Include(FFlags, fpdefTopicSettingUp);
653 try
654 TopicShort.Clear;
655 TopicDescrSynEdit.Clear;
656 TopicShort.Enabled := False;
657 TopicDescrSynEdit.Enabled := False;
658 finally
659 if not OldSettingUp then
660 Exclude(FFlags, fpdefTopicSettingUp);
661 end;
662 end;
663
GetDocFilenull664 function TFPDocEditor.GetDocFile: TLazFPDocFile;
665 begin
666 Result:=nil;
667 if fChain<>nil then
668 Result:=fChain.DocFile
669 else
670 Result:=fDocFile;
671 end;
672
GetSourceFilenamenull673 function TFPDocEditor.GetSourceFilename: string;
674 begin
675 Result:=fSourceFilename;
676 end;
677
678 procedure TFPDocEditor.UpdateCaption;
679 var
680 strCaption: String;
681 Filename: String;
682 begin
683 if fUpdateLock>0 then begin
684 Include(FFlags,fpdefCaptionNeedsUpdate);
685 exit;
686 end;
687 Exclude(FFlags,fpdefCaptionNeedsUpdate);
688
689 {$IFDEF VerboseCodeHelp}
690 DebugLn(['TFPDocEditForm.UpdateCaption START']);
691 {$ENDIF}
692 strCaption := lisCodeHelpMainFormCaption + ' - ';
693
694 if (fChain <> nil) and (fChain.Count>0) then
695 strCaption := strCaption + GetContextTitle(fChain[0]) + ' - '
696 else
697 strCaption := strCaption + lisCodeHelpNoTagCaption + ' - ';
698
699 if DocFile<>nil then begin
700 Filename:=DocFile.Filename;
701 if (LazarusIDE.ActiveProject<>nil) then
702 Filename:=LazarusIDE.ActiveProject.GetShortFilename(Filename,true);
703 Caption := strCaption + Filename;
704 end else
705 Caption := strCaption + lisCodeHelpNoTagCaption;
706 {$IFDEF VerboseCodeHelp}
707 DebugLn(['TFPDocEditor.UpdateCaption ',Caption]);
708 {$ENDIF}
709 end;
710
711 procedure TFPDocEditor.UpdateValueControls;
712 var
713 Element: TCodeHelpElement;
714 begin
715 if fUpdateLock>0 then begin
716 Include(FFlags,fpdefValueControlsNeedsUpdate);
717 exit;
718 end;
719 Exclude(FFlags,fpdefValueControlsNeedsUpdate);
720
721 {$IFDEF VerboseCodeHelp}
722 DebugLn(['TFPDocEditForm.UpdateValueControls START']);
723 {$ENDIF}
724 Element:=nil;
725 if (fChain<>nil) and (fChain.Count>0) then
726 Element:=fChain[0];
727 LoadGUIValues(Element);
728 SaveButton.Enabled:=FModified;
729 end;
730
731 procedure TFPDocEditor.UpdateInheritedControls;
732 var
733 i: LongInt;
734 Element: TCodeHelpElement;
735 ShortDescr: String;
736 begin
737 if fUpdateLock>0 then begin
738 Include(FFlags,fpdefInheritedControlsNeedsUpdate);
739 exit;
740 end;
741 Exclude(FFlags,fpdefInheritedControlsNeedsUpdate);
742
743 {$IFDEF VerboseCodeHelp}
744 DebugLn(['TFPDocEditForm.UpdateInheritedControls START']);
745 {$ENDIF}
746 i:=FindInheritedIndex;
747 if i<0 then begin
748 InheritedShortEdit.Text:='';
749 InheritedShortEdit.Enabled:=false;
750 InheritedShortLabel.Caption:=lisCodeHelpnoinheriteddescriptionfound;
751 end else begin
752 Element:=fChain[i];
753 ShortDescr:=Element.FPDocFile.GetValueFromNode(Element.ElementNode,fpdiShort);
754 InheritedShortEdit.Text:=ShortDescr;
755 InheritedShortEdit.Enabled:=true;
756 InheritedShortLabel.Caption:=lisCodeHelpShortdescriptionOf+' '
757 +GetContextTitle(Element);
758 end;
759 MoveToInheritedButton.Enabled:=(fChain<>nil)
760 and (fChain.Count>1)
761 and (ShortEdit.Text<>'');
762 CopyFromInheritedButton.Enabled:=(i>=0);
763 AddLinkToInheritedButton.Enabled:=(i>=0);
764 end;
765
766 procedure TFPDocEditor.UpdateChain;
767 var
768 Code: TCodeBuffer;
769 LDResult: TCodeHelpParseResult;
770 NewChain: TCodeHelpElementChain;
771 CacheWasUsed: Boolean;
772 begin
773 fDocFile:=nil;
774 FreeAndNil(fChain);
775 if fUpdateLock>0 then begin
776 Include(FFlags,fpdefChainNeedsUpdate);
777 exit;
778 end;
779 Exclude(FFlags,fpdefChainNeedsUpdate);
780
781 if (fSourceFilename='') or (CaretXY.X<1) or (CaretXY.Y<1) then exit;
782
783 {$IFDEF VerboseCodeHelp}
784 DebugLn(['TFPDocEditForm.UpdateChain START ',fSourceFilename,' ',dbgs(CaretXY)]);
785 {$ENDIF}
786 NewChain:=nil;
787 try
788 // fetch pascal source
789 Code:=CodeToolBoss.LoadFile(fSourceFilename,true,false);
790 if Code=nil then begin
791 DebugLn(['TFPDocEditForm.UpdateChain failed loading ',fSourceFilename]);
792 exit;
793 end;
794
795 // start getting the fpdoc element chain
796 LDResult:=CodeHelpBoss.GetElementChain(Code,CaretXY.X,CaretXY.Y,true,
797 NewChain,CacheWasUsed);
798 case LDResult of
799 chprParsing:
800 begin
801 Include(FFlags,fpdefChainNeedsUpdate);
802 DebugLn(['TFPDocEditForm.UpdateChain ToDo: still parsing CodeHelpBoss.GetElementChain for ',fSourceFilename,' ',dbgs(CaretXY)]);
803 exit;
804 end;
805 chprFailed:
806 begin
807 {$IFDEF VerboseFPDocFails}
808 DebugLn(['TFPDocEditForm.UpdateChain failed CodeHelpBoss.GetElementChain for ',fSourceFilename,' ',dbgs(CaretXY)]);
809 {$ENDIF}
810 exit;
811 end;
812 else
813 {$IFDEF VerboseCodeHelp}
814 NewChain.WriteDebugReport;
815 {$ENDIF}
816 fChain:=NewChain;
817 fDocFile:=fChain.DocFile;
818 NewChain:=nil;
819 end;
820 finally
821 NewChain.Free;
822 end;
823 if (fDocFile=nil) then begin
824 // load default docfile, needed to show syntax errors in xml and for topics
825 fDocFile:=GetDefaultDocFile;
826 end;
827 OpenXMLButton.Enabled:=fDocFile<>nil;
828 if fDocFile<>nil then
829 OpenXMLButton.Hint:=fDocFile.Filename
830 else
831 OpenXMLButton.Hint:='';
832 end;
833
834 procedure TFPDocEditor.OnFPDocChanging(Sender: TObject;
835 FPDocFPFile: TLazFPDocFile);
836 begin
837 if fpdefWriting in FFlags then exit;
838 if (fChain<>nil) and (fChain.IndexOfFile(FPDocFPFile)>=0) then
839 InvalidateChain
840 else if (fDocFile<>nil) and (fDocFile=FPDocFPFile) then
841 Include(FFlags,fpdefTopicNeedsUpdate);
842 end;
843
844 procedure TFPDocEditor.OnFPDocChanged(Sender: TObject;
845 FPDocFPFile: TLazFPDocFile);
846 begin
847 if fpdefWriting in FFlags then exit;
848 if FPDocFPFile=nil then exit;
849 // maybe eventually update the editor
850 end;
851
852 procedure TFPDocEditor.LoadGUIValues(Element: TCodeHelpElement);
853 var
854 EnabledState: Boolean;
855 OldModified: Boolean;
856 begin
857 if fpdefReading in FFlags then exit;
858 OldModified:=FModified;
859
860 Include(FFlags,fpdefReading);
861 try
862 EnabledState := (Element<>nil) and (Element.ElementNode<>nil);
863
864 //CreateButton.Enabled := (Element<>nil) and (Element.ElementNode=nil)
865 // and (Element.ElementName<>'');
866
867 if EnabledState then
868 begin
869 FOldValues:=Element.FPDocFile.GetValuesFromNode(Element.ElementNode);
870 FOldVisualValues[fpdiShort]:=ReplaceLineEndings(FOldValues[fpdiShort],'');
871 FOldVisualValues[fpdiElementLink]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiElementLink]);
872 FOldVisualValues[fpdiDescription]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiDescription]);
873 FOldVisualValues[fpdiErrors]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiErrors]);
874 FOldVisualValues[fpdiSeeAlso]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiSeeAlso]);
875 FOldVisualValues[fpdiExample]:=LineBreaksToSystemLineBreaks(FOldValues[fpdiExample]);
876 //DebugLn(['TFPDocEditor.LoadGUIValues Short="',dbgstr(FOldValues[fpdiShort]),'"']);
877 end
878 else
879 begin
880 FOldVisualValues[fpdiShort]:='';
881 FOldVisualValues[fpdiElementLink]:='';
882 FOldVisualValues[fpdiDescription]:='';
883 FOldVisualValues[fpdiErrors]:='';
884 FOldVisualValues[fpdiSeeAlso]:='';
885 FOldVisualValues[fpdiExample]:='';
886 end;
887 ShortEdit.Text := FOldVisualValues[fpdiShort];
888 DescrShortEdit.Text := ShortEdit.Text;
889 //debugln(['TFPDocEditor.LoadGUIValues "',ShortEdit.Text,'" "',FOldVisualValues[fpdiShort],'"']);
890 LinkEdit.Text := FOldVisualValues[fpdiElementLink];
891 DescrSynEdit.Lines.Text := FOldVisualValues[fpdiDescription];
892 //debugln(['TFPDocEditor.LoadGUIValues DescrMemo="',dbgstr(DescrSynEdit.Lines.Text),'" Descr="',dbgstr(FOldVisualValues[fpdiDescription]),'"']);
893 SeeAlsoSynEdit.Text := FOldVisualValues[fpdiSeeAlso];
894 ErrorsSynEdit.Lines.Text := FOldVisualValues[fpdiErrors];
895 ExampleEdit.Text := FOldVisualValues[fpdiExample];
896
897 ShortEdit.Enabled := EnabledState;
898 DescrShortEdit.Enabled := ShortEdit.Enabled;
899 LinkEdit.Enabled := EnabledState;
900 DescrSynEdit.Enabled := EnabledState;
901 SeeAlsoSynEdit.Enabled := EnabledState;
902 ErrorsSynEdit.Enabled := EnabledState;
903 ExampleEdit.Enabled := EnabledState;
904 BrowseExampleButton.Enabled := EnabledState;
905
906 FModified:=OldModified;
907 SaveButton.Enabled:=false;
908
909 finally
910 Exclude(FFlags,fpdefReading);
911 end;
912 end;
913
914 procedure TFPDocEditor.MoveToInherited(Element: TCodeHelpElement);
915 var
916 Values: TFPDocElementValues;
917 begin
918 Values:=GetGUIValues;
919 WriteNode(Element,Values,true);
920 end;
921
TFPDocEditor.ExtractIDFromLinkTagnull922 function TFPDocEditor.ExtractIDFromLinkTag(const LinkTag: string; out ID, Title: string
923 ): boolean;
924 // extract id and title from example:
925 // <link id="TCustomControl"/>
926 // <link id="#lcl.Graphics.TCanvas">TCanvas</link>
927 var
928 StartPos: Integer;
929 EndPos: LongInt;
930 begin
931 Result:=false;
932 ID:='';
933 Title:='';
934 StartPos:=length('<link id="')+1;
935 if copy(LinkTag,1,StartPos-1)<>'<link id="' then
936 exit;
937 EndPos:=StartPos;
938 while (EndPos<=length(LinkTag)) do begin
939 if LinkTag[EndPos]='"' then begin
940 ID:=copy(LinkTag,StartPos,EndPos-StartPos);
941 Title:='';
942 Result:=true;
943 // extract title
944 StartPos:=EndPos;
945 while (StartPos<=length(LinkTag)) and (LinkTag[StartPos]<>'>') do inc(StartPos);
946 if LinkTag[StartPos-1]='\' then begin
947 // no title
948 end else begin
949 // has title
950 inc(StartPos);
951 EndPos:=StartPos;
952 while (EndPos<=length(LinkTag)) and (LinkTag[EndPos]<>'<') do inc(EndPos);
953 Title:=copy(LinkTag,StartPos,EndPos-StartPos);
954 end;
955 exit;
956 end;
957 inc(EndPos);
958 end;
959 end;
960
CreateElementnull961 function TFPDocEditor.CreateElement(Element: TCodeHelpElement): Boolean;
962 var
963 NewElement: TCodeHelpElement;
964 begin
965 //DebugLn(['TFPDocEditForm.CreateElement ']);
966 if (Element=nil) or (Element.ElementName='') then exit(false);
967 NewElement:=nil;
968 Include(FFlags,fpdefWriting);
969 try
970 Result:=CodeHelpBoss.CreateElement(Element.CodeXYPos.Code,
971 Element.CodeXYPos.X,Element.CodeXYPos.Y,NewElement);
972 finally
973 Exclude(FFlags,fpdefWriting);
974 NewElement.Free;
975 end;
976 Reset;
977 InvalidateChain;
978 end;
979
980 procedure TFPDocEditor.UpdateButtons;
981 var
982 HasEdit: Boolean;
983 begin
984 HasEdit:=(PageControl.ActivePage = ShortTabSheet)
985 or (PageControl.ActivePage = DescrTabSheet)
986 or (PageControl.ActivePage = SeeAlsoTabSheet)
987 or (PageControl.ActivePage = ErrorsTabSheet)
988 or (PageControl.ActivePage = TopicSheet);
989 BoldFormatButton.Enabled:=HasEdit;
990 ItalicFormatButton.Enabled:=HasEdit;
991 UnderlineFormatButton.Enabled:=HasEdit;
992 InsertCodeTagButton.Enabled:=HasEdit;
993 InsertLinkSpeedButton.Enabled:=HasEdit;
994 InsertParagraphSpeedButton.Enabled:=HasEdit;
995 InsertRemarkButton.Enabled:=HasEdit;
996 InsertVarTagButton.Enabled:=HasEdit;
997 end;
998
GetCurrentUnitNamenull999 function TFPDocEditor.GetCurrentUnitName: string;
1000 begin
1001 if (fChain<>nil) and (fChain.Count>0) then
1002 Result:=fChain[0].ElementUnitName
1003 else
1004 Result:='';
1005 end;
1006
GetCurrentOwnerNamenull1007 function TFPDocEditor.GetCurrentOwnerName: string;
1008 begin
1009 if (fChain<>nil) and (fChain.Count>0) then
1010 Result:=fChain[0].ElementOwnerName
1011 else
1012 Result:='';
1013 end;
1014
1015 procedure TFPDocEditor.JumpToError(Item: TFPDocItem; LineCol: TPoint);
1016 begin
1017 case Item of
1018 fpdiShort: PageControl.ActivePage:=ShortTabSheet;
1019 fpdiElementLink: PageControl.ActivePage:=InheritedTabSheet;
1020 fpdiDescription:
1021 begin
1022 PageControl.ActivePage:=DescrTabSheet;
1023 DescrSynEdit.CaretXY:=LineCol;
1024 end;
1025 fpdiErrors: PageControl.ActivePage:=ErrorsTabSheet;
1026 fpdiSeeAlso: PageControl.ActivePage:=SeeAlsoTabSheet;
1027 fpdiExample: PageControl.ActivePage:=ExampleTabSheet;
1028 end;
1029 end;
1030
1031 procedure TFPDocEditor.OpenXML;
1032 var
1033 CurDocFile: TLazFPDocFile;
1034 begin
1035 CurDocFile:=DocFile;
1036 if CurDocFile=nil then exit;
1037 if FileExistsUTF8(CurDocFile.Filename) then begin
1038 LazarusIDE.DoOpenEditorFile(CurDocFile.Filename,-1,-1,
1039 [ofOnlyIfExists,ofRegularFile,ofUseCache]);
1040 end;
1041 end;
1042
TFPDocEditor.GUIModifiednull1043 function TFPDocEditor.GUIModified: boolean;
1044 begin
1045 if fpdefReading in FFlags then exit(false);
1046 Result:=(ShortEdit.Text<>FOldVisualValues[fpdiShort])
1047 or (LinkEdit.Text<>FOldVisualValues[fpdiElementLink])
1048 or (DescrSynEdit.Text<>FOldVisualValues[fpdiDescription])
1049 or (SeeAlsoSynEdit.Text<>FOldVisualValues[fpdiSeeAlso])
1050 or (ErrorsSynEdit.Text<>FOldVisualValues[fpdiErrors])
1051 or (ExampleEdit.Text<>FOldVisualValues[fpdiExample]);
1052 if Result then begin
1053 if (ShortEdit.Text<>FOldVisualValues[fpdiShort]) then
1054 debugln(['TFPDocEditor.GUIModified Short ',dbgstr(ShortEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiShort])]);
1055 if (LinkEdit.Text<>FOldVisualValues[fpdiElementLink]) then
1056 debugln(['TFPDocEditor.GUIModified link ',dbgstr(LinkEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiElementLink])]);
1057 if (DescrSynEdit.Text<>FOldVisualValues[fpdiDescription]) then
1058 debugln(['TFPDocEditor.GUIModified Descr ',dbgstr(DescrSynEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiDescription])]);
1059 if (SeeAlsoSynEdit.Text<>FOldVisualValues[fpdiSeeAlso]) then
1060 debugln(['TFPDocEditor.GUIModified SeeAlso ',dbgstr(SeeAlsoSynEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiSeeAlso])]);
1061 if (ErrorsSynEdit.Text<>FOldVisualValues[fpdiErrors]) then
1062 debugln(['TFPDocEditor.GUIModified Errors ',dbgstr(ErrorsSynEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiErrors])]);
1063 if (ExampleEdit.Text<>FOldVisualValues[fpdiExample]) then
1064 debugln(['TFPDocEditor.GUIModified Example ',dbgstr(ExampleEdit.Text),' <> ',dbgstr(FOldVisualValues[fpdiExample])]);
1065 end;
1066 end;
1067
1068 procedure TFPDocEditor.DoEditorUpdate(Sender: TObject);
1069 begin
1070 if GetCaptureControl <> nil then // If SynEdit has Capture the user may be selecting by Mouse. https://bugs.freepascal.org/view.php?id=37150
1071 exit;
1072 if FollowCursor then
1073 LoadIdentifierAtCursor;
1074 end;
1075
1076 procedure TFPDocEditor.DoEditorMouseUp(Sender: TObject);
1077 begin
1078 if FollowCursor then
1079 LoadIdentifierAtCursor;
1080 end;
1081
1082 procedure TFPDocEditor.UpdateTopicCombo;
1083 var
1084 cnt, i: LongInt;
1085 DFile: TLazFPDocFile;
1086 Topics: TStringList;
1087 begin
1088 Exclude(FFlags,fpdefTopicNeedsUpdate);
1089 Topics:=TStringList.Create;
1090 Include(FFlags,fpdefTopicSettingUp);
1091 try
1092 Dfile := DocFile;
1093 if DFile<>nil then begin
1094 cnt := DFile.GetModuleTopicCount;
1095 for i := 0 to cnt - 1 do
1096 Topics.Add(DFile.GetModuleTopicName(i));
1097 end;
1098 TopicListBox.Items.Assign(Topics);
1099 TopicListBox.ItemIndex:=TopicListBox.Items.IndexOf(FCurrentTopic);
1100 UpdateTopic;
1101 finally
1102 Exclude(FFlags,fpdefTopicSettingUp);
1103 Topics.Free;
1104 end;
1105 end;
1106
1107 procedure TFPDocEditor.SetIdleConnected(AValue: boolean);
1108 begin
1109 if FIdleConnected=AValue then Exit;
1110 FIdleConnected:=AValue;
1111 if IdleConnected then
1112 Application.AddOnIdleHandler(@ApplicationIdle)
1113 else
1114 Application.RemoveOnIdleHandler(@ApplicationIdle);
1115 end;
1116
1117 procedure TFPDocEditor.SetFollowCursor(AValue: boolean);
1118 begin
1119 if FFollowCursor=AValue then Exit;
1120 FFollowCursor:=AValue;
1121 if FollowCursor then
1122 LoadIdentifierAtCursor;
1123 end;
1124
GetDefaultDocFilenull1125 function TFPDocEditor.GetDefaultDocFile(CreateIfNotExists: Boolean): TLazFPDocFile;
1126 var
1127 CacheWasUsed : Boolean;
1128 AnOwner: TObject;
1129 FPDocFileName: String;
1130 begin
1131 Result := nil;
1132 if (not CreateIfNotExists) and (fDocFile<>nil) then
1133 exit(fDocFile);
1134
1135 FPDocFileName := CodeHelpBoss.GetFPDocFilenameForSource(SourceFilename, true,
1136 CacheWasUsed, AnOwner, CreateIfNotExists);
1137 if (FPDocFileName = '')
1138 or (CodeHelpBoss.LoadFPDocFile(FPDocFileName, [chofUpdateFromDisk], Result,
1139 CacheWasUsed) <> chprSuccess)
1140 then
1141 Result := nil;
1142 end;
1143
1144 procedure TFPDocEditor.Reset;
1145 var
1146 i: TFPDocItem;
1147 begin
1148 FreeAndNil(fChain);
1149 if fpdefReading in FFlags then exit;
1150 Include(FFlags,fpdefReading);
1151 try
1152 // clear all element editors/viewers
1153 ShortEdit.Clear;
1154 DescrShortEdit.Clear;
1155 LinkEdit.Clear;
1156 DescrSynEdit.Clear;
1157 SeeAlsoSynEdit.Clear;
1158 ErrorsSynEdit.Clear;
1159 ExampleEdit.Clear;
1160 ClearTopicControls;
1161 for i:=Low(TFPDocItem) to high(TFPDocItem) do
1162 FOldVisualValues[i]:='';
1163
1164 Modified := False;
1165 //CreateButton.Enabled:=false;
1166 OpenXMLButton.Enabled:=false;
1167 finally
1168 Exclude(FFlags,fpdefReading);
1169 end;
1170 end;
1171
1172 procedure TFPDocEditor.InvalidateChain;
1173 begin
1174 FreeAndNil(fChain);
1175 FFlags:=FFlags+[fpdefCodeCacheNeedsUpdate,
1176 fpdefChainNeedsUpdate,fpdefCaptionNeedsUpdate,
1177 fpdefValueControlsNeedsUpdate,fpdefInheritedControlsNeedsUpdate];
1178 IdleConnected:=true;
1179 end;
1180
1181 procedure TFPDocEditor.LoadIdentifierAt(const SrcFilename: string;
1182 const Caret: TPoint);
1183 var
1184 NewSrcFilename: String;
1185 begin
1186 //debugln(['TFPDocEditor.LoadIdentifierAt START ',SrcFilename,' ',dbgs(Caret)]);
1187 // save the current changes to documentation
1188 Save(IsVisible);
1189
1190 NewSrcFilename:=TrimAndExpandFilename(SrcFilename);
1191 if (NewSrcFilename=SourceFilename) and (CompareCaret(Caret,CaretXY)=0)
1192 and (fChain<>nil) and fChain.IsValid
1193 and (not LazarusIDE.NeedSaveSourceEditorChangesToCodeCache(nil)) then
1194 exit;
1195
1196 FCaretXY:=Caret;
1197 fSourceFilename:=NewSrcFilename;
1198
1199 Reset;
1200 Include(FFlags,fpdefTopicNeedsUpdate);
1201 InvalidateChain;
1202 end;
1203
1204 procedure TFPDocEditor.LoadIdentifierAtCursor;
1205 var
1206 SrcEdit: TSourceEditorInterface;
1207 begin
1208 if SourceEditorManagerIntf=nil then exit;
1209 if csDestroying in ComponentState then exit;
1210 if FFlags*[fpdefReading,fpdefWriting]<>[] then exit;
1211 SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
1212 if SrcEdit=nil then
1213 Reset
1214 else
1215 LoadIdentifierAt(SrcEdit.FileName,SrcEdit.CursorTextXY);
1216 end;
1217
1218 procedure TFPDocEditor.BeginUpdate;
1219 begin
1220 inc(fUpdateLock);
1221 end;
1222
1223 procedure TFPDocEditor.EndUpdate;
1224 begin
1225 dec(fUpdateLock);
1226 if fUpdateLock<0 then RaiseGDBException('');
1227 if fUpdateLock=0 then begin
1228 if fpdefCaptionNeedsUpdate in FFlags then UpdateCaption;
1229 end;
1230 end;
1231
1232 procedure TFPDocEditor.ClearEntry(DoSave: Boolean);
1233 begin
1234 Modified:=true;
1235 ShortEdit.Text:='';
1236 DescrShortEdit.Text:=ShortEdit.Text;
1237 DescrSynEdit.Text:='';
1238 SeeAlsoSynEdit.Text:='';
1239 ErrorsSynEdit.Text:='';
1240 ExampleEdit.Text:='';
1241 if DoSave then Save;
1242 end;
1243
1244 procedure TFPDocEditor.Save(CheckGUI: boolean);
1245 var
1246 Values: TFPDocElementValues;
1247 TopicDocFile: TLazFPDocFile;
1248 Node: TDOMNode;
1249 Child: TDOMNode;
1250 TopicChanged: Boolean;
1251 begin
1252 //DebugLn(['TFPDocEditor.Save FModified=',FModified]);
1253 if fpdefReading in FFlags then exit;
1254
1255 if (not FModified)
1256 and ((not CheckGUI) or (not GUIModified)) then
1257 begin
1258 SaveButton.Enabled:=false;
1259 Exit; // nothing changed => exit
1260 end;
1261 //DebugLn(['TFPDocEditor.Save FModified=',FModified,' CheckGUI=',CheckGUI,' GUIModified=',GUIModified]);
1262 FModified:=false;
1263 SaveButton.Enabled:=false;
1264
1265 TopicChanged:=false;
1266 TopicDocFile:=DocFile;
1267 if FCurrentTopic <> '' then
1268 begin
1269 if fDocFile=nil then
1270 fDocFile := GetDefaultDocFile(True);
1271 TopicDocFile:=DocFile;
1272 if TopicDocFile <> nil then begin
1273 Node := TopicDocFile.GetModuleTopic(FCurrentTopic);
1274 if Node <> nil then begin
1275 Child := Node.FindNode('short');
1276 if (Child = nil)
1277 or (TopicDocFile.GetChildValuesAsString(Child)<>TopicShort.Text)
1278 then begin
1279 TopicDocFile.SetChildValue(Node, 'short', TopicShort.Text);
1280 TopicChanged:=true;
1281 end;
1282 Child := Node.FindNode('descr');
1283 if (Child = nil)
1284 or (TopicDocFile.GetChildValuesAsString(Child)<>TopicDescrSynEdit.Text)
1285 then begin
1286 TopicDocFile.SetChildValue(Node, 'descr', TopicDescrSynEdit.Text);
1287 TopicChanged:=true;
1288 end;
1289 end;
1290 end;
1291 end;
1292 if (fChain=nil) or (fChain.Count=0) then
1293 begin
1294 if IsVisible then
1295 DebugLn(['TFPDocEditor.Save failed: no chain']);
1296 end else if not fChain.IsValid then
1297 begin
1298 if IsVisible then
1299 DebugLn(['TFPDocEditor.Save failed: chain not valid']);
1300 end else if (fChain[0].FPDocFile <> nil) then
1301 begin
1302 Values:=GetGUIValues;
1303 if WriteNode(fChain[0],Values,true) then
1304 begin
1305 // write succeeded
1306 if fChain.DocFile=TopicDocFile then
1307 TopicChanged:=false;
1308 end else begin
1309 DebugLn(['TFPDocEditor.Save WriteNode FAILED']);
1310 end;
1311 end;
1312 if TopicChanged then begin
1313 Include(FFlags,fpdefWriting);
1314 try
1315 CodeHelpBoss.SaveFPDocFile(TopicDocFile);
1316 finally
1317 Exclude(FFlags,fpdefWriting);
1318 end;
1319 end;
1320 end;
1321
GetGUIValuesnull1322 function TFPDocEditor.GetGUIValues: TFPDocElementValues;
1323 var
1324 i: TFPDocItem;
1325 begin
1326 Result[fpdiShort]:=ShortEdit.Text;
1327 Result[fpdiDescription]:=DescrSynEdit.Text;
1328 Result[fpdiErrors]:=ErrorsSynEdit.Text;
1329 Result[fpdiSeeAlso]:=SeeAlsoSynEdit.Text;
1330 Result[fpdiExample]:=ExampleEdit.Text;
1331 Result[fpdiElementLink]:=LinkEdit.Text;
1332 for i:=Low(TFPDocItem) to High(TFPDocItem) do
1333 if Trim(Result[i])='' then
1334 Result[i]:='';
1335 end;
1336
1337 procedure TFPDocEditor.SetModified(const AValue: boolean);
1338 begin
1339 if FModified=AValue then exit;
1340 FModified:=AValue;
1341 SaveButton.Enabled:=FModified;
1342 //debugln(['TFPDocEditor.SetModified New=',FModified]);
1343 end;
1344
1345 procedure TFPDocEditor.UpdateTopic;
1346 var
1347 Child: TDOMNode;
1348 Node: TDOMNode;
1349 DFile: TLazFPDocFile;
1350 begin
1351 FCurrentTopic := '';
1352 try
1353 if TopicListBox.ItemIndex < 0 then exit;
1354 Dfile := GetDefaultDocFile(True);
1355 if DFile = nil then exit;
1356
1357 FCurrentTopic := TopicListBox.Items[TopicListBox.ItemIndex];
1358 Node := DFile.GetModuleTopic(FCurrentTopic);
1359 if Node = nil then exit;
1360
1361 Include(FFlags, fpdefTopicSettingUp);
1362 try
1363 Child := Node.FindNode('short');
1364 if Child <> nil then
1365 TopicShort.Text := DFile.GetChildValuesAsString(Child);
1366 Child := Node.FindNode('descr');
1367 if Child <> nil then
1368 TopicDescrSynEdit.Text := DFile.GetChildValuesAsString(Child);
1369 TopicShort.Enabled := True;
1370 TopicDescrSynEdit.Enabled := True;
1371 if TopicShort.IsVisible then
1372 TopicShort.SetFocus;
1373 finally
1374 Exclude(FFlags, fpdefTopicSettingUp);
1375 end;
1376 finally
1377 if FCurrentTopic='' then
1378 ClearTopicControls;
1379 end;
1380 end;
1381
1382 procedure TFPDocEditor.UpdateShowing;
1383 begin
1384 inherited UpdateShowing;
1385 if IsVisible and (fpdefWasHidden in FFlags) then begin
1386 Exclude(FFlags,fpdefWasHidden);
1387 LoadIdentifierAtCursor;
1388 end;
1389 end;
1390
1391 procedure TFPDocEditor.Loaded;
1392 begin
1393 inherited Loaded;
1394 DescrSynEdit.ControlStyle:=DescrSynEdit.ControlStyle+[];
1395 end;
1396
WriteNodenull1397 function TFPDocEditor.WriteNode(Element: TCodeHelpElement;
1398 Values: TFPDocElementValues; Interactive: Boolean): Boolean;
1399 var
1400 TopNode: TDOMNode;
1401 CurDocFile: TLazFPDocFile;
1402 CurDoc: TXMLDocument;
1403
Checknull1404 function Check(Test: boolean; const Msg: string): Boolean;
1405 var
1406 CurName: String;
1407 begin
1408 Result:=Test;
1409 if not Test then exit;
1410 DebugLn(['TFPDocEditor.WriteNode ERROR ',Msg]);
1411 if Interactive then begin;
1412 if Element.FPDocFile<>nil then
1413 CurName:=Element.FPDocFile.Filename
1414 else
1415 CurName:=Element.ElementName;
1416 IDEMessageDialog(lisCodeToolsDefsWriteError,
1417 Format(lisFPDocErrorWriting, [CurName, LineEnding, Msg]), mtError, [mbCancel]);
1418 end;
1419 end;
1420
SetValuenull1421 function SetValue(Item: TFPDocItem): boolean;
1422 var
1423 NewValue: String;
1424 begin
1425 Result:=false;
1426 NewValue:=Values[Item];
1427 try
1428 FixFPDocFragment(NewValue,
1429 Item in [fpdiShort,fpdiDescription,fpdiErrors,fpdiSeeAlso],
1430 true);
1431 CurDocFile.SetChildValue(TopNode,FPDocItemNames[Item],NewValue);
1432 Result:=true;
1433 except
1434 on E: EXMLReadError do begin
1435 DebugLn(['SetValue ',dbgs(E.LineCol),' Name=',FPDocItemNames[Item]]);
1436 JumpToError(Item,E.LineCol);
1437 IDEMessageDialog(lisFPDocFPDocSyntaxError,
1438 Format(lisFPDocThereIsASyntaxErrorInTheFpdocElement, [FPDocItemNames
1439 [Item], LineEnding+LineEnding, E.Message]), mtError, [mbOk], '');
1440 end;
1441 end;
1442 end;
1443
1444 begin
1445 Result:=false;
1446 if fpdefWriting in FFlags then begin
1447 DebugLn(['TFPDocEditForm.WriteNode inconsistency detected: recursive write']);
1448 exit;
1449 end;
1450
1451 if Check(Element=nil,'Element=nil') then exit;
1452 CurDocFile:=Element.FPDocFile;
1453 if Check(CurDocFile=nil,'Element.FPDocFile=nil') then begin
1454 // no fpdoc file found
1455 DebugLn(['TFPDocEditForm.WriteNode TODO: implement creating new fpdoc file']);
1456 exit;
1457 end;
1458 CurDoc:=CurDocFile.Doc;
1459 if Check(CurDoc=nil,'Element.FPDocFile.Doc=nil') then exit;
1460 if Check(not Element.ElementNodeValid,'not Element.ElementNodeValid') then exit;
1461 TopNode:=Element.ElementNode;
1462 if Check(TopNode=nil,'TopNode=nil') then begin
1463 // no old node found
1464 Check(false,'no old node found. TODO: implement creating a new.');
1465 Exit;
1466 end;
1467
1468 Include(FFlags,fpdefWriting);
1469 CurDocFile.BeginUpdate;
1470 try
1471 if SetValue(fpdiShort)
1472 and SetValue(fpdiElementLink)
1473 and SetValue(fpdiDescription)
1474 and SetValue(fpdiErrors)
1475 and SetValue(fpdiSeeAlso)
1476 and SetValue(fpdiExample) then
1477 ;
1478 finally
1479 CurDocFile.EndUpdate;
1480 fChain.MakeValid;
1481 Exclude(FFlags,fpdefWriting);
1482 end;
1483
1484 if CodeHelpBoss.SaveFPDocFile(CurDocFile)<>mrOk then begin
1485 DebugLn(['TFPDocEditForm.WriteNode failed writing ',CurDocFile.Filename]);
1486 exit;
1487 end;
1488 Result:=true;
1489 end;
1490
1491 procedure TFPDocEditor.UpdateCodeCache;
1492 begin
1493 if fUpdateLock>0 then begin
1494 Include(FFlags,fpdefCodeCacheNeedsUpdate);
1495 exit;
1496 end;
1497 Exclude(FFlags,fpdefCodeCacheNeedsUpdate);
1498 LazarusIDE.SaveSourceEditorChangesToCodeCache(nil);
1499 end;
1500
1501 procedure TFPDocEditor.ErrorsSynEditChange(Sender: TObject);
1502 begin
1503 if fpdefReading in FFlags then exit;
1504 if ErrorsSynEdit.Text<>FOldVisualValues[fpdiErrors] then
1505 Modified:=true;
1506 end;
1507
1508 procedure TFPDocEditor.ExampleEditChange(Sender: TObject);
1509 begin
1510 if fpdefReading in FFlags then exit;
1511 if ExampleEdit.Text<>FOldVisualValues[fpdiExample] then
1512 Modified:=true;
1513 end;
1514
FindInheritedIndexnull1515 function TFPDocEditor.FindInheritedIndex: integer;
1516 // returns Index in chain of an overriden Element with a short description
1517 // returns -1 if not found
1518 var
1519 Element: TCodeHelpElement;
1520 begin
1521 if (fChain<>nil) then begin
1522 Result:=1;
1523 while (Result<fChain.Count) do begin
1524 Element:=fChain[Result];
1525 if (Element.ElementNode<>nil)
1526 and (Element.FPDocFile.GetValueFromNode(Element.ElementNode,fpdiShort)<>'')
1527 then
1528 exit;
1529 inc(Result);
1530 end;
1531 end;
1532 Result:=-1;
1533 end;
1534
1535 procedure TFPDocEditor.AddLinkToInheritedButtonClick(Sender: TObject);
1536 var
1537 i: LongInt;
1538 Element: TCodeHelpElement;
1539 Link: String;
1540 begin
1541 i:=FindInheritedIndex;
1542 if i<0 then exit;
1543 //DebugLn(['TFPDocEditor.AddLinkToInheritedButtonClick ']);
1544 Element:=fChain[i];
1545 Link:=Element.ElementName;
1546 if Element.ElementUnitName<>'' then begin
1547 Link:=Element.ElementUnitName+'.'+Link;
1548 if Element.ElementFPDocPackageName<>'' then
1549 Link:='#'+Element.ElementFPDocPackageName+'.'+Link;
1550 end;
1551 if Link<>LinkEdit.Text then begin
1552 LinkEdit.Text:=Link;
1553 Modified:=true;
1554 end;
1555 end;
1556
1557 procedure TFPDocEditor.BrowseExampleButtonClick(Sender: TObject);
1558 begin
1559 if Doc=nil then exit;
1560 InitIDEFileDialog(OpenDialog);
1561 OpenDialog.Title:=lisChooseAnExampleFile;
1562 OpenDialog.Filter:=dlgFilterPascalFile+'|*.pas;*.pp;*.p|'+dlgFilterAll+'|'+FileMask;
1563 OpenDialog.InitialDir:=ExtractFilePath(DocFile.Filename);
1564 if OpenDialog.Execute then begin
1565 ExampleEdit.Text := ExtractRelativepath(
1566 ExtractFilePath(DocFile.Filename), GetForcedPathDelims(OpenDialog.FileName));
1567 if ExampleEdit.Text<>FOldVisualValues[fpdiExample] then
1568 Modified:=true;
1569 end;
1570 StoreIDEFileDialog(OpenDialog);
1571 end;
1572
1573 procedure TFPDocEditor.CopyFromInheritedButtonClick(Sender: TObject);
1574 var
1575 i: LongInt;
1576 begin
1577 i:=FindInheritedIndex;
1578 if i<0 then exit;
1579 //DebugLn(['TFPDocEditForm.CopyFromInheritedButtonClick ']);
1580 if ShortEdit.Text<>'' then begin
1581 if IDEQuestionDialog('Confirm replace',
1582 GetContextTitle(fChain[0])+' already contains the help:'+LineEnding+ShortEdit.Text,
1583 mtConfirmation, [mrYes,'Replace',
1584 mrCancel]) <> mrYes then exit;
1585 end;
1586 LoadGUIValues(fChain[i]);
1587 Modified:=true;
1588 end;
1589
1590 procedure TFPDocEditor.CopyShortToDescrMenuItemClick(Sender: TObject);
1591 begin
1592 DescrSynEdit.Append(ShortEdit.Text);
1593 Modified:=true;
1594 end;
1595
1596 procedure TFPDocEditor.CreateButtonClick(Sender: TObject);
1597 begin
1598 if ((fChain=nil) or (fChain.Count=0))
1599 or (TCodeHelpElement(fChain[0]).ElementName='') then begin
1600 IDEMessageDialog('Invalid Declaration','Please place the editor caret on an identifier. If this is a new unit, please save the file first.',
1601 mtError,[mbOK]);
1602 exit;
1603 end;
1604 CreateElement(fChain[0]);
1605 end;
1606
1607 procedure TFPDocEditor.DescrSynEditChange(Sender: TObject);
1608 begin
1609 if fpdefReading in FFlags then exit;
1610 if DescrSynEdit.Text<>FOldVisualValues[fpdiDescription] then
1611 Modified:=true;
1612 end;
1613
1614 end.
1615