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 Author: Michael Van Canneyt 22 23 Page editor. Edits 1 file. A page editor exists of a package editor 24 and an element editor, joined on a panel with a splitter between them. 25 26 It is built modular, in 3 pieces. 27 - Package editor. Edits structure of the documentation file. 28 - Element editor. Edits 1 element node in the documentation file. 29 - EditorPage. Combines the above two to one visual element. 30 31 The package editor is mainly concerned with structure of the file in 32 terms of packages/topic/module/element. All things which are concerned 33 with structure are shunted to the package editor. In turn, the package 34 editor has a series of event handlers in order to react on changes. 35 36 The element editor edits 1 element. It can handle the following tags: 37 short/descr/errors/seealso/example 38 39 There is no direct interaction between the package editor and the element 40 editor. This allows changing either of them without needing to change the other. 41 The page editor handles all communication between the two. 42 43 This allows one to implement several editors. One could also implement the 44 editor page so it is split horizontal, whatever. 45 46} 47unit pgEditor; 48 49{$mode objfpc} 50{$h+} 51 52interface 53 54uses SysUtils, Classes, dom, xmlread, xmlwrite, Forms, Controls, ExtCtrls, 55 ComCtrls, Dialogs, freditor, frpeditor, fpdeutil, LazFileUtils, LazUTF8; 56 57type 58 TEditorPageNew = class(TFrame) 59 60 end; 61 62Type 63 64 { TEditorPage } 65 TEditorPage = Class(TTabSheet) 66 Private 67 FDocument : TXMLDocument; 68 FPackages : TPackageEditor; 69 FElement : TElementEditor; 70 FSplitter : TSplitter; 71 FFileNAme : String; 72 Procedure ElementSelected(Node : TDomElement) ; 73 function FindElement(const ElementName: String; Out PE,ME : TDomElement): TDomElement; 74 function FindElementNode(AParent: TDomElement; const ANodeName: String; 75 const AElementName: string): TDomElement; 76 Procedure TopicSelected(Node : TDomElement) ; 77 Procedure ModuleSelected(Node : TDomElement) ; 78 Procedure PackageSelected(Node : TDomElement) ; 79// Procedure SelectionChanged(Sender : TObject; Node : TDomElement) ; 80// Procedure ElementDeSelected(Sender : TObject; Node : TDomElement) ; 81 Function GetCurrentSelection : String; 82 Function GetCurrentPackage : TDomElement; 83 Function GetCurrentModule : TDomElement; 84 Function GetCurrentTopic : TDomElement; 85 Function GetCurrentElement : TDomElement; 86 Procedure SetCurrentModule(Value : TDomElement); 87 Procedure SetCurrentTopic(Value : TDomElement); 88 Procedure SetCurrentPackage(Value : TDomElement); 89 Procedure SetCurrentElement(Value : TDomElement); 90 Procedure SetModified(Value : Boolean); 91 Function GetModified : Boolean; 92 Function MakeBackup(FN : String) : Boolean; 93 Procedure DisplayDocument(Const AStartNode : String = ''); 94 Procedure ElementChanged(Sender: TObject); 95 protected 96 procedure SetParent(NewParent: TWinControl); override; 97 Public 98 constructor Create(AOwner : TComponent); override; 99 Function FirstPackage : TDomElement; 100 Function FirstModule(APackage : TDomElement) : TDomElement; 101 Procedure LoadFromFile(FN : String; Const AStartNode : String = ''); 102 Procedure LoadFromStream(S : TStream); 103 Procedure SaveToFile(FN : String); 104 Procedure SetFileName(FN : String); 105 Procedure InsertTag(TagType : TTagType); 106 Procedure InsertLink(LinkTarget,LinkText : String); 107 Procedure InsertTable(Cols,Rows : Integer; UseHeader : Boolean); 108 Procedure InsertShortPrintLink(pLinkTarget: string); 109 Procedure InsertItemizeList(ItemsCount: Integer); 110 Procedure InsertEnumerateList(ItemsCount: Integer); 111 Procedure NewPackage(APackageName : String); 112 Procedure NewModule(AModuleName : String); 113 Procedure NewTopic(ATopicName : String); 114 procedure NewElement(AElementName: String); 115 Procedure GetElementList(List : TStrings); 116 function GetInitialDir: String; 117 Procedure ClearDocument; 118 procedure UpdateTree; 119 Function CanInsertTag(TagType : TTagType) : Boolean; 120 Property FileName : String Read FFileName; 121 Property CurrentSelection : String Read GetCurrentSelection; 122 Property CurrentPackage : TDomElement Read GetCurrentPackage Write SetCurrentPackage; 123 Property CurrentModule : TDomElement Read GetCurrentModule Write SetCurrentModule; 124 Property CurrentTopic : TDomElement Read GetCurrentTopic Write SetCurrentTopic; 125 Property CurrentElement : TDomElement Read GetCurrentElement Write SetCurrentElement; 126 Property Modified : Boolean Read GetModified Write SetModified; 127 end; 128 129 130implementation 131 132uses lazdeopts,lazdemsg; 133 134{$R *.lfm} 135 136{ --------------------------------------------------------------------- 137 TPageEditor 138 ---------------------------------------------------------------------} 139 140constructor TEditorPage.Create(AOwner : TComponent); 141begin 142 inherited; 143 FPackages:=TPackageEditor.Create(Self); 144 FPackages.Parent:=Self; 145 FPackages.Align:=alLeft; 146 FPackages.OnSelectElement:=@ElementSelected; 147 FPackages.OnSelectModule:=@ModuleSelected; 148 FPackages.OnSelectPackage:=@PackageSelected; 149 FPackages.OnSelectTopic:=@TopicSelected; 150 151 FSplitter:=TSplitter.Create(Self); 152 FSPlitter.Parent:=Self; 153 FSplitter.Align:=alLeft; 154 FSplitter.Left:=1; 155 FSplitter.Width:=5; 156 157 FElement:=TElementEditor.Create(Self); 158 FElement.Parent:=Self; 159 FElement.Align:=AlClient; 160 FElement.OnGetElementList:=@GetELementList; 161 FElement.OnGetInitialDir:=@GetInitialDir; 162 FElement.OnChange:=@ElementChanged; 163end; 164 165 166 167Procedure TEditorPage.ClearDocument; 168 169begin 170 if (FDocument<>nil) then 171 begin 172 FDocument.Free; 173 FDocument:=Nil; 174 end; 175end; 176 177procedure TEditorPage.UpdateTree; 178begin 179 FPackages.UpdateTree; 180end; 181 182function TEditorPage.CanInsertTag(TagType: TTagType): Boolean; 183begin 184 Result:=FElement.CanInsertTag(TagType); 185end; 186 187Procedure TEditorPage.LoadFromFile(FN : String; Const AStartNode : String = ''); 188 189Var 190 F : TFileStream; 191 192begin 193 ClearDocument; 194 F:=TFileStream.Create(UTF8ToSys(FN),fmOpenRead); 195 Try 196 SetFileName(FN); 197 ReadXMLFile(FDocument,F); 198 DisplayDocument(AStartNode); 199 FPackages.ExpandTree; 200 finally 201 F.Free; 202 end; 203end; 204 205Procedure TEditorPage.LoadFromStream(S : TStream); 206 207begin 208 ClearDocument; 209 ReadXMLFile(FDocument,S); 210 SetFileName(SNewDocument); 211 DisplayDocument; 212end; 213 214Procedure TEditorPage.SetFileName(FN : String); 215 216begin 217 FFileName:=FN; 218 Caption:=ChangeFileExt(ExtractFileName(FN),''); 219end; 220 221Function TEditorPage.MakeBackup(FN : String) : Boolean; 222 223Var 224 BN : String; 225 226begin 227 Result:=Not CreateBackup; 228 If not Result then 229 begin 230 BN:=ChangeFileExt(FN,BackupExtension); 231 Result:=RenameFileUTF8(FN,BN); 232 end; 233end; 234 235Procedure TEditorPage.SaveToFile(FN : String); 236begin 237 MakeBackup(FN); 238 if FN <> FFileName then SetFileName(FN); 239 If FElement.Modified then FElement.Save; 240 WriteXMLFile(FDocument, FN); 241 Modified :=False; 242 FElement.Refresh; 243end; 244 245function TEditorPage.FindElementNode(AParent : TDomElement; Const ANodeName : String; Const AElementName : string) : TDomElement; 246 247Var 248 N : TDomNode; 249 250begin 251 Result:=Nil; 252 N:=AParent.FirstChild; 253 While (Result=Nil) and (N<>Nil) do 254 begin 255 if (N.NodeType=ELEMENT_NODE) and (N.NodeName=ANodeName) then 256 If (AElementName='') or (CompareText((N as TDomElement).AttribStrings['name'],AElementName)=0) then 257 Result:=N as TDomElement; 258 N:=N.NextSibling; 259 end; 260end; 261 262 263function TEditorPage.FindElement(Const ElementName : String; Out PE,ME : TDomElement) : TDomElement; 264 265 Function GetNextPart(Var N : String) : String; 266 267 Var 268 p : integer; 269 270 begin 271 P:=Pos('.',N); 272 if P=0 then 273 P:=Length(N)+1; 274 Result:=Copy(N,1,P-1); 275 Delete(N,1,P); 276 end; 277 278Var 279 PN,N : String; 280 281begin 282 Result:=Nil; 283 PE:=Nil; 284 ME:=Nil; 285 N:=ElementName; 286 // Extract package name. 287 PN:=GetNextPart(N); 288 // Search package node 289 PE:=FindElementNode(FDocument.DocumentElement,'package',PN); 290 // if not found, assume first part is modulename in first package. 291 if (PE=Nil) then 292 begin 293 PE:=FindElementNode(FDocument.DocumentElement,'package',''); 294 N:=ElementName; 295 end; 296 if (PE=Nil) then // No package node ! 297 exit; 298 // Extract Module name 299 PN:=GetNextPart(N); 300 ME:=FindElementNode(PE,'module',PN); 301 // if not found, assume elementname is element in first module. 302 if (ME=Nil) then 303 begin 304 ME:=FindElementNode(PE,'module',''); 305 N:=ElementName; 306 end; 307 if (ME=Nil) then // No module node ! 308 exit; 309 Result:=FindElementNode(ME,'element',N); 310end; 311 312Procedure TEditorPage.DisplayDocument(Const AStartNode : String = ''); 313 314Var 315 PE,ME,EE : TDomElement; 316 317begin 318 EE:=Nil; 319 if (AStartNode <> '') then 320 begin 321 EE:=FindElement(AStartNode,PE,ME); 322 If (EE=Nil) then 323 ShowMessage(Format(SStartNodeNotFound,[AStartNode])); 324 end; 325 FPackages.DescriptionNode:=FDocument.DocumentElement; 326 if (EE<>Nil) then 327 begin 328 FPackages.CurrentPackage:=PE; 329 FPackages.CurrentModule:=ME; 330 FPackages.CurrentElement:=EE; 331 end; 332end; 333 334procedure TEditorPage.ElementChanged(Sender: TObject); 335begin 336 if Sender=nil then ; 337 TPackageEditor(FPackages).UpdateSelectedNodeStatus; 338end; 339 340procedure TEditorPage.SetParent(NewParent: TWinControl); 341begin 342 inherited SetParent(NewParent); 343 if Assigned(NewParent) then 344 FSplitter.Width:=5; 345end; 346 347 348 349Procedure TEditorPage.ElementSelected(Node : TDomElement) ; 350 351Var 352 OldNode : TDomElement; 353 354begin 355 OldNode:=FElement.Element; 356 If OldNode<>Node then 357 FElement.Element:=Node; 358end; 359 360Procedure TEditorPage.PackageSelected(Node : TDomElement) ; 361 362begin 363 ElementSelected(Node); 364end; 365 366Procedure TEditorPage.ModuleSelected(Node : TDomElement) ; 367 368begin 369 ElementSelected(Node); 370end; 371 372Procedure TEditorPage.TopicSelected(Node : TDomElement) ; 373 374begin 375 ElementSelected(Node); 376end; 377 378 379Procedure TEditorPage.InsertTag(TagType : TTagType); 380 381begin 382 FElement.InsertTag(TagType) 383end; 384 385Procedure TEditorPage.InsertLink(LinkTarget,LinkText : String); 386 387begin 388 FElement.InsertLink(LinkTarget,LinkText); 389end; 390 391 392Procedure TEditorPage.InsertTable(Cols,Rows : Integer; UseHeader : Boolean); 393 394begin 395 Felement.InsertTable(Cols,Rows,UseHeader); 396end; 397 398 399procedure TEditorPage.InsertShortPrintLink(pLinkTarget: string); 400begin 401 FElement.InsertPrintShortLink(pLinkTarget); 402end; 403 404procedure TEditorPage.InsertItemizeList(ItemsCount: Integer); 405begin 406 Felement.InsertItemizeList(ItemsCount); 407end; 408 409procedure TEditorPage.InsertEnumerateList(ItemsCount: Integer); 410begin 411 Felement.InsertEnumerateList(ItemsCount); 412end; 413 414 415Function TEditorPage.GetCurrentSelection : String; 416 417begin 418 Result:=FElement.CurrentSelection; 419end; 420 421Procedure TEditorPage.NewPackage(APackageName : String); 422 423Var 424 P : TDomElement; 425 426begin 427 P:=FDocument.CreateElement('package'); 428 P['name']:=APAckageName; 429 FDocument.DocumentElement.AppendChild(P); 430 FPackages.Refresh; 431 FPackages.Modified:=True; 432 CurrentPackage:=P; 433end; 434 435Function TEditorPage.FirstPackage : TDomElement; 436 437Var 438 N : TDomNode; 439 440begin 441 N:=FDocument.DocumentElement.FirstChild; 442 While (N<>Nil) and Not IsPackageNode(N) do 443 N:=N.NextSibling; 444 Result:=TDomElement(N); 445end; 446 447Function TEditorPage.FirstModule(APackage : TDomElement) : TDomElement; 448 449Var 450 N : TDomNode; 451 452begin 453 N:=APAckage.FirstChild; 454 While (N<>Nil) and Not IsModuleNode(N) do 455 N:=N.NextSibling; 456 Result:=TDomElement(N); 457end; 458 459Procedure TEditorPage.NewModule(AModuleName : String); 460 461Var 462 M,P : TDomElement; 463 464begin 465 If CurrentPackage<>Nil then 466 P:=CurrentPackage 467 else 468 P:=FirstPackage; 469 If (P=Nil) then 470 Raise Exception.CreateFmt(SErrNoPackageForModule,[AModuleName]); 471 M:=FDocument.CreateElement('module'); 472 M['name']:=AModuleName; 473 P.AppendChild(M); 474 FPackages.Refresh; 475 FPackages.Modified:=True; 476 CurrentModule:=M; 477end; 478 479Procedure TEditorPage.NewTopic(ATopicName : String); 480 481Var 482 T,M,P : TDomElement; 483 484begin 485 { 486 If currently a topic is selected, make a subtopic, or a sibling topic. 487 If no topic is selected, then make a topic under the current module or 488 package. A menu to move topics up/down is needed... 489 } 490 if (CurrentTopic<>Nil) then 491 begin 492 M:=CurrentTopic.ParentNode as TDomElement; 493 If (M.NodeName='module') or (M.NodeName='topic') then 494 P:=M 495 else 496 P:=CurrentTopic; 497 end 498 else if (CurrentModule<>Nil) then 499 P:=CurrentModule 500 else if (CurrentPackage<>Nil) then 501 P:=CurrentPackage 502 else 503 P:=FirstPackage; 504 If (P=Nil) then 505 Raise Exception.CreateFmt(SErrNoNodeForTopic,[ATopicName]); 506 T:=FDocument.CreateElement('topic'); 507 T['name']:=ATopicName; 508 P.AppendChild(T); 509 FPackages.Refresh; 510 FPackages.Modified:=True; 511 CurrentTopic:=T; 512end; 513 514Procedure TEditorPage.NewElement(AElementName : String); 515 516Var 517 P,E,M : TDomElement; 518 519begin 520 If CurrentModule<>Nil then 521 M:=CurrentModule 522 else 523 begin 524 P:=FirstPackage; 525 If P<>Nil then 526 M:=FirstModule(P) 527 else 528 M:=Nil; 529 If M<>Nil then 530 CurrentModule:=M; 531 end; 532 If (M=Nil) then 533 Raise Exception.CreateFmt(SErrNoModuleForElement,[AElementName]); 534 E:=FDocument.CreateElement('element'); 535 E['name']:=AElementName; 536 M.AppendChild(E); 537 FPackages.AddElement(E); 538end; 539 540Function TEditorPage.GetCurrentPackage : TDomElement; 541 542begin 543 Result:=FPackages.CurrentPackage; 544end; 545 546 547Function TEditorPage.GetCurrentModule : TDomElement; 548 549begin 550 Result:=FPackages.CurrentModule; 551end; 552 553 554Function TEditorPage.GetCurrentTopic : TDomElement; 555 556begin 557 Result:=FPackages.CurrentTopic; 558end; 559 560 561Function TEditorPage.GetCurrentElement : TDomElement; 562begin 563 Result:=FElement.Element; 564end; 565 566Procedure TEditorPage.SetCurrentElement(Value : TDomElement); 567 568begin 569 FPackages.CurrentElement:=Value; 570end; 571 572 573Procedure TEditorPage.SetCurrentModule(Value : TDomElement); 574 575begin 576 FPackages.CurrentModule:=Value; 577end; 578 579 580Procedure TEditorPage.SetCurrentTopic(Value : TDomElement); 581 582begin 583 FPackages.CurrentTopic:=Value; 584end; 585 586 587Procedure TEditorPage.SetCurrentPackage(Value : TDomElement); 588 589begin 590 FPackages.CurrentPackage:=Value; 591end; 592 593Procedure TEditorPage.SetModified(Value : Boolean); 594 595begin 596 If Not Value then 597 begin 598 FPackages.Modified:=False; 599 FElement.Modified:=False; 600 FElement.SavedNode:=False; 601 end; 602end; 603 604Function TEditorPage.GetModified : Boolean; 605 606begin 607 Result:=FPackages.Modified or 608 FElement.Modified or 609 FElement.SavedNode; 610end; 611 612Procedure TEditorPage.GetElementList(List : TStrings); 613 614Var 615 N : TDOmNode; 616 617begin 618 With List do 619 begin 620 Clear; 621 If Assigned(CurrentModule) then 622 begin 623 N:=Currentmodule.FirstChild; 624 While (N<>Nil) do 625 begin 626 If (N is TDomElement) and (N.NodeName='element') then 627 Add(TDomElement(N)['name']); 628 N:=N.NextSibling; 629 end; 630 end; 631 end; 632end; 633 634function TEditorPage.GetInitialDir: String; 635begin 636 result := ''; 637 if FileExistsUTF8(FFileName) then 638 Result := ExtractFilePath(FFileName); 639end; 640 641end. 642