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